ducky/subscriptions
ducky/subscriptions/client/client.go
Add comments, move ChangingSystemProperties to the api package. Add comments to all our exported types and variables in subscription.go, both to make golint happy and because it's good to have comments. Move the subscriptions.ChangingSystemProperties helper to api.changingSystemProperties, because it returns API-specific strings and there's no real reason it has to be in the subscriptions package--everything it needs to work on is exported.
| 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 } |