ducky/subscriptions

Paddy 2015-10-04 Parent:8eb19bcbf17d Child:7eef47ecc01c

16:b063bc0a6e84 Go to Latest

ducky/subscriptions/client/client.go

Make api subpackage golint-passing. Add comments to all the exported functions, methods, and variables in the api subpackage, to make golint happy. Also, make the individual endpoints in the api subpackage unexported, as there's no real use case for exporting them. The handlers depend on the placeholders in the endpoint, so we need them to be controlled in unison, which means it's probably a bad idea to declare the route outside of the API package. And the only reason to expose the Handler is so people can declare custom endpoints.

History
paddy@7 1 package client
paddy@7 2
paddy@7 3 import (
paddy@7 4 "bytes"
paddy@7 5 "encoding/json"
paddy@7 6 "errors"
paddy@9 7 "fmt"
paddy@7 8 "io"
paddy@7 9 "net/http"
paddy@7 10 "strings"
paddy@7 11 "time"
paddy@7 12
paddy@9 13 commonAPI "code.secondbit.org/api.hg"
paddy@7 14 "code.secondbit.org/uuid.hg"
paddy@7 15
paddy@7 16 "code.secondbit.org/ducky/subscriptions.hg/api"
paddy@7 17 )
paddy@7 18
paddy@7 19 var (
paddy@7 20 ErrNilClient = errors.New("nil client wrapper")
paddy@7 21 ErrNilHTTPClient = errors.New("nil client")
paddy@7 22 )
paddy@7 23
paddy@7 24 type Client struct {
paddy@7 25 client *http.Client
paddy@7 26 address string
paddy@7 27 ID uuid.ID
paddy@7 28 }
paddy@7 29
paddy@7 30 func New(address string, id uuid.ID) *Client {
paddy@7 31 address = strings.TrimRight(address, "/")
paddy@7 32 return &Client{
paddy@7 33 address: address,
paddy@7 34 client: &http.Client{},
paddy@7 35 ID: id,
paddy@7 36 }
paddy@7 37 }
paddy@7 38
paddy@7 39 func (c *Client) do(method, url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 40 if c == nil {
paddy@7 41 return api.Response{}, ErrNilClient
paddy@7 42 }
paddy@7 43 if c.client == nil {
paddy@7 44 return api.Response{}, ErrNilHTTPClient
paddy@7 45 }
paddy@7 46 var response api.Response
paddy@7 47 if !strings.HasPrefix(url, "http") {
paddy@7 48 url = strings.TrimLeft(url, "/")
paddy@7 49 url = "/" + url
paddy@7 50 url = c.address + url
paddy@7 51 }
paddy@7 52 var body io.Reader
paddy@7 53 if request != nil {
paddy@7 54 data, err := json.Marshal(request)
paddy@7 55 if err != nil {
paddy@7 56 return response, err
paddy@7 57 }
paddy@7 58 body = bytes.NewBuffer(data)
paddy@7 59 }
paddy@7 60 req, err := http.NewRequest(method, url, body)
paddy@7 61 if err != nil {
paddy@7 62 return response, err
paddy@7 63 }
paddy@7 64 req.Header.Set("Ducky-Scope", strings.Join(scopes, " "))
paddy@7 65 req.Header.Set("Ducky-Issuer", c.ID.String())
paddy@7 66 if subject != nil {
paddy@7 67 req.Header.Set("Ducky-Subject", subject.String())
paddy@7 68 }
paddy@7 69 req.Header.Set("Ducky-Expires", time.Now().Add(time.Hour).String())
paddy@7 70 req.Header.Set("Ducky-Issued-At", time.Now().String())
paddy@7 71 req.Header.Set("Ducky-Not-Before", time.Now().Add(-5*time.Minute).String())
paddy@7 72 resp, err := c.client.Do(req)
paddy@7 73 if err != nil {
paddy@7 74 return response, err
paddy@7 75 }
paddy@7 76 defer resp.Body.Close()
paddy@7 77 switch resp.Header.Get("Content-Type") {
paddy@7 78 case "application/json":
paddy@7 79 dec := json.NewDecoder(resp.Body)
paddy@7 80 err = dec.Decode(&response)
paddy@7 81 if err != nil {
paddy@7 82 return response, err
paddy@7 83 }
paddy@7 84 default:
paddy@7 85 dec := json.NewDecoder(resp.Body)
paddy@7 86 err = dec.Decode(&response)
paddy@7 87 if err != nil {
paddy@7 88 return response, err
paddy@7 89 }
paddy@7 90 }
paddy@9 91 if len(response.Errors) > 0 {
paddy@9 92 return response, httpErrors(response.Errors)
paddy@9 93 }
paddy@7 94 return response, nil
paddy@7 95 }
paddy@7 96
paddy@9 97 type httpErrors []commonAPI.RequestError
paddy@9 98
paddy@9 99 func (h httpErrors) Error() string {
paddy@9 100 return fmt.Sprintf("%+#v", h)
paddy@9 101 }
paddy@9 102
paddy@7 103 func (c *Client) Get(url string, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 104 return c.do("GET", url, nil, scopes, subject)
paddy@7 105 }
paddy@7 106
paddy@7 107 func (c *Client) Post(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 108 return c.do("POST", url, request, scopes, subject)
paddy@7 109 }
paddy@7 110
paddy@7 111 func (c *Client) Patch(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) {
paddy@7 112 return c.do("PATCH", url, request, scopes, subject)
paddy@7 113 }