]>
Commit | Line | Data |
---|---|---|
9b12e4fe JC |
1 | package statuscake |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | "encoding/json" | |
6 | "fmt" | |
7 | "io" | |
8 | "io/ioutil" | |
9 | "net/http" | |
10 | "net/url" | |
11 | "strings" | |
12 | ) | |
13 | ||
14 | const apiBaseURL = "https://app.statuscake.com/API" | |
15 | ||
16 | type responseBody struct { | |
17 | io.Reader | |
18 | } | |
19 | ||
20 | func (r *responseBody) Close() error { | |
21 | return nil | |
22 | } | |
23 | ||
24 | // Auth wraps the authorisation headers required for each request | |
25 | type Auth struct { | |
26 | Username string | |
27 | Apikey string | |
28 | } | |
29 | ||
30 | func (a *Auth) validate() error { | |
31 | e := make(ValidationError) | |
32 | ||
33 | if a.Username == "" { | |
34 | e["Username"] = "is required" | |
35 | } | |
36 | ||
37 | if a.Apikey == "" { | |
38 | e["Apikey"] = "is required" | |
39 | } | |
40 | ||
41 | if len(e) > 0 { | |
42 | return e | |
43 | } | |
44 | ||
45 | return nil | |
46 | } | |
47 | ||
48 | type httpClient interface { | |
49 | Do(*http.Request) (*http.Response, error) | |
50 | } | |
51 | ||
52 | type apiClient interface { | |
53 | get(string, url.Values) (*http.Response, error) | |
54 | delete(string, url.Values) (*http.Response, error) | |
55 | put(string, url.Values) (*http.Response, error) | |
56 | } | |
57 | ||
58 | // Client is the http client that wraps the remote API. | |
59 | type Client struct { | |
60 | c httpClient | |
61 | username string | |
62 | apiKey string | |
63 | testsClient Tests | |
64 | } | |
65 | ||
66 | // New returns a new Client | |
67 | func New(auth Auth) (*Client, error) { | |
68 | if err := auth.validate(); err != nil { | |
69 | return nil, err | |
70 | } | |
71 | ||
72 | return &Client{ | |
73 | c: &http.Client{}, | |
74 | username: auth.Username, | |
75 | apiKey: auth.Apikey, | |
76 | }, nil | |
77 | } | |
78 | ||
79 | func (c *Client) newRequest(method string, path string, v url.Values, body io.Reader) (*http.Request, error) { | |
80 | url := fmt.Sprintf("%s%s", apiBaseURL, path) | |
81 | if v != nil { | |
82 | url = fmt.Sprintf("%s?%s", url, v.Encode()) | |
83 | } | |
84 | ||
85 | r, err := http.NewRequest(method, url, body) | |
86 | if err != nil { | |
87 | return nil, err | |
88 | } | |
89 | ||
90 | r.Header.Set("Username", c.username) | |
91 | r.Header.Set("API", c.apiKey) | |
92 | ||
93 | return r, nil | |
94 | } | |
95 | ||
96 | func (c *Client) doRequest(r *http.Request) (*http.Response, error) { | |
97 | resp, err := c.c.Do(r) | |
98 | if err != nil { | |
99 | return nil, err | |
100 | } | |
101 | defer resp.Body.Close() | |
102 | ||
103 | if resp.StatusCode < 200 || resp.StatusCode > 299 { | |
104 | return nil, &httpError{ | |
105 | status: resp.Status, | |
106 | statusCode: resp.StatusCode, | |
107 | } | |
108 | } | |
109 | ||
110 | var aer autheticationErrorResponse | |
111 | ||
112 | // We read and save the response body so that if we don't have error messages | |
113 | // we can set it again for future usage | |
114 | b, err := ioutil.ReadAll(resp.Body) | |
115 | if err != nil { | |
116 | return nil, err | |
117 | } | |
118 | ||
119 | err = json.Unmarshal(b, &aer) | |
120 | if err == nil && aer.ErrNo == 0 && aer.Error != "" { | |
121 | return nil, &AuthenticationError{ | |
122 | errNo: aer.ErrNo, | |
123 | message: aer.Error, | |
124 | } | |
125 | } | |
126 | ||
127 | resp.Body = &responseBody{ | |
128 | Reader: bytes.NewReader(b), | |
129 | } | |
130 | ||
131 | return resp, nil | |
132 | } | |
133 | ||
134 | func (c *Client) get(path string, v url.Values) (*http.Response, error) { | |
135 | r, err := c.newRequest("GET", path, v, nil) | |
136 | if err != nil { | |
137 | return nil, err | |
138 | } | |
139 | ||
140 | return c.doRequest(r) | |
141 | } | |
142 | ||
143 | func (c *Client) put(path string, v url.Values) (*http.Response, error) { | |
144 | r, err := c.newRequest("PUT", path, nil, strings.NewReader(v.Encode())) | |
145 | r.Header.Set("Content-Type", "application/x-www-form-urlencoded") | |
146 | ||
147 | if err != nil { | |
148 | return nil, err | |
149 | } | |
150 | ||
151 | return c.doRequest(r) | |
152 | } | |
153 | ||
154 | func (c *Client) delete(path string, v url.Values) (*http.Response, error) { | |
155 | r, err := c.newRequest("DELETE", path, v, nil) | |
156 | if err != nil { | |
157 | return nil, err | |
158 | } | |
159 | ||
160 | return c.doRequest(r) | |
161 | } | |
162 | ||
163 | // Tests returns a client that implements the `Tests` API. | |
164 | func (c *Client) Tests() Tests { | |
165 | if c.testsClient == nil { | |
166 | c.testsClient = newTests(c) | |
167 | } | |
168 | ||
169 | return c.testsClient | |
170 | } |