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