package pagerduty import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) const ( apiEndpoint = "https://api.pagerduty.com" ) // APIObject represents generic api json response that is shared by most // domain object (like escalation type APIObject struct { ID string `json:"id,omitempty"` Type string `json:"type,omitempty"` Summary string `json:"summary,omitempty"` Self string `json:"self,omitempty"` HTMLURL string `json:"html_url,omitempty"` } // APIListObject are the fields used to control pagination when listing objects. type APIListObject struct { Limit uint `url:"limit,omitempty"` Offset uint `url:"offset,omitempty"` More bool `url:"more,omitempty"` Total uint `url:"total,omitempty"` } // APIReference are the fields required to reference another API object. type APIReference struct { ID string `json:"id,omitempty"` Type string `json:"type,omitempty"` } type errorObject struct { Code int `json:"code,omitempty"` Mesage string `json:"message,omitempty"` Errors []string `json:"errors,omitempty"` } // Client wraps http client type Client struct { authToken string } // NewClient creates an API client func NewClient(authToken string) *Client { return &Client{ authToken: authToken, } } func (c *Client) delete(path string) (*http.Response, error) { return c.do("DELETE", path, nil, nil) } func (c *Client) put(path string, payload interface{}, headers *map[string]string) (*http.Response, error) { if payload != nil { data, err := json.Marshal(payload) if err != nil { return nil, err } return c.do("PUT", path, bytes.NewBuffer(data), headers) } return c.do("PUT", path, nil, headers) } func (c *Client) post(path string, payload interface{}) (*http.Response, error) { data, err := json.Marshal(payload) if err != nil { return nil, err } return c.do("POST", path, bytes.NewBuffer(data), nil) } func (c *Client) get(path string) (*http.Response, error) { return c.do("GET", path, nil, nil) } func (c *Client) do(method, path string, body io.Reader, headers *map[string]string) (*http.Response, error) { endpoint := apiEndpoint + path req, _ := http.NewRequest(method, endpoint, body) req.Header.Set("Accept", "application/vnd.pagerduty+json;version=2") if headers != nil { for k, v := range *headers { req.Header.Set(k, v) } } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Token token="+c.authToken) resp, err := http.DefaultClient.Do(req) return c.checkResponse(resp, err) } func (c *Client) decodeJSON(resp *http.Response, payload interface{}) error { defer resp.Body.Close() decoder := json.NewDecoder(resp.Body) return decoder.Decode(payload) } func (c *Client) checkResponse(resp *http.Response, err error) (*http.Response, error) { if err != nil { return resp, fmt.Errorf("Error calling the API endpoint: %v", err) } if 199 >= resp.StatusCode || 300 <= resp.StatusCode { var eo *errorObject var getErr error if eo, getErr = c.getErrorFromResponse(resp); getErr != nil { return resp, fmt.Errorf("Response did not contain formatted error: %s. HTTP response code: %v. Raw response: %+v", getErr, resp.StatusCode, resp) } return resp, fmt.Errorf("Failed call API endpoint. HTTP response code: %v. Error: %v", resp.StatusCode, eo) } return resp, nil } func (c *Client) getErrorFromResponse(resp *http.Response) (*errorObject, error) { var result map[string]errorObject if err := c.decodeJSON(resp, &result); err != nil { return nil, fmt.Errorf("Could not decode JSON response: %v", err) } s, ok := result["error"] if !ok { return nil, fmt.Errorf("JSON response does not have error field") } return &s, nil }