ducky/subscriptions
2015-10-04
Parent:8eb19bcbf17d
ducky/subscriptions/client/client.go
Document our client to make golint happy. Take care of all the documentation warnings in the client subpackage, which means golint now returns successfully.
| 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@17 | 20 // ErrNilClient is returned when a method is called on a nil Client. |
| paddy@17 | 21 ErrNilClient = errors.New("nil client wrapper") |
| paddy@17 | 22 // ErrNilHTTPClient is returned when a method is called on a Client |
| paddy@17 | 23 // with its http.Client not set. |
| paddy@7 | 24 ErrNilHTTPClient = errors.New("nil client") |
| paddy@7 | 25 ) |
| paddy@7 | 26 |
| paddy@17 | 27 // Client is a wrapper that bundles all the information necessary to interact |
| paddy@17 | 28 // with the subscriptions API. |
| paddy@7 | 29 type Client struct { |
| paddy@7 | 30 client *http.Client |
| paddy@7 | 31 address string |
| paddy@7 | 32 ID uuid.ID |
| paddy@7 | 33 } |
| paddy@7 | 34 |
| paddy@17 | 35 // New returns a new Client. The passed address is the base address for the |
| paddy@17 | 36 // subscriptions API, as an absolute or relative URL. The passed ID is the |
| paddy@17 | 37 // OAuth2 client ID to use for the Client. |
| paddy@7 | 38 func New(address string, id uuid.ID) *Client { |
| paddy@7 | 39 address = strings.TrimRight(address, "/") |
| paddy@7 | 40 return &Client{ |
| paddy@7 | 41 address: address, |
| paddy@7 | 42 client: &http.Client{}, |
| paddy@7 | 43 ID: id, |
| paddy@7 | 44 } |
| paddy@7 | 45 } |
| paddy@7 | 46 |
| paddy@7 | 47 func (c *Client) do(method, url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 48 if c == nil { |
| paddy@7 | 49 return api.Response{}, ErrNilClient |
| paddy@7 | 50 } |
| paddy@7 | 51 if c.client == nil { |
| paddy@7 | 52 return api.Response{}, ErrNilHTTPClient |
| paddy@7 | 53 } |
| paddy@7 | 54 var response api.Response |
| paddy@7 | 55 if !strings.HasPrefix(url, "http") { |
| paddy@7 | 56 url = strings.TrimLeft(url, "/") |
| paddy@7 | 57 url = "/" + url |
| paddy@7 | 58 url = c.address + url |
| paddy@7 | 59 } |
| paddy@7 | 60 var body io.Reader |
| paddy@7 | 61 if request != nil { |
| paddy@7 | 62 data, err := json.Marshal(request) |
| paddy@7 | 63 if err != nil { |
| paddy@7 | 64 return response, err |
| paddy@7 | 65 } |
| paddy@7 | 66 body = bytes.NewBuffer(data) |
| paddy@7 | 67 } |
| paddy@7 | 68 req, err := http.NewRequest(method, url, body) |
| paddy@7 | 69 if err != nil { |
| paddy@7 | 70 return response, err |
| paddy@7 | 71 } |
| paddy@7 | 72 req.Header.Set("Ducky-Scope", strings.Join(scopes, " ")) |
| paddy@7 | 73 req.Header.Set("Ducky-Issuer", c.ID.String()) |
| paddy@7 | 74 if subject != nil { |
| paddy@7 | 75 req.Header.Set("Ducky-Subject", subject.String()) |
| paddy@7 | 76 } |
| paddy@7 | 77 req.Header.Set("Ducky-Expires", time.Now().Add(time.Hour).String()) |
| paddy@7 | 78 req.Header.Set("Ducky-Issued-At", time.Now().String()) |
| paddy@7 | 79 req.Header.Set("Ducky-Not-Before", time.Now().Add(-5*time.Minute).String()) |
| paddy@7 | 80 resp, err := c.client.Do(req) |
| paddy@7 | 81 if err != nil { |
| paddy@7 | 82 return response, err |
| paddy@7 | 83 } |
| paddy@7 | 84 defer resp.Body.Close() |
| paddy@7 | 85 switch resp.Header.Get("Content-Type") { |
| paddy@7 | 86 case "application/json": |
| paddy@7 | 87 dec := json.NewDecoder(resp.Body) |
| paddy@7 | 88 err = dec.Decode(&response) |
| paddy@7 | 89 if err != nil { |
| paddy@7 | 90 return response, err |
| paddy@7 | 91 } |
| paddy@7 | 92 default: |
| paddy@7 | 93 dec := json.NewDecoder(resp.Body) |
| paddy@7 | 94 err = dec.Decode(&response) |
| paddy@7 | 95 if err != nil { |
| paddy@7 | 96 return response, err |
| paddy@7 | 97 } |
| paddy@7 | 98 } |
| paddy@9 | 99 if len(response.Errors) > 0 { |
| paddy@9 | 100 return response, httpErrors(response.Errors) |
| paddy@9 | 101 } |
| paddy@7 | 102 return response, nil |
| paddy@7 | 103 } |
| paddy@7 | 104 |
| paddy@9 | 105 type httpErrors []commonAPI.RequestError |
| paddy@9 | 106 |
| paddy@9 | 107 func (h httpErrors) Error() string { |
| paddy@9 | 108 return fmt.Sprintf("%+#v", h) |
| paddy@9 | 109 } |
| paddy@9 | 110 |
| paddy@17 | 111 // Get returns the api Response object for the passed URL called |
| paddy@17 | 112 // over HTTP GET. The passed scope IDs will be applied to the request, |
| paddy@17 | 113 // and the request will be made on behalf of the subject (user) |
| paddy@17 | 114 // specified by the passed ID. If the zero value is passed for the ID, |
| paddy@17 | 115 // the request will be made without a subject. To request no scopes, |
| paddy@17 | 116 // pass an empty slice or nil. |
| paddy@7 | 117 func (c *Client) Get(url string, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 118 return c.do("GET", url, nil, scopes, subject) |
| paddy@7 | 119 } |
| paddy@7 | 120 |
| paddy@17 | 121 // Post returns the api Response object for the passed URL called |
| paddy@17 | 122 // over HTTP POST. The passed scope IDs will be applied to the request, |
| paddy@17 | 123 // and the request will be made on behalf of the subject (user) |
| paddy@17 | 124 // specified by the passed ID. If the zero value is passed for the ID, |
| paddy@17 | 125 // the request will be made without a subject. To request no scopes, |
| paddy@17 | 126 // pass an empty slice or nil. |
| paddy@7 | 127 func (c *Client) Post(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 128 return c.do("POST", url, request, scopes, subject) |
| paddy@7 | 129 } |
| paddy@7 | 130 |
| paddy@17 | 131 // Patch returns the api Response object for the passed URL called |
| paddy@17 | 132 // over HTTP PATCH. The passed scope IDs will be applied to the request, |
| paddy@17 | 133 // and the request will be made on behalf of the subject (user) |
| paddy@17 | 134 // specified by the passed ID. If the zero value is passed for the ID, |
| paddy@17 | 135 // the request will be made without a subject. To request no scopes, |
| paddy@17 | 136 // pass an empty slice or nil. |
| paddy@7 | 137 func (c *Client) Patch(url string, request interface{}, scopes []string, subject uuid.ID) (api.Response, error) { |
| paddy@7 | 138 return c.do("PATCH", url, request, scopes, subject) |
| paddy@7 | 139 } |