|
1 package gondole |
|
2 |
|
3 import ( |
|
4 "bytes" |
|
5 "encoding/json" |
|
6 "fmt" |
|
7 "net/http" |
|
8 "net/url" |
|
9 "strings" |
|
10 |
|
11 "github.com/sendgrid/rest" |
|
12 ) |
|
13 |
|
14 // restAPI actually does the HTTP query |
|
15 // It is a copy of rest.API with better handling of parameters with multiple values |
|
16 func restAPI(request rest.Request) (*rest.Response, error) { |
|
17 c := &rest.Client{HTTPClient: http.DefaultClient} |
|
18 |
|
19 // Build the HTTP request object. |
|
20 if len(request.QueryParams) != 0 { |
|
21 // Add parameters to the URL |
|
22 request.BaseURL += "?" |
|
23 urlp := url.Values{} |
|
24 for key, value := range request.QueryParams { |
|
25 // It seems Mastodon doesn't like parameters with index |
|
26 // numbers, but it needs the brackets. |
|
27 // Let's check if the key matches '^.+\[.*\]$' |
|
28 klen := len(key) |
|
29 if klen == 0 { |
|
30 continue |
|
31 } |
|
32 i := strings.Index(key, "[") |
|
33 if key[klen-1] == ']' && i > 0 { |
|
34 // This is an array, let's remove the index number |
|
35 key = key[:i] + "[]" |
|
36 } |
|
37 urlp.Add(key, value) |
|
38 } |
|
39 urlpstr := urlp.Encode() |
|
40 request.BaseURL += urlpstr |
|
41 } |
|
42 |
|
43 req, err := http.NewRequest(string(request.Method), request.BaseURL, bytes.NewBuffer(request.Body)) |
|
44 if err != nil { |
|
45 return nil, err |
|
46 } |
|
47 |
|
48 for key, value := range request.Headers { |
|
49 req.Header.Set(key, value) |
|
50 } |
|
51 _, exists := req.Header["Content-Type"] |
|
52 if len(request.Body) > 0 && !exists { |
|
53 req.Header.Set("Content-Type", "application/json") |
|
54 } |
|
55 |
|
56 // Build the HTTP client and make the request. |
|
57 res, err := c.MakeRequest(req) |
|
58 if err != nil { |
|
59 return nil, err |
|
60 } |
|
61 |
|
62 // Build Response object. |
|
63 response, err := rest.BuildResponse(res) |
|
64 if err != nil { |
|
65 return nil, err |
|
66 } |
|
67 |
|
68 return response, nil |
|
69 } |
|
70 |
|
71 // prepareRequest inserts all pre-defined stuff |
|
72 func (g *Client) prepareRequest(target string, method rest.Method, params apiCallParams) (req rest.Request) { |
|
73 endPoint := g.APIBase + "/" + target |
|
74 |
|
75 // Request headers |
|
76 hdrs := make(map[string]string) |
|
77 hdrs["User-Agent"] = fmt.Sprintf("Gondole/%s", GondoleVersion) |
|
78 if g.UserToken != nil { |
|
79 hdrs["Authorization"] = fmt.Sprintf("Bearer %s", g.UserToken.AccessToken) |
|
80 } |
|
81 |
|
82 req = rest.Request{ |
|
83 BaseURL: endPoint, |
|
84 Headers: hdrs, |
|
85 Method: method, |
|
86 QueryParams: params, |
|
87 } |
|
88 return |
|
89 } |
|
90 |
|
91 // apiCall makes a call to the Mastodon API server |
|
92 func (g *Client) apiCall(endPoint string, method rest.Method, params apiCallParams, data interface{}) error { |
|
93 // Prepare query |
|
94 req := g.prepareRequest(endPoint, method, params) |
|
95 |
|
96 // Make API call |
|
97 r, err := restAPI(req) |
|
98 if err != nil { |
|
99 return fmt.Errorf("API query (%s) failed: %s", endPoint, err.Error()) |
|
100 } |
|
101 |
|
102 // Check for error reply |
|
103 var errorResult Error |
|
104 if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil { |
|
105 // The empty object is not an error |
|
106 if errorResult.Text != "" { |
|
107 return fmt.Errorf("%s", errorResult.Text) |
|
108 } |
|
109 } |
|
110 |
|
111 // Not an error reply; let's unmarshal the data |
|
112 err = json.Unmarshal([]byte(r.Body), &data) |
|
113 if err != nil { |
|
114 return fmt.Errorf("cannot decode API response (%s): %s", method, err.Error()) |
|
115 } |
|
116 return nil |
|
117 } |