forked from a8m/documentdb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
139 lines (124 loc) · 3.3 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
package documentdb
import (
"encoding/json"
"strings"
"net/http"
"bytes"
"io"
"fmt"
)
type Clienter interface {
Read(link string, ret interface{}) error
Delete(link string) error
Query(link string, query string, ret interface{}) error
Create(link string, body, ret interface{}) error
Replace(link string, body, ret interface{}) error
Execute(link string, body, ret interface{}) error
}
type Client struct {
Url string
Config Config
http.Client
}
// Read resource by self link
func (c *Client) Read(link string, ret interface{}) error {
return c.method("GET", link, http.StatusOK, ret, &bytes.Buffer{})
}
// Delete resource by self link
func (c *Client) Delete(link string) error {
return c.method("DELETE", link, http.StatusNoContent, nil, &bytes.Buffer{})
}
// Query resource
func (c *Client) Query(link, query string, ret interface{}) error {
buf := bytes.NewBufferString(querify(query))
req, err := http.NewRequest("POST", path(c.Url, link), buf)
if err != nil {
return err
}
r := ResourceRequest(link, req)
if err = r.DefaultHeaders(c.Config.MasterKey); err != nil {
return err
}
r.QueryHeaders(buf.Len())
return c.do(r, http.StatusOK, ret)
}
// Create resource
func (c *Client) Create(link string, body, ret interface{}) error {
data, err := stringify(body)
if err != nil {
return err
}
buf := bytes.NewBuffer(data)
return c.method("POST", link, http.StatusCreated, ret, buf)
}
// Replace resource
func (c *Client) Replace(link string, body, ret interface{}) error {
data, err := stringify(body)
if err != nil {
return err
}
buf := bytes.NewBuffer(data)
return c.method("PUT", link, http.StatusOK, ret, buf)
}
// Replace resource
// TODO: DRY, move to methods instead of actions(POST, PUT, ...)
func (c *Client) Execute(link string, body, ret interface{}) error {
data, err := stringify(body)
if err != nil {
return err
}
buf := bytes.NewBuffer(data)
return c.method("POST", link, http.StatusOK, ret, buf)
}
// Private generic method resource
func (c *Client) method(method, link string, status int, ret interface{}, body *bytes.Buffer) (err error) {
req, err := http.NewRequest(method, path(c.Url, link), body)
if err != nil {
return err
}
r := ResourceRequest(link, req)
if err = r.DefaultHeaders(c.Config.MasterKey); err != nil {
return err
}
return c.do(r, status, ret)
}
// Private Do function, DRY
func (c *Client) do(r *Request, status int, data interface{}) error {
resp, err := c.Do(r.Request)
if err != nil {
return err
}
if resp.StatusCode != status {
err = &RequestError{}
readJson(resp.Body, &err)
return err
}
defer resp.Body.Close()
if data == nil {
return nil
}
return readJson(resp.Body, data)
}
// Generate link
func path(url string, args ...string) (link string) {
args = append([]string{url}, args...)
link = strings.Join(args, "/")
return
}
// Read json response to given interface(struct, map, ..)
func readJson(reader io.Reader, data interface{}) error {
return json.NewDecoder(reader).Decode(&data)
}
// Stringify query-string as documentdb expected
func querify(query string) string {
return fmt.Sprintf(`{ "%s": "%s" }`, "query", query)
}
// Stringify body data
func stringify(body interface{}) (bt []byte, err error) {
switch t := body.(type) {
case string: bt = []byte(t)
case []byte: bt = t
default: bt, err = json.Marshal(t)
}
return
}