11 "bytes" |
11 "bytes" |
12 "encoding/json" |
12 "encoding/json" |
13 "fmt" |
13 "fmt" |
14 "net/http" |
14 "net/http" |
15 "net/url" |
15 "net/url" |
|
16 "regexp" |
16 "strconv" |
17 "strconv" |
17 "strings" |
18 "strings" |
18 |
19 |
19 "github.com/sendgrid/rest" |
20 "github.com/sendgrid/rest" |
20 ) |
21 ) |
|
22 |
|
23 type apiLinks struct { |
|
24 next, prev *LimitParams |
|
25 } |
|
26 |
|
27 func parseLink(links []string) (*apiLinks, error) { |
|
28 if len(links) == 0 { |
|
29 return nil, nil |
|
30 } |
|
31 |
|
32 al := new(apiLinks) |
|
33 linkRegex := regexp.MustCompile(`<([^>]+)>; rel="([^"]+)`) |
|
34 for _, l := range links { |
|
35 m := linkRegex.FindAllStringSubmatch(l, -1) |
|
36 for _, submatch := range m { |
|
37 if len(submatch) != 3 { |
|
38 continue |
|
39 } |
|
40 // Parse URL |
|
41 u, err := url.Parse(submatch[1]) |
|
42 if err != nil { |
|
43 return al, err |
|
44 } |
|
45 var lp *LimitParams |
|
46 since := u.Query().Get("since_id") |
|
47 max := u.Query().Get("max_id") |
|
48 lim := u.Query().Get("limit") |
|
49 if since == "" && max == "" { |
|
50 continue |
|
51 } |
|
52 lp = new(LimitParams) |
|
53 if since != "" { |
|
54 lp.SinceID, err = strconv.Atoi(since) |
|
55 if err != nil { |
|
56 return al, err |
|
57 } |
|
58 } |
|
59 if max != "" { |
|
60 lp.MaxID, err = strconv.Atoi(max) |
|
61 if err != nil { |
|
62 return al, err |
|
63 } |
|
64 } |
|
65 if lim != "" { |
|
66 lp.Limit, err = strconv.Atoi(lim) |
|
67 if err != nil { |
|
68 return al, err |
|
69 } |
|
70 } |
|
71 switch submatch[2] { |
|
72 case "prev": |
|
73 al.prev = lp |
|
74 case "next": |
|
75 al.next = lp |
|
76 } |
|
77 } |
|
78 } |
|
79 return al, nil |
|
80 } |
21 |
81 |
22 // restAPI actually does the HTTP query |
82 // restAPI actually does the HTTP query |
23 // It is a copy of rest.API with better handling of parameters with multiple values |
83 // It is a copy of rest.API with better handling of parameters with multiple values |
24 func restAPI(request rest.Request) (*rest.Response, error) { |
84 func restAPI(request rest.Request) (*rest.Response, error) { |
25 c := &rest.Client{HTTPClient: http.DefaultClient} |
85 c := &rest.Client{HTTPClient: http.DefaultClient} |
101 } |
161 } |
102 return req, nil |
162 return req, nil |
103 } |
163 } |
104 |
164 |
105 // apiCall makes a call to the Mastodon API server |
165 // apiCall makes a call to the Mastodon API server |
106 func (mc *Client) apiCall(endPoint string, method rest.Method, params apiCallParams, limitOptions *LimitParams, data interface{}) error { |
166 // If links is not nil, the prev/next links from the API response headers |
|
167 // will be set (if they exist) in the structure. |
|
168 func (mc *Client) apiCall(endPoint string, method rest.Method, params apiCallParams, limitOptions *LimitParams, links *apiLinks, data interface{}) error { |
107 if mc == nil { |
169 if mc == nil { |
108 return fmt.Errorf("use of uninitialized madon client") |
170 return fmt.Errorf("use of uninitialized madon client") |
109 } |
171 } |
110 |
172 |
111 if limitOptions != nil { |
173 if limitOptions != nil { |
131 |
193 |
132 // Make API call |
194 // Make API call |
133 r, err := restAPI(req) |
195 r, err := restAPI(req) |
134 if err != nil { |
196 if err != nil { |
135 return fmt.Errorf("API query (%s) failed: %s", endPoint, err.Error()) |
197 return fmt.Errorf("API query (%s) failed: %s", endPoint, err.Error()) |
|
198 } |
|
199 |
|
200 if links != nil { |
|
201 pLinks, err := parseLink(r.Headers["Link"]) |
|
202 if err != nil { |
|
203 return fmt.Errorf("cannot decode header links (%s): %s", method, err.Error()) |
|
204 } |
|
205 if pLinks != nil { |
|
206 *links = *pLinks |
|
207 } |
136 } |
208 } |
137 |
209 |
138 // Check for error reply |
210 // Check for error reply |
139 var errorResult Error |
211 var errorResult Error |
140 if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { |
212 if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { |