danstis/go-nitrado

View on GitHub
nitrado/nitrado.go

Summary

Maintainability
A
0 mins
Test Coverage
package nitrado

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "reflect"
    "strings"
    "sync"
    "time"

    "github.com/google/go-querystring/query"
)

const (
    defaultBaseURI string        = "https://api.nitrado.net/"
    retryCount     int           = 10
    retryDelay     time.Duration = 2 * time.Second
    userAgent      string        = "go-nitrado"
)

// Client represents the config of the Nitrado.net Client
type Client struct {
    sync.Mutex
    client    *http.Client
    token     string
    UserAgent string

    // Base URL for API requests. Defaults to the public Nitrado API. BaseURL should
    // always be specified with a trailing slash.
    BaseURI *url.URL

    common apiService // Reuse a single struct instead of allocating one for each service on the heap.

    // Services used for talking to different parts of the Nitrado API.
    FileServerService   *FileServerService
    GameServers         *GameServersService
    GameServersSettings *GSSettingsService
    GameServerStats     *GameServerStatsService
    PlayerListService   *PlayerListService
    Services            *ServicesService
}

type apiService struct {
    client *Client
}

// addOptions adds the parameters in opts as URL query parameters to s. opts
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opts interface{}) (string, error) {
    v := reflect.ValueOf(opts)
    if v.Kind() == reflect.Ptr && v.IsNil() {
        return s, nil
    }

    u, err := url.Parse(s)
    if err != nil {
        return s, err
    }

    qs, err := query.Values(opts)
    if err != nil {
        return s, err
    }

    u.RawQuery = qs.Encode()
    return u.String(), nil
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURI of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
    if !strings.HasSuffix(c.BaseURI.Path, "/") {
        return nil, fmt.Errorf("BaseURI must have a trailing slash, but %q does not", c.BaseURI)
    }
    u, err := c.BaseURI.Parse(urlStr)
    if err != nil {
        return nil, err
    }

    var buf io.ReadWriter
    if body != nil {
        buf = &bytes.Buffer{}
        enc := json.NewEncoder(buf)
        enc.SetEscapeHTML(false)
        err := enc.Encode(body)
        if err != nil {
            return nil, err
        }
    }

    req, err := http.NewRequest(method, u.String(), buf)
    if err != nil {
        return nil, err
    }

    if body != nil {
        req.Header.Set("Content-Type", "application/json")
    }
    req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.token))
    if c.UserAgent != "" {
        req.Header.Set("User-Agent", c.UserAgent)
    }
    return req, nil
}

// Do sends an API request to the Nitrado API and returns a response object.
// The API response is JSON decoded and stored in the value pointed to by v,
// or returned as an error if an API error has occurred. If v implements the
// io.Writer interface, the raw response body will be written to v, without
// attempting to first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {

    // Do the request
    var err error = nil
    var resp *http.Response
    for i := 0; i < retryCount; i++ {
        resp, err = c.client.Do(req)
        if err == nil && resp.StatusCode < 400 {
            break
        }
        time.Sleep(retryDelay)
    }
    if v != nil {
        if w, ok := v.(io.Writer); ok {
            _, err = io.Copy(w, resp.Body)
        } else {
            decErr := json.NewDecoder(resp.Body).Decode(v)
            if decErr == io.EOF {
                decErr = nil // ignore EOF errors caused by empty response body
            }
            if decErr != nil {
                err = decErr
            }
        }
    }

    return resp, err
}

// NewClient creates a new instance of a NitradoAPI
func NewClient(apiToken string) *Client {
    baseURL, _ := url.Parse(defaultBaseURI)

    c := &Client{
        BaseURI:   baseURL,
        token:     apiToken,
        client:    &http.Client{},
        UserAgent: userAgent,
    }

    c.common.client = c

    c.FileServerService = (*FileServerService)(&c.common)
    c.GameServers = (*GameServersService)(&c.common)
    c.GameServersSettings = (*GSSettingsService)(&c.common)
    c.Services = (*ServicesService)(&c.common)
    c.GameServerStats = (*GameServerStatsService)(&c.common)
    c.PlayerListService = (*PlayerListService)(&c.common)

    return c
}