]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/DreamItGetIT/statuscake/tests.go
Update client dependency
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / DreamItGetIT / statuscake / tests.go
1 package statuscake
2
3 import (
4 "encoding/json"
5 "fmt"
6 "net/url"
7 "reflect"
8 "strings"
9 )
10
11 const queryStringTag = "querystring"
12
13 // Test represents a statuscake Test
14 type Test struct {
15 // TestID is an int, use this to get more details about this test. If not provided will insert a new check, else will update
16 TestID int `json:"TestID" querystring:"TestID" querystringoptions:"omitempty"`
17
18 // Sent tfalse To Unpause and true To Pause.
19 Paused bool `json:"Paused" querystring:"Paused"`
20
21 // Website name. Tags are stripped out
22 WebsiteName string `json:"WebsiteName" querystring:"WebsiteName"`
23
24 // CustomHeader. A special header that will be sent along with the HTTP tests.
25 CustomHeader string `json:"CustomHeader" querystring:"CustomHeader"`
26
27 // Use to populate the test with a custom user agent
28 UserAgent string `json:"UserAgent" queryString:"UserAgent"`
29
30 // Test location, either an IP (for TCP and Ping) or a fully qualified URL for other TestTypes
31 WebsiteURL string `json:"WebsiteURL" querystring:"WebsiteURL"`
32
33 // A Port to use on TCP Tests
34 Port int `json:"Port" querystring:"Port"`
35
36 // Contact group ID - deprecated in favor of ContactGroup but still provided in the API detail response
37 ContactID int `json:"ContactID"`
38
39 // Contact group IDs - will return list of ints or empty if not provided
40 ContactGroup []string `json:"ContactGroup" querystring:"ContactGroup"`
41
42 // Current status at last test
43 Status string `json:"Status"`
44
45 // 1 Day Uptime
46 Uptime float64 `json:"Uptime"`
47
48 // Any test locations seperated by a comma (using the Node Location IDs)
49 NodeLocations []string `json:"NodeLocations" querystring:"NodeLocations"`
50
51 // Timeout in an int form representing seconds.
52 Timeout int `json:"Timeout" querystring:"Timeout"`
53
54 // A URL to ping if a site goes down.
55 PingURL string `json:"PingURL" querystring:"PingURL"`
56
57 Confirmation int `json:"Confirmation,string" querystring:"Confirmation"`
58
59 // The number of seconds between checks.
60 CheckRate int `json:"CheckRate" querystring:"CheckRate"`
61
62 // A Basic Auth User account to use to login
63 BasicUser string `json:"BasicUser" querystring:"BasicUser"`
64
65 // If BasicUser is set then this should be the password for the BasicUser
66 BasicPass string `json:"BasicPass" querystring:"BasicPass"`
67
68 // Set 1 to enable public reporting, 0 to disable
69 Public int `json:"Public" querystring:"Public"`
70
71 // A URL to a image to use for public reporting
72 LogoImage string `json:"LogoImage" querystring:"LogoImage"`
73
74 // Set to 0 to use branding (default) or 1 to disable public reporting branding
75 Branding int `json:"Branding" querystring:"Branding"`
76
77 // Used internally by the statuscake API
78 WebsiteHost string `json:"WebsiteHost" querystring:"WebsiteHost"`
79
80 // Enable virus checking or not. 1 to enable
81 Virus int `json:"Virus" querystring:"Virus"`
82
83 // A string that should either be found or not found.
84 FindString string `json:"FindString" querystring:"FindString"`
85
86 // If the above string should be found to trigger a alert. true will trigger if FindString found
87 DoNotFind bool `json:"DoNotFind" querystring:"DoNotFind"`
88
89 // What type of test type to use. Accepted values are HTTP, TCP, PING
90 TestType string `json:"TestType" querystring:"TestType"`
91
92 // Use 1 to TURN OFF real browser testing
93 RealBrowser int `json:"RealBrowser" querystring:"RealBrowser"`
94
95 // How many minutes to wait before sending an alert
96 TriggerRate int `json:"TriggerRate" querystring:"TriggerRate"`
97
98 // Tags should be seperated by a comma - no spacing between tags (this,is,a set,of,tags)
99 TestTags []string `json:"TestTags" querystring:"TestTags"`
100
101 // Comma Seperated List of StatusCodes to Trigger Error on (on Update will replace, so send full list each time)
102 StatusCodes string `json:"StatusCodes" querystring:"StatusCodes"`
103
104 // Set to 1 to enable the Cookie Jar. Required for some redirects.
105 UseJar int `json:"UseJar" querystring:"UseJar"`
106
107 // Raw POST data seperated by an ampersand
108 PostRaw string `json:"PostRaw" querystring:"PostRaw"`
109
110 // Use to specify the expected Final URL in the testing process
111 FinalEndpoint string `json:"FinalEndpoint" querystring:"FinalEndpoint"`
112
113 // Use to enable SSL validation
114 EnableSSLAlert bool `json:"EnableSSLAlert" querystring:"EnableSSLAlert"`
115
116 // Use to specify whether redirects should be followed
117 FollowRedirect bool `json:"FollowRedirect" querystring:"FollowRedirect"`
118 }
119
120 // Validate checks if the Test is valid. If it's invalid, it returns a ValidationError with all invalid fields. It returns nil otherwise.
121 func (t *Test) Validate() error {
122 e := make(ValidationError)
123
124 if t.WebsiteName == "" {
125 e["WebsiteName"] = "is required"
126 }
127
128 if t.WebsiteURL == "" {
129 e["WebsiteURL"] = "is required"
130 }
131
132 if t.Timeout != 0 && (t.Timeout < 6 || t.Timeout > 99) {
133 e["Timeout"] = "must be 0 or between 6 and 99"
134 }
135
136 if t.Confirmation < 0 || t.Confirmation > 9 {
137 e["Confirmation"] = "must be between 0 and 9"
138 }
139
140 if t.CheckRate < 0 || t.CheckRate > 23999 {
141 e["CheckRate"] = "must be between 0 and 23999"
142 }
143
144 if t.Public < 0 || t.Public > 1 {
145 e["Public"] = "must be 0 or 1"
146 }
147
148 if t.Virus < 0 || t.Virus > 1 {
149 e["Virus"] = "must be 0 or 1"
150 }
151
152 if t.TestType != "HTTP" && t.TestType != "TCP" && t.TestType != "PING" {
153 e["TestType"] = "must be HTTP, TCP, or PING"
154 }
155
156 if t.RealBrowser < 0 || t.RealBrowser > 1 {
157 e["RealBrowser"] = "must be 0 or 1"
158 }
159
160 if t.TriggerRate < 0 || t.TriggerRate > 59 {
161 e["TriggerRate"] = "must be between 0 and 59"
162 }
163
164 if t.PostRaw != "" && t.TestType != "HTTP" {
165 e["PostRaw"] = "must be HTTP to submit a POST request"
166 }
167
168 if t.FinalEndpoint != "" && t.TestType != "HTTP" {
169 e["FinalEndpoint"] = "must be a Valid URL"
170 }
171
172 if t.CustomHeader != "" {
173 var jsonVerifiable map[string]interface{}
174 if json.Unmarshal([]byte(t.CustomHeader), &jsonVerifiable) != nil {
175 e["CustomHeader"] = "must be provided as json string"
176 }
177 }
178
179 if len(e) > 0 {
180 return e
181 }
182
183 return nil
184 }
185
186 // ToURLValues returns url.Values of all fields required to create/update a Test.
187 func (t Test) ToURLValues() url.Values {
188 values := make(url.Values)
189 st := reflect.TypeOf(t)
190 sv := reflect.ValueOf(t)
191 for i := 0; i < st.NumField(); i++ {
192 sf := st.Field(i)
193 tag := sf.Tag.Get(queryStringTag)
194 ft := sf.Type
195 if ft.Name() == "" && ft.Kind() == reflect.Ptr {
196 // Follow pointer.
197 ft = ft.Elem()
198 }
199
200 v := sv.Field(i)
201 options := sf.Tag.Get("querystringoptions")
202 omit := options == "omitempty" && isEmptyValue(v)
203
204 if tag != "" && !omit {
205 values.Set(tag, valueToQueryStringValue(v))
206 }
207 }
208
209 return values
210 }
211
212 func isEmptyValue(v reflect.Value) bool {
213 switch v.Kind() {
214 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
215 return v.Len() == 0
216 case reflect.Bool:
217 return !v.Bool()
218 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
219 return v.Int() == 0
220 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
221 return v.Uint() == 0
222 case reflect.Float32, reflect.Float64:
223 return v.Float() == 0
224 case reflect.Interface, reflect.Ptr:
225 return v.IsNil()
226 }
227
228 return false
229 }
230
231 func valueToQueryStringValue(v reflect.Value) string {
232 if v.Type().Name() == "bool" {
233 if v.Bool() {
234 return "1"
235 }
236
237 return "0"
238 }
239
240 if v.Type().Kind() == reflect.Slice {
241 if ss, ok := v.Interface().([]string); ok {
242 return strings.Join(ss, ",")
243 }
244 }
245
246 return fmt.Sprint(v)
247 }
248
249 // Tests is a client that implements the `Tests` API.
250 type Tests interface {
251 All() ([]*Test, error)
252 AllWithFilter(url.Values) ([]*Test, error)
253 Detail(int) (*Test, error)
254 Update(*Test) (*Test, error)
255 Delete(TestID int) error
256 }
257
258 type tests struct {
259 client apiClient
260 }
261
262 func newTests(c apiClient) Tests {
263 return &tests{
264 client: c,
265 }
266 }
267
268 func (tt *tests) All() ([]*Test, error) {
269 resp, err := tt.client.get("/Tests", nil)
270 if err != nil {
271 return nil, err
272 }
273 defer resp.Body.Close()
274
275 var tests []*Test
276 err = json.NewDecoder(resp.Body).Decode(&tests)
277
278 return tests, err
279 }
280
281 func (tt *tests) AllWithFilter(filterOptions url.Values) ([]*Test, error) {
282 resp, err := tt.client.get("/Tests", filterOptions)
283 if err != nil {
284 return nil, err
285 }
286 defer resp.Body.Close()
287
288 var tests []*Test
289 err = json.NewDecoder(resp.Body).Decode(&tests)
290
291 return tests, err
292 }
293
294 func (tt *tests) Update(t *Test) (*Test, error) {
295 resp, err := tt.client.put("/Tests/Update", t.ToURLValues())
296 if err != nil {
297 return nil, err
298 }
299 defer resp.Body.Close()
300
301 var ur updateResponse
302 err = json.NewDecoder(resp.Body).Decode(&ur)
303 if err != nil {
304 return nil, err
305 }
306
307 if !ur.Success {
308 return nil, &updateError{Issues: ur.Issues, Message: ur.Message}
309 }
310
311 t2 := *t
312 t2.TestID = ur.InsertID
313
314 return &t2, err
315 }
316
317 func (tt *tests) Delete(testID int) error {
318 resp, err := tt.client.delete("/Tests/Details", url.Values{"TestID": {fmt.Sprint(testID)}})
319 if err != nil {
320 return err
321 }
322 defer resp.Body.Close()
323
324 var dr deleteResponse
325 err = json.NewDecoder(resp.Body).Decode(&dr)
326 if err != nil {
327 return err
328 }
329
330 if !dr.Success {
331 return &deleteError{Message: dr.Error}
332 }
333
334 return nil
335 }
336
337 func (tt *tests) Detail(testID int) (*Test, error) {
338 resp, err := tt.client.get("/Tests/Details", url.Values{"TestID": {fmt.Sprint(testID)}})
339 if err != nil {
340 return nil, err
341 }
342 defer resp.Body.Close()
343
344 var dr *detailResponse
345 err = json.NewDecoder(resp.Body).Decode(&dr)
346 if err != nil {
347 return nil, err
348 }
349
350 return dr.test(), nil
351 }