-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.go
More file actions
131 lines (110 loc) · 3.04 KB
/
client.go
File metadata and controls
131 lines (110 loc) · 3.04 KB
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
/*
* Client API. Some ideas inspired by centurylink_sdk
*/
package clcv1
import (
"net/http/cookiejar"
"net/http/httputil"
"encoding/json"
"io/ioutil"
"net/http"
"reflect"
"bytes"
"time"
"flag"
"log"
"fmt"
"io"
)
const (
BaseURL = "https://api.ctl.io/REST"
)
// Global variables
var g_debug bool /* Command-line debug flag */
func init() {
flag.BoolVar(&g_debug, "d", false, "Produce debug output")
}
// Client wraps http.Client, with logging added
type Client struct {
*http.Client
Log *log.Logger
}
// Return new v1 Client
func NewClient(logger *log.Logger) (*Client, error) {
jar, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
if logger == nil {
log.New(ioutil.Discard, "", log.LstdFlags)
}
return &Client{ &http.Client{ Jar: jar }, logger }, nil
}
// Set the transport timeout for the client
func (c *Client) SetTimeout(timeout time.Duration) {
c.Client.Timeout = timeout
}
// Change the logger
func (c *Client) SetLogger(logger *log.Logger) {
if logger == nil {
panic(fmt.Errorf("Client: logger argument must not be nil."))
}
c.Log = logger
}
// POST a v1 API request to @path relative to BaseURL.
// @reqModel: request model to serialize, or nil
// @resModel: result model to deserialize, must be a pointer to the expected result
// Evaluates the StatusCode of the BaseResponse (embedded) in @inModel and sets @err accordingly.
// If @err == nil, fills in @resModel, else returns error.
func (c *Client) getResponse(path string, reqModel interface{}, resModel interface{}) (err error) {
var reqBody io.Reader
if reqModel != nil {
if g_debug {
c.Log.Printf("reqModel %T %+v\n", reqModel, reqModel)
}
jsonReq, err := json.Marshal(reqModel)
if err != nil {
return fmt.Errorf("Failed to encode request model %T %+v: %s", reqModel, reqModel, err)
}
reqBody = bytes.NewBuffer(jsonReq)
}
/* resModel must be a pointer type (call-by-value) */
if resModel == nil {
return fmt.Errorf("Result model can not be nil")
} else if resType := reflect.TypeOf(resModel); resType.Kind() != reflect.Ptr {
return fmt.Errorf("Expecting pointer to result model %T", resModel)
} else if g_debug {
c.Log.Printf("resModel %T %+v\n", resModel, resModel)
}
req, err := http.NewRequest("POST", BaseURL + path, reqBody)
if err != nil {
return
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("Accept", "application/json")
if g_debug {
reqDump, _ := httputil.DumpRequest(req, true)
c.Log.Printf("%s", reqDump)
}
res, err := c.Do(req)
if err != nil {
return
}
defer res.Body.Close()
if g_debug {
resDump, _ := httputil.DumpResponse(res, true)
c.Log.Printf("%s", resDump)
}
/* StatusCode is used instead of the HTTP status code (which is 200 even if there was an error) */
if res.StatusCode != 200 {
return fmt.Errorf("POST request at %s failed with status: %q", path, res.Status)
}
if err = json.NewDecoder(res.Body).Decode(resModel); err != nil {
return
}
br, err := ExtractBaseResponse(resModel)
if err != nil {
return
}
return br.Evaluate()
}