/
util.go
134 lines (109 loc) · 3.8 KB
/
util.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
package apischema
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/juju/errgo"
)
func IsStatus(statusCode int, responseBody string) (bool, error) {
return isStatus(statusCode, "", responseBody)
}
func IsStatusAndReason(statusCode int, reason, responseBody string) (bool, error) {
return isStatus(statusCode, reason, responseBody)
}
func IsStatusWithRawBody(statusCode int, resBody *io.ReadCloser) (bool, error) {
return IsStatusAndReasonWithRawBody(statusCode, "", resBody)
}
func IsStatusAndReasonWithRawBody(statusCode int, reason string, resBody *io.ReadCloser) (bool, error) {
byteSlice, err := ioutil.ReadAll(*resBody)
if err != nil {
return false, errgo.Mask(err, errgo.Any)
}
// This is a hack to be able to read from response body twice. Because we
// need to read the response body to identify the actual status of the
// response, we consume the stream maybe somebody else would like too. Thus
// we buffer the response body and write it back to the original response
// body reference.
*resBody = ioutil.NopCloser(bytes.NewReader(byteSlice))
return isStatus(statusCode, reason, string(byteSlice))
}
func IsSuccessResponse(statusCode int) bool {
return statusCode == http.StatusOK
}
func IsFailureResponse(statusCode int) bool {
return statusCode == http.StatusInternalServerError
}
type ServerResponse struct {
StatusCode int `json:"status_code"`
StatusText string `json:"status_text"`
Data interface{} `json:"data"`
}
func ParseData(resBody *io.ReadCloser, v interface{}) error {
byteSlice, err := ioutil.ReadAll(*resBody)
if err != nil {
return errgo.Mask(err)
}
// This is a hack to be able to read from response body twice. Because we
// need to read the response body to identify the actual status of the
// response, we consume the stream maybe somebody else would like too. Thus
// we buffer the response body and write it back to the original response
// body reference.
*resBody = ioutil.NopCloser(bytes.NewReader(byteSlice))
target := ServerResponse{Data: &v}
if err := json.Unmarshal(byteSlice, &target); err != nil {
// In case we receive a response we did not expect and cannot read, we just
// return an error containing the content of the response.
return newUnexpectedContentError(string(byteSlice))
}
return nil
}
//------------------------------------------------------------------------------
// private
func isStatus(statusCode int, reason, responseBody string) (bool, error) {
var responsePayload ResponsePayload
if err := json.Unmarshal([]byte(responseBody), &responsePayload); err != nil {
// In case we receive a response we did not expect and cannot read, we just
// return an error containing the content of the response.
return false, newUnexpectedContentError(responseBody)
}
if responsePayload.StatusCode == statusCode {
if reason == "" {
// We're not looking for a specific reason, so we're done
return true, nil
}
// Match end of status text to ": " + <reason>
if strings.HasSuffix(responsePayload.StatusText, ": "+reason) {
return true, nil
}
}
return false, nil
}
// newUnexpectedContentError creates an error containing the given content as messages, unless that is empty.
// In that case a human readable message is returned.
func newUnexpectedContentError(content string) error {
if content == "" {
return errgo.New("Unexpected empty response")
} else {
return errgo.New(content)
}
}
func newReason(text, reason string) string {
if len(reason) > 0 {
text = text + ": " + reason
}
return text
}
func isJSON(s string) bool {
return isJSONMap(s) || isJSONSlice(s)
}
func isJSONMap(s string) bool {
var js map[string]interface{}
return json.Unmarshal([]byte(s), &js) == nil
}
func isJSONSlice(s string) bool {
var js []interface{}
return json.Unmarshal([]byte(s), &js) == nil
}