-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.go
182 lines (161 loc) · 5.04 KB
/
client.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//Package rest implements Equinix REST client
package rest
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"github.com/equinix/rest-go/internal/api"
"github.com/go-resty/resty/v2"
)
const (
//LogLevelEnvVar is OS variable name that controlls logging level
LogLevelEnvVar = "EQUINIX_REST_LOG"
)
//Client describes Equinix REST client implementation.
//Implementation is based on github.com/go-resty
type Client struct {
//PageSize determines default page size for GET requests on resource collections
PageSize int
baseURL string
ctx context.Context
*resty.Client
}
//Error describes REST API error
type Error struct {
//HTTPCode is HTTP status code
HTTPCode int
//Message is textual, general description of an error
Message string
//ApplicationErrors is list of one or more application sub-errors
ApplicationErrors []ApplicationError
}
//ApplicationError describes standardized application error
type ApplicationError struct {
//Code is short error identifier
Code string
//Message is textual description of an error
Message string
//Property is a name of resource property that is related to an error
Property string
//AdditionalInfo provides additional information about an error
AdditionalInfo string
}
func (e Error) Error() string {
var errorStr = fmt.Sprintf("Equinix REST API error: Message: %q", e.Message)
if e.HTTPCode > 0 {
errorStr = fmt.Sprintf("%s, HTTPCode: %d", errorStr, e.HTTPCode)
}
var appErrorsStr string
for _, appError := range e.ApplicationErrors {
appErrorsStr += "[" + appError.Error() + "] "
}
if len(appErrorsStr) > 0 {
errorStr += ", ApplicationErrors: " + appErrorsStr
}
return errorStr
}
func (e ApplicationError) Error() string {
return fmt.Sprintf("Code: %q, Property: %q, Message: %q, AdditionalInfo: %q", e.Code, e.Property, e.Message, e.AdditionalInfo)
}
//NewClient creates new Equinix REST client with a given HTTP context, URL and http client.
//Equinix REST client is based on github.com/go-resty
func NewClient(ctx context.Context, baseURL string, httpClient *http.Client) *Client {
resty := resty.NewWithClient(httpClient)
resty.SetHeader("Accept", "application/json")
resty.SetDebug(isDebugEnabled(osEnvProvider{}))
return &Client{
100,
baseURL,
ctx,
resty}
}
//SetPageSize sets page size used by Equinix REST client for paginated queries
func (c *Client) SetPageSize(pageSize int) *Client {
c.PageSize = pageSize
return c
}
//Execute runs provided request using provider http method and path
func (c *Client) Execute(req *resty.Request, method string, path string) error {
_, err := c.Do(method, path, req)
return err
}
//Do runs given method on a given path with given request and returns response and error
func (c *Client) Do(method string, path string, req *resty.Request) (*resty.Response, error) {
if path[0:1] == "/" {
path = path[1:]
}
url := c.baseURL + "/" + path
resp, err := req.SetContext(c.ctx).Execute(method, url)
if err != nil {
restErr := Error{Message: "HTTP operation failed: " + err.Error()}
if resp != nil {
restErr.HTTPCode = resp.StatusCode()
}
return resp, restErr
}
if resp.IsError() {
return resp, createError(resp)
}
return resp, nil
}
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Unexported package methods
//_______________________________________________________________________
func mapErrorBodyAPIToDomain(body []byte) ([]ApplicationError, bool) {
apiError := api.ErrorResponse{}
if err := json.Unmarshal(body, &apiError); err == nil {
return mapApplicationErrorsAPIToDomain([]api.ErrorResponse{apiError}), true
}
apiErrors := api.ErrorResponses{}
if err := json.Unmarshal(body, &apiErrors); err == nil {
return mapApplicationErrorsAPIToDomain(apiErrors), true
}
return nil, false
}
func mapApplicationErrorsAPIToDomain(apiErrors api.ErrorResponses) []ApplicationError {
transformed := make([]ApplicationError, len(apiErrors))
for i := range apiErrors {
transformed[i] = mapApplicationErrorAPIToDomain(apiErrors[i])
}
return transformed
}
func mapApplicationErrorAPIToDomain(apiError api.ErrorResponse) ApplicationError {
return ApplicationError{
Code: apiError.ErrorCode,
Property: apiError.Property,
Message: apiError.ErrorMessage,
AdditionalInfo: apiError.MoreInfo,
}
}
func createError(resp *resty.Response) Error {
respBody := resp.Body()
err := Error{}
err.HTTPCode = resp.StatusCode()
err.Message = http.StatusText(err.HTTPCode)
appErrors, ok := mapErrorBodyAPIToDomain(respBody)
if !ok {
err.Message = string(respBody)
return err
}
err.ApplicationErrors = appErrors
return err
}
type envProvider interface {
getEnv(key string) string
}
type osEnvProvider struct {
}
func (osEnvProvider) getEnv(key string) string {
return os.Getenv(key)
}
func isDebugEnabled(envProvider envProvider) bool {
envLevel := envProvider.getEnv(LogLevelEnvVar)
switch envLevel {
case "DEBUG":
return true
default:
return false
}
}