package api import ( "bytes" "encoding/json" "fmt" "net/http" ) // Response is a custom response that wraps an HTTP response. Body will be // populated with a buffer containing the response body after Decode is called; // it will be nil if the response was a 204. type Response struct { resp *http.Response Body *bytes.Buffer Map map[string]interface{} } // HttpResponse returns the underlying HTTP response func (r *Response) HttpResponse() *http.Response { return r.resp } func (r *Response) Decode(inStruct interface{}) (*Error, error) { if r == nil || r.resp == nil { return nil, fmt.Errorf("nil response, cannot decode") } defer r.resp.Body.Close() if r.resp.StatusCode == 204 { // Do nothing. return nil, nil } // TODO Remove as we'll use the common err format for this if r.resp.StatusCode == 403 { // Nothing to be done return &Error{ Status: http.StatusForbidden, Message: "Forbidden", }, nil } apiErr := &Error{ Status: int32(r.resp.StatusCode), } if r.resp.Body != nil { r.Body = new(bytes.Buffer) if _, err := r.Body.ReadFrom(r.resp.Body); err != nil { return nil, fmt.Errorf("error reading response body: %w", err) } if r.Body.Len() > 0 { reader := bytes.NewReader(r.Body.Bytes()) dec := json.NewDecoder(reader) dec.UseNumber() if r.resp.StatusCode >= 400 { inStruct = apiErr } r.Map = make(map[string]interface{}) if err := dec.Decode(&r.Map); err != nil { return nil, fmt.Errorf("error decoding response to map: %w; response was %s", err, r.Body.String()) } if inStruct != nil { reader.Seek(0, 0) dec = json.NewDecoder(reader) if err := dec.Decode(&inStruct); err != nil { return nil, fmt.Errorf("error decoding response to struct: %w; response was %s", err, r.Body.String()) } } } } if r.resp.StatusCode >= 400 { errStruct := inStruct.(*Error) errStruct.responseBody = r.Body errStruct.responseMap = r.Map return apiErr, nil } return nil, nil }