-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathclient.go
168 lines (142 loc) · 3.79 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
// SPDX-FileCopyrightText: © 2020 Etebase Authors
// SPDX-License-Identifier: BSD-3-Clause
package etebase
import (
"bytes"
"errors"
"net/http"
"path"
"strings"
"github.com/etesync/etebase-go/internal/codec"
"github.com/vmihailenco/msgpack/v5"
)
// ClientOptions allow you control specific options of the client.
// Most of the users should use DefaultClientOptions when constructing the
// client.
type ClientOptions struct {
// Host is the Etebase server host.
Host string
// Prefix is a string used as a prefix for requests.
// Possible values are `/partner/your-username` or
// `/developer/your-username` if your are using etebase.com server.
// For local server leave it blank.
Prefix string
// UseSSL specifies is ssl should be used or not.
UseSSL bool
Logger interface {
Logf(format string, v ...interface{})
}
}
func (opts ClientOptions) baseUrl() string {
var schema string
if opts.UseSSL {
schema = "https"
} else {
schema = "http"
}
return schema + "://" + path.Join(opts.Host, opts.Prefix, "api/v1")
}
// DefaultClientOptions will make your client point to the official Etebase
// server in 'developer' mode.
func DeveloperClientOptions(name string) ClientOptions {
return ClientOptions{
Host: "api.etebase.com",
UseSSL: true,
Prefix: path.Join("developer", name),
}
}
func PartnerClientOptions(name string) ClientOptions {
return ClientOptions{
Host: "api.etebase.com",
UseSSL: true,
Prefix: path.Join("partner", name),
}
}
// Client implements the network client to use to interact with the Etebase
// server.
type Client struct {
baseUrl string
token string
opts *ClientOptions
}
// NewClient returns a new client object given a name (your etebase account name),
// and options inside the ClientOptions struct.
func NewClient(opts ClientOptions) *Client {
return &Client{
baseUrl: opts.baseUrl(),
opts: &opts,
}
}
// WithToken returns a client that attaches a `Authorization: Token <token>` to
// any request.
func (c Client) WithToken(token string) *Client {
c.token = token
return &c
}
func (c *Client) url(path string) string {
url := c.baseUrl + path
if !strings.HasSuffix(url, "/") {
url += "/"
}
return url
}
func (c *Client) log(format string, v ...interface{}) {
if l := c.opts.Logger; l != nil {
l.Logf(format, v...)
}
}
// do sends a http Request with the right headers and verifies the status code
// before returning.
// If the status code isn't 200 <= x <= 400 it decodes the response error and
// closes the body.
func (c *Client) do(req *http.Request) (*http.Response, error) {
c.log("%-6s %s\n", req.Method, req.URL.Path)
req.Header.Set("Content-Type", "application/msgpack")
req.Header.Set("Accept", "application/msgpack")
if t := c.token; t != "" {
req.Header.Set("Authorization", "Token "+t)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
code := resp.StatusCode
if code >= 200 && code < 400 {
return resp, nil
}
defer resp.Body.Close()
switch code {
case 404:
return nil, errors.New("not found")
case 500:
return nil, errors.New("internal error")
}
var body ErrorResponse
if err := codec.NewDecoder(resp.Body).Decode(&body); err != nil {
return nil, err
}
return nil, &body
}
// Post posts an encoded value `v` to the server.
// `v` will be encoded using msgpack format.
func (c *Client) Post(path string, v interface{}) (*http.Response, error) {
body, err := msgpack.Marshal(v)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", c.url(path), bytes.NewBuffer(body))
if err != nil {
return nil, err
}
return c.do(req)
}
func (c *Client) Get(path string) (*http.Response, error) {
req, err := http.NewRequest("GET", c.url(path), nil)
if err != nil {
return nil, err
}
return c.do(req)
}
func (c *Client) Host() string {
return c.opts.Host
}