This repository has been archived by the owner on Aug 4, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
/
query.go
156 lines (125 loc) · 4.07 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package client
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"regexp"
log "github.com/sirupsen/logrus"
)
// NewQueryClient makes a new client for the user to query with.
func NewQueryClient(queryKey, accountID string) *QueryClient {
client := &QueryClient{}
client.URL = createQueryURL(accountID)
client.QueryKey = queryKey
client.Logger = log.New()
// Defaults
client.RequestTimeout = DefaultQueryRequestTimeout
client.RetryCount = DefaultRetries
client.RetryWait = DefaultRetryWaitTime
return client
}
func createQueryURL(accountID string) *url.URL {
insightsURL, _ := url.Parse(insightsQueryURL)
insightsURL.Path = fmt.Sprintf("%s/%s/query", insightsURL.Path, accountID)
return insightsURL
}
// Validate makes sure the QueryClient is configured correctly for use
func (c *QueryClient) Validate() error {
if correct, _ := regexp.MatchString("api.newrelic.com/v1/accounts/[0-9]+/query", c.URL.String()); !correct {
return fmt.Errorf("invalid query endpoint %s", c.URL)
}
if len(c.QueryKey) < 1 {
return fmt.Errorf("not a valid license key: %s", c.QueryKey)
}
return nil
}
// QueryEvents initiates an Insights query, returns a response for parsing
func (c *QueryClient) QueryEvents(nrqlQuery string) (response *QueryResponse, err error) {
response = &QueryResponse{}
err = c.Query(nrqlQuery, response)
if err != nil {
return nil, err
}
return response, nil
}
// Query initiates an Insights query, with the JSON parsed into 'response' struct
func (c *QueryClient) Query(nrqlQuery string, response interface{}) (err error) {
if response == nil {
return errors.New("go-insights: Invalid query response can not be nil")
}
err = c.queryRequest(nrqlQuery, response)
if err != nil {
return err
}
return nil
}
// queryRequest makes a NRQL query and returns the result in `queryResult`
// which must be a pointer to a struct that the JSON package can unmarshall
func (c *QueryClient) queryRequest(nrqlQuery string, queryResult interface{}) (err error) {
var request *http.Request
var response *http.Response
queryURL, err := c.generateQueryURL(nrqlQuery)
if err != nil {
return err
}
if queryResult == nil {
return errors.New("must have pointer for result")
}
request, err = http.NewRequest("GET", queryURL, nil)
if err != nil {
return err
}
request.Header.Add("Accept", "application/json")
request.Header.Add("X-Query-Key", c.QueryKey)
client := &http.Client{Timeout: c.RequestTimeout}
response, err = client.Do(request)
if err != nil {
err = fmt.Errorf("failed query request for: %v", err)
return
}
defer func() {
respErr := response.Body.Close()
if respErr != nil && err == nil {
err = respErr // Don't mask previous errors
}
}()
if response.StatusCode != http.StatusOK {
err = fmt.Errorf("bad response code: %d", response.StatusCode)
return
}
err = c.parseResponse(response, queryResult)
if err != nil {
err = fmt.Errorf("failed query: %v", err)
}
return err
}
// generateQueryURL URL encodes the NRQL
func (c *QueryClient) generateQueryURL(nrqlQuery string) (string, error) {
if len(nrqlQuery) < minValidNRQLLength {
fmt.Println("Query was too short")
return "", fmt.Errorf("NRQL query is too short [%s]", nrqlQuery)
}
// Use a new set of Values to sanitize the query string
urlQuery := url.Values{}
urlQuery.Set("nrql", nrqlQuery)
queryString := urlQuery.Encode()
queryURL := c.URL.String() + "?" + queryString
c.Logger.Debugf("query url is: %s", queryURL)
return queryURL, nil
}
// parseQueryResponse takes an HTTP response, make sure it is a valid response,
// then attempts to decode the JSON body into the `parsedResponse` interface
func (c *QueryClient) parseResponse(response *http.Response, parsedResponse interface{}) error {
body, readErr := ioutil.ReadAll(response.Body)
if readErr != nil {
return fmt.Errorf("failed to read response body: %s", readErr.Error())
}
c.Logger.Debugf("Response %d body: %s", response.StatusCode, body)
if jsonErr := json.Unmarshal(body, parsedResponse); jsonErr != nil {
return fmt.Errorf("unable to unmarshal query response: %v", jsonErr)
}
return nil
}