-
Notifications
You must be signed in to change notification settings - Fork 7
/
sawyer.go
105 lines (89 loc) · 2.74 KB
/
sawyer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package sawyer
import (
"github.com/lostisland/go-sawyer/hypermedia"
"net/http"
"net/url"
"strings"
)
// A Client wraps an *http.Client with a base url Endpoint and common header and
// query values.
type Client struct {
HttpClient *http.Client
Endpoint *url.URL
Header http.Header
Query url.Values
Cacher Cacher
}
// New returns a new Client with a given a URL and an optional client.
func New(endpoint *url.URL, client *http.Client) *Client {
if client == nil {
client = http.DefaultClient
}
if len(endpoint.Path) > 0 && !strings.HasSuffix(endpoint.Path, "/") {
endpoint.Path = endpoint.Path + "/"
}
return &Client{client, endpoint, make(http.Header), endpoint.Query(), noOpCacher}
}
// NewFromString returns a new Client given a string URL and an optional client.
func NewFromString(endpoint string, client *http.Client) (*Client, error) {
e, err := url.Parse(endpoint)
if err != nil {
return nil, err
}
return New(e, client), nil
}
// ResolveReference resolves a URI reference to an absolute URI from an absolute
// base URI. It also merges the query values.
func (c *Client) ResolveReference(u *url.URL) *url.URL {
absurl := c.Endpoint.ResolveReference(u)
if len(c.Query) > 0 {
absurl.RawQuery = mergeQueries(c.Query, absurl.Query())
}
return absurl
}
// ResolveReference resolves a string URI reference to an absolute URI from an
// absolute base URI. It also merges the query values.
func (c *Client) ResolveReferenceString(rawurl string) (string, error) {
u, err := url.Parse(rawurl)
if err != nil {
return "", err
}
return c.ResolveReference(u).String(), nil
}
// Rels attempts to get the cached relations for the given request. If it
// hasn't been cached, send a GET to the request URL, decode the response body
// to the given value, and get the relations from the value.
func (c *Client) Rels(req *Request, value interface{}) (hypermedia.Relations, *Response) {
if rels, ok := c.Cacher.Rels(req.Request); ok {
return rels, &Response{}
}
res := req.Get()
res.Decode(value)
return hypermedia.Rels(value), res
}
// buildRequest assembles a net/http Request using the given relative url path.
func buildRequest(c *Client, rawurl string) (*http.Request, error) {
u, err := c.ResolveReferenceString(rawurl)
if err != nil {
return nil, err
}
httpreq, err := http.NewRequest(GetMethod, u, nil)
for key, _ := range c.Header {
httpreq.Header.Set(key, c.Header.Get(key))
}
return httpreq, err
}
// mergeQueries merges the given url.Values into a single encoded URI query
// string.
func mergeQueries(queries ...url.Values) string {
merged := make(url.Values)
for _, q := range queries {
if len(q) == 0 {
break
}
for key, _ := range q {
merged.Set(key, q.Get(key))
}
}
return merged.Encode()
}