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