diff options
author | Jake Champlin <jake@gnu.space> | 2017-06-09 17:54:32 +0000 |
---|---|---|
committer | Jake Champlin <jake@gnu.space> | 2017-06-09 17:54:32 +0000 |
commit | 9b12e4fe6f3c95986f1f3ec791636c58ca7e7583 (patch) | |
tree | 38f5f12bec0e488a12f0459a7356e6b7de7d8f84 /vendor/github.com/DreamItGetIT | |
parent | cec3de8a3bcaffd21dedd1bf42da4b490cae7e16 (diff) | |
download | terraform-provider-statuscake-9b12e4fe6f3c95986f1f3ec791636c58ca7e7583.tar.gz terraform-provider-statuscake-9b12e4fe6f3c95986f1f3ec791636c58ca7e7583.tar.zst terraform-provider-statuscake-9b12e4fe6f3c95986f1f3ec791636c58ca7e7583.zip |
Transfer of provider code
Diffstat (limited to 'vendor/github.com/DreamItGetIT')
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/LICENSE | 21 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/README.md | 5 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/client.go | 170 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/doc.go | 34 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/errors.go | 80 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/makefile | 11 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/responses.go | 70 | ||||
-rw-r--r-- | vendor/github.com/DreamItGetIT/statuscake/tests.go | 298 |
8 files changed, 689 insertions, 0 deletions
diff --git a/vendor/github.com/DreamItGetIT/statuscake/LICENSE b/vendor/github.com/DreamItGetIT/statuscake/LICENSE new file mode 100644 index 0000000..5442aad --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/LICENSE | |||
@@ -0,0 +1,21 @@ | |||
1 | The MIT License (MIT) | ||
2 | |||
3 | Copyright (c) 2015 DreamItGetIT | ||
4 | |||
5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | of this software and associated documentation files (the "Software"), to deal | ||
7 | in the Software without restriction, including without limitation the rights | ||
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | copies of the Software, and to permit persons to whom the Software is | ||
10 | furnished to do so, subject to the following conditions: | ||
11 | |||
12 | The above copyright notice and this permission notice shall be included in all | ||
13 | copies or substantial portions of the Software. | ||
14 | |||
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
21 | SOFTWARE. | ||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/README.md b/vendor/github.com/DreamItGetIT/statuscake/README.md new file mode 100644 index 0000000..f1e4eaf --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/README.md | |||
@@ -0,0 +1,5 @@ | |||
1 | # statuscake | ||
2 | |||
3 | `statuscake` is a Go pkg that implements a client for the [statuscake]("https://statuscake.com") API. | ||
4 | |||
5 | More documentation and examples at [http://godoc.org/github.com/DreamItGetIT/statuscake](http://godoc.org/github.com/DreamItGetIT/statuscake). | ||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/client.go b/vendor/github.com/DreamItGetIT/statuscake/client.go new file mode 100644 index 0000000..6094be5 --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/client.go | |||
@@ -0,0 +1,170 @@ | |||
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 | } | ||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/doc.go b/vendor/github.com/DreamItGetIT/statuscake/doc.go new file mode 100644 index 0000000..fa68d94 --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/doc.go | |||
@@ -0,0 +1,34 @@ | |||
1 | // Package statuscake implements a client for statuscake.com API. | ||
2 | // | ||
3 | // // list all `Tests` | ||
4 | // c, err := statuscake.New(statuscake.Auth{Username: username, Apikey: apikey}) | ||
5 | // if err != nil { | ||
6 | // log.Fatal(err) | ||
7 | // } | ||
8 | // | ||
9 | // tests, err := c.Tests().All() | ||
10 | // if err != nil { | ||
11 | // log.Fatal(err) | ||
12 | // } | ||
13 | // | ||
14 | // // delete a `Test` | ||
15 | // err = c.Tests().Delete(TestID) | ||
16 | // | ||
17 | // // create a test | ||
18 | // t := &statuscake.Test{ | ||
19 | // WebsiteName: "Foo", | ||
20 | // WebsiteURL: "htto://example.com", | ||
21 | // ... other required args... | ||
22 | // } | ||
23 | // | ||
24 | // if err = t.Validate(); err != nil { | ||
25 | // log.Fatal(err) | ||
26 | // } | ||
27 | // | ||
28 | // t2 := c.Tests().Update(t) | ||
29 | // fmt.Printf("New Test created with id: %d\n", t2.TestID) | ||
30 | // | ||
31 | // // get Tests details | ||
32 | // t, err := tt.Detail(id) | ||
33 | // ... | ||
34 | package statuscake | ||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/errors.go b/vendor/github.com/DreamItGetIT/statuscake/errors.go new file mode 100644 index 0000000..4c51991 --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/errors.go | |||
@@ -0,0 +1,80 @@ | |||
1 | package statuscake | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | ) | ||
7 | |||
8 | // APIError implements the error interface an it's used when the API response has errors. | ||
9 | type APIError interface { | ||
10 | APIError() string | ||
11 | } | ||
12 | |||
13 | type httpError struct { | ||
14 | status string | ||
15 | statusCode int | ||
16 | } | ||
17 | |||
18 | func (e *httpError) Error() string { | ||
19 | return fmt.Sprintf("HTTP error: %d - %s", e.statusCode, e.status) | ||
20 | } | ||
21 | |||
22 | // ValidationError is a map where the key is the invalid field and the value is a message describing why the field is invalid. | ||
23 | type ValidationError map[string]string | ||
24 | |||
25 | func (e ValidationError) Error() string { | ||
26 | var messages []string | ||
27 | |||
28 | for k, v := range e { | ||
29 | m := fmt.Sprintf("%s %s", k, v) | ||
30 | messages = append(messages, m) | ||
31 | } | ||
32 | |||
33 | return strings.Join(messages, ", ") | ||
34 | } | ||
35 | |||
36 | type updateError struct { | ||
37 | Issues interface{} | ||
38 | } | ||
39 | |||
40 | func (e *updateError) Error() string { | ||
41 | var messages []string | ||
42 | |||
43 | if issues, ok := e.Issues.(map[string]interface{}); ok { | ||
44 | for k, v := range issues { | ||
45 | m := fmt.Sprintf("%s %s", k, v) | ||
46 | messages = append(messages, m) | ||
47 | } | ||
48 | } else if issues, ok := e.Issues.([]interface{}); ok { | ||
49 | for _, v := range issues { | ||
50 | m := fmt.Sprint(v) | ||
51 | messages = append(messages, m) | ||
52 | } | ||
53 | } | ||
54 | |||
55 | return strings.Join(messages, ", ") | ||
56 | } | ||
57 | |||
58 | // APIError returns the error specified in the API response | ||
59 | func (e *updateError) APIError() string { | ||
60 | return e.Error() | ||
61 | } | ||
62 | |||
63 | type deleteError struct { | ||
64 | Message string | ||
65 | } | ||
66 | |||
67 | func (e *deleteError) Error() string { | ||
68 | return e.Message | ||
69 | } | ||
70 | |||
71 | // AuthenticationError implements the error interface and it's returned | ||
72 | // when API responses have authentication errors | ||
73 | type AuthenticationError struct { | ||
74 | errNo int | ||
75 | message string | ||
76 | } | ||
77 | |||
78 | func (e *AuthenticationError) Error() string { | ||
79 | return fmt.Sprintf("%d, %s", e.errNo, e.message) | ||
80 | } | ||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/makefile b/vendor/github.com/DreamItGetIT/statuscake/makefile new file mode 100644 index 0000000..946f6d9 --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | .PHONY: default lint test | ||
2 | |||
3 | default: lint test | ||
4 | |||
5 | lint: | ||
6 | @golint ./... | ||
7 | @go vet ./... | ||
8 | |||
9 | test: | ||
10 | go test ${GOTEST_ARGS} ./... | ||
11 | |||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/responses.go b/vendor/github.com/DreamItGetIT/statuscake/responses.go new file mode 100644 index 0000000..b9216b7 --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/responses.go | |||
@@ -0,0 +1,70 @@ | |||
1 | package statuscake | ||
2 | |||
3 | type autheticationErrorResponse struct { | ||
4 | ErrNo int | ||
5 | Error string | ||
6 | } | ||
7 | |||
8 | type updateResponse struct { | ||
9 | Issues interface{} `json:"Issues"` | ||
10 | Success bool `json:"Success"` | ||
11 | Message string `json:"Message"` | ||
12 | InsertID int `json:"InsertID"` | ||
13 | } | ||
14 | |||
15 | type deleteResponse struct { | ||
16 | Success bool `json:"Success"` | ||
17 | Error string `json:"Error"` | ||
18 | } | ||
19 | |||
20 | type detailResponse struct { | ||
21 | Method string `json:"Method"` | ||
22 | TestID int `json:"TestID"` | ||
23 | TestType string `json:"TestType"` | ||
24 | Paused bool `json:"Paused"` | ||
25 | WebsiteName string `json:"WebsiteName"` | ||
26 | URI string `json:"URI"` | ||
27 | ContactID int `json:"ContactID"` | ||
28 | Status string `json:"Status"` | ||
29 | Uptime float64 `json:"Uptime"` | ||
30 | CheckRate int `json:"CheckRate"` | ||
31 | Timeout int `json:"Timeout"` | ||
32 | LogoImage string `json:"LogoImage"` | ||
33 | Confirmation int `json:"Confirmation,string"` | ||
34 | WebsiteHost string `json:"WebsiteHost"` | ||
35 | NodeLocations []string `json:"NodeLocations"` | ||
36 | FindString string `json:"FindString"` | ||
37 | DoNotFind bool `json:"DoNotFind"` | ||
38 | LastTested string `json:"LastTested"` | ||
39 | NextLocation string `json:"NextLocation"` | ||
40 | Port int `json:"Port"` | ||
41 | Processing bool `json:"Processing"` | ||
42 | ProcessingState string `json:"ProcessingState"` | ||
43 | ProcessingOn string `json:"ProcessingOn"` | ||
44 | DownTimes int `json:"DownTimes,string"` | ||
45 | Sensitive bool `json:"Sensitive"` | ||
46 | TriggerRate int `json:"TriggerRate,string"` | ||
47 | } | ||
48 | |||
49 | func (d *detailResponse) test() *Test { | ||
50 | return &Test{ | ||
51 | TestID: d.TestID, | ||
52 | TestType: d.TestType, | ||
53 | Paused: d.Paused, | ||
54 | WebsiteName: d.WebsiteName, | ||
55 | WebsiteURL: d.URI, | ||
56 | ContactID: d.ContactID, | ||
57 | Status: d.Status, | ||
58 | Uptime: d.Uptime, | ||
59 | CheckRate: d.CheckRate, | ||
60 | Timeout: d.Timeout, | ||
61 | LogoImage: d.LogoImage, | ||
62 | Confirmation: d.Confirmation, | ||
63 | WebsiteHost: d.WebsiteHost, | ||
64 | NodeLocations: d.NodeLocations, | ||
65 | FindString: d.FindString, | ||
66 | DoNotFind: d.DoNotFind, | ||
67 | Port: d.Port, | ||
68 | TriggerRate: d.TriggerRate, | ||
69 | } | ||
70 | } | ||
diff --git a/vendor/github.com/DreamItGetIT/statuscake/tests.go b/vendor/github.com/DreamItGetIT/statuscake/tests.go new file mode 100644 index 0000000..4053e53 --- /dev/null +++ b/vendor/github.com/DreamItGetIT/statuscake/tests.go | |||
@@ -0,0 +1,298 @@ | |||
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 | // 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 | |||
24 | // Test location, either an IP (for TCP and Ping) or a fully qualified URL for other TestTypes | ||
25 | WebsiteURL string `json:"WebsiteURL" querystring:"WebsiteURL"` | ||
26 | |||
27 | // A Port to use on TCP Tests | ||
28 | Port int `json:"Port" querystring:"Port"` | ||
29 | |||
30 | // Contact group ID - will return int of contact group used else 0 | ||
31 | ContactID int `json:"ContactID" querystring:"ContactGroup"` | ||
32 | |||
33 | // Current status at last test | ||
34 | Status string `json:"Status"` | ||
35 | |||
36 | // 7 Day Uptime | ||
37 | Uptime float64 `json:"Uptime"` | ||
38 | |||
39 | // Any test locations seperated by a comma (using the Node Location IDs) | ||
40 | NodeLocations []string `json:"NodeLocations" querystring:"NodeLocations"` | ||
41 | |||
42 | // Timeout in an int form representing seconds. | ||
43 | Timeout int `json:"Timeout" querystring:"Timeout"` | ||
44 | |||
45 | // A URL to ping if a site goes down. | ||
46 | PingURL string `json:"PingURL" querystring:"PingURL"` | ||
47 | |||
48 | Confirmation int `json:"Confirmationi,string" querystring:"Confirmation"` | ||
49 | |||
50 | // The number of seconds between checks. | ||
51 | CheckRate int `json:"CheckRate" querystring:"CheckRate"` | ||
52 | |||
53 | // A Basic Auth User account to use to login | ||
54 | BasicUser string `json:"BasicUser" querystring:"BasicUser"` | ||
55 | |||
56 | // If BasicUser is set then this should be the password for the BasicUser | ||
57 | BasicPass string `json:"BasicPass" querystring:"BasicPass"` | ||
58 | |||
59 | // Set 1 to enable public reporting, 0 to disable | ||
60 | Public int `json:"Public" querystring:"Public"` | ||
61 | |||
62 | // A URL to a image to use for public reporting | ||
63 | LogoImage string `json:"LogoImage" querystring:"LogoImage"` | ||
64 | |||
65 | // Set to 0 to use branding (default) or 1 to disable public reporting branding | ||
66 | Branding int `json:"Branding" querystring:"Branding"` | ||
67 | |||
68 | // Used internally by the statuscake API | ||
69 | WebsiteHost string `json:"WebsiteHost"` | ||
70 | |||
71 | // Enable virus checking or not. 1 to enable | ||
72 | Virus int `json:"Virus" querystring:"Virus"` | ||
73 | |||
74 | // A string that should either be found or not found. | ||
75 | FindString string `json:"FindString" querystring:"FindString"` | ||
76 | |||
77 | // If the above string should be found to trigger a alert. true will trigger if FindString found | ||
78 | DoNotFind bool `json:"DoNotFind" querystring:"DoNotFind"` | ||
79 | |||
80 | // What type of test type to use. Accepted values are HTTP, TCP, PING | ||
81 | TestType string `json:"TestType" querystring:"TestType"` | ||
82 | |||
83 | // Use 1 to TURN OFF real browser testing | ||
84 | RealBrowser int `json:"RealBrowser" querystring:"RealBrowser"` | ||
85 | |||
86 | // How many minutes to wait before sending an alert | ||
87 | TriggerRate int `json:"TriggerRate" querystring:"TriggerRate"` | ||
88 | |||
89 | // Tags should be seperated by a comma - no spacing between tags (this,is,a set,of,tags) | ||
90 | TestTags string `json:"TestTags" querystring:"TestTags"` | ||
91 | |||
92 | // Comma Seperated List of StatusCodes to Trigger Error on (on Update will replace, so send full list each time) | ||
93 | StatusCodes string `json:"StatusCodes" querystring:"StatusCodes"` | ||
94 | } | ||
95 | |||
96 | // Validate checks if the Test is valid. If it's invalid, it returns a ValidationError with all invalid fields. It returns nil otherwise. | ||
97 | func (t *Test) Validate() error { | ||
98 | e := make(ValidationError) | ||
99 | |||
100 | if t.WebsiteName == "" { | ||
101 | e["WebsiteName"] = "is required" | ||
102 | } | ||
103 | |||
104 | if t.WebsiteURL == "" { | ||
105 | e["WebsiteURL"] = "is required" | ||
106 | } | ||
107 | |||
108 | if t.Timeout != 0 && (t.Timeout < 6 || t.Timeout > 99) { | ||
109 | e["Timeout"] = "must be 0 or between 6 and 99" | ||
110 | } | ||
111 | |||
112 | if t.Confirmation < 0 || t.Confirmation > 9 { | ||
113 | e["Confirmation"] = "must be between 0 and 9" | ||
114 | } | ||
115 | |||
116 | if t.CheckRate < 0 || t.CheckRate > 23999 { | ||
117 | e["CheckRate"] = "must be between 0 and 23999" | ||
118 | } | ||
119 | |||
120 | if t.Public < 0 || t.Public > 1 { | ||
121 | e["Public"] = "must be 0 or 1" | ||
122 | } | ||
123 | |||
124 | if t.Virus < 0 || t.Virus > 1 { | ||
125 | e["Virus"] = "must be 0 or 1" | ||
126 | } | ||
127 | |||
128 | if t.TestType != "HTTP" && t.TestType != "TCP" && t.TestType != "PING" { | ||
129 | e["TestType"] = "must be HTTP, TCP, or PING" | ||
130 | } | ||
131 | |||
132 | if t.RealBrowser < 0 || t.RealBrowser > 1 { | ||
133 | e["RealBrowser"] = "must be 0 or 1" | ||
134 | } | ||
135 | |||
136 | if t.TriggerRate < 0 || t.TriggerRate > 59 { | ||
137 | e["TriggerRate"] = "must be between 0 and 59" | ||
138 | } | ||
139 | |||
140 | if len(e) > 0 { | ||
141 | return e | ||
142 | } | ||
143 | |||
144 | return nil | ||
145 | } | ||
146 | |||
147 | // ToURLValues returns url.Values of all fields required to create/update a Test. | ||
148 | func (t Test) ToURLValues() url.Values { | ||
149 | values := make(url.Values) | ||
150 | st := reflect.TypeOf(t) | ||
151 | sv := reflect.ValueOf(t) | ||
152 | for i := 0; i < st.NumField(); i++ { | ||
153 | sf := st.Field(i) | ||
154 | tag := sf.Tag.Get(queryStringTag) | ||
155 | ft := sf.Type | ||
156 | if ft.Name() == "" && ft.Kind() == reflect.Ptr { | ||
157 | // Follow pointer. | ||
158 | ft = ft.Elem() | ||
159 | } | ||
160 | |||
161 | v := sv.Field(i) | ||
162 | options := sf.Tag.Get("querystringoptions") | ||
163 | omit := options == "omitempty" && isEmptyValue(v) | ||
164 | |||
165 | if tag != "" && !omit { | ||
166 | values.Set(tag, valueToQueryStringValue(v)) | ||
167 | } | ||
168 | } | ||
169 | |||
170 | return values | ||
171 | } | ||
172 | |||
173 | func isEmptyValue(v reflect.Value) bool { | ||
174 | switch v.Kind() { | ||
175 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: | ||
176 | return v.Len() == 0 | ||
177 | case reflect.Bool: | ||
178 | return !v.Bool() | ||
179 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
180 | return v.Int() == 0 | ||
181 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
182 | return v.Uint() == 0 | ||
183 | case reflect.Float32, reflect.Float64: | ||
184 | return v.Float() == 0 | ||
185 | case reflect.Interface, reflect.Ptr: | ||
186 | return v.IsNil() | ||
187 | } | ||
188 | |||
189 | return false | ||
190 | } | ||
191 | |||
192 | func valueToQueryStringValue(v reflect.Value) string { | ||
193 | if v.Type().Name() == "bool" { | ||
194 | if v.Bool() { | ||
195 | return "1" | ||
196 | } | ||
197 | |||
198 | return "0" | ||
199 | } | ||
200 | |||
201 | if v.Type().Kind() == reflect.Slice { | ||
202 | if ss, ok := v.Interface().([]string); ok { | ||
203 | return strings.Join(ss, ",") | ||
204 | } | ||
205 | } | ||
206 | |||
207 | return fmt.Sprint(v) | ||
208 | } | ||
209 | |||
210 | // Tests is a client that implements the `Tests` API. | ||
211 | type Tests interface { | ||
212 | All() ([]*Test, error) | ||
213 | Detail(int) (*Test, error) | ||
214 | Update(*Test) (*Test, error) | ||
215 | Delete(TestID int) error | ||
216 | } | ||
217 | |||
218 | type tests struct { | ||
219 | client apiClient | ||
220 | } | ||
221 | |||
222 | func newTests(c apiClient) Tests { | ||
223 | return &tests{ | ||
224 | client: c, | ||
225 | } | ||
226 | } | ||
227 | |||
228 | func (tt *tests) All() ([]*Test, error) { | ||
229 | resp, err := tt.client.get("/Tests", nil) | ||
230 | if err != nil { | ||
231 | return nil, err | ||
232 | } | ||
233 | defer resp.Body.Close() | ||
234 | |||
235 | var tests []*Test | ||
236 | err = json.NewDecoder(resp.Body).Decode(&tests) | ||
237 | |||
238 | return tests, err | ||
239 | } | ||
240 | |||
241 | func (tt *tests) Update(t *Test) (*Test, error) { | ||
242 | resp, err := tt.client.put("/Tests/Update", t.ToURLValues()) | ||
243 | if err != nil { | ||
244 | return nil, err | ||
245 | } | ||
246 | defer resp.Body.Close() | ||
247 | |||
248 | var ur updateResponse | ||
249 | err = json.NewDecoder(resp.Body).Decode(&ur) | ||
250 | if err != nil { | ||
251 | return nil, err | ||
252 | } | ||
253 | |||
254 | if !ur.Success { | ||
255 | return nil, &updateError{Issues: ur.Issues} | ||
256 | } | ||
257 | |||
258 | t2 := *t | ||
259 | t2.TestID = ur.InsertID | ||
260 | |||
261 | return &t2, err | ||
262 | } | ||
263 | |||
264 | func (tt *tests) Delete(testID int) error { | ||
265 | resp, err := tt.client.delete("/Tests/Details", url.Values{"TestID": {fmt.Sprint(testID)}}) | ||
266 | if err != nil { | ||
267 | return err | ||
268 | } | ||
269 | defer resp.Body.Close() | ||
270 | |||
271 | var dr deleteResponse | ||
272 | err = json.NewDecoder(resp.Body).Decode(&dr) | ||
273 | if err != nil { | ||
274 | return err | ||
275 | } | ||
276 | |||
277 | if !dr.Success { | ||
278 | return &deleteError{Message: dr.Error} | ||
279 | } | ||
280 | |||
281 | return nil | ||
282 | } | ||
283 | |||
284 | func (tt *tests) Detail(testID int) (*Test, error) { | ||
285 | resp, err := tt.client.get("/Tests/Details", url.Values{"TestID": {fmt.Sprint(testID)}}) | ||
286 | if err != nil { | ||
287 | return nil, err | ||
288 | } | ||
289 | defer resp.Body.Close() | ||
290 | |||
291 | var dr *detailResponse | ||
292 | err = json.NewDecoder(resp.Body).Decode(&dr) | ||
293 | if err != nil { | ||
294 | return nil, err | ||
295 | } | ||
296 | |||
297 | return dr.test(), nil | ||
298 | } | ||