-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.go
160 lines (132 loc) · 4.16 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
/*
* Copyright © 2022-2023 Durudex
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
package polybase
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
)
const (
// The ClientHeaderKey constant stores the value of the client key for
// an HTTP header.
ClientHeaderKey = "X-Polybase-Client"
// The clientHeaderPrefix constant stores the value of the client prefix
// for an HTTP header. This value is added to the beginning of the client
// name specified in the configuration.
//
// Example: "durudex/go-polybase:default-name"
clientHeaderPrefix = "durudex/go-polybase:"
)
// Client interface stores methods for interaction with Polybase Node.
type Client interface {
// MakeRequest method makes a request with the specified settings and
// decodes the JSON response.
MakeRequest(ctx context.Context, req *Request, resp any) error
Config() *Config
}
// Request structure stores data what used for creating a new HTTP request.
type Request struct {
Endpoint string
Method string
Body Body
}
// Body structure stores data what used in client POST HTTP requests.
type Body struct {
Args []any `json:"args"`
}
// RecordResponse structure stores the Polybase record response.
type RecordResponse[T any] struct {
Block Block `json:"block"`
Data T `json:"data"`
}
// Response structure stores data when returned from single response.
type Response[T any] struct {
RecordResponse[T]
Error Error `json:"error,omitempty"`
}
// ResponseList structure stores data when returned from multiple responses.
type ResponseList[T any] struct {
Data []RecordResponse[T] `json:"data,omitempty"`
Cursor Cursor `json:"cursor,omitempty"`
Error Error `json:"error,omitempty"`
}
// client structure implements all methods of the Client interface.
type client struct {
cfg *Config
doer *http.Client
}
// To start using the go-polybase client, you need to crete a new client
// instance. This can be done using the internal New() function, which
// returns a new instance with either a specified configuration or the
// default configuration.
//
// To create an instance with a specified configuration, you need to pass
// a pointer of Config value as an argument to the New(...) function. This
// can be useful if you want to use specific settings, for example, if you
// have your own configuration file.
//
// client := polybase.New(&polybase.Config{
// ...
// }
//
// If you want to use the default configuration, you can simply call the New()
// function without any arguments. The client will be created with the default
// configuration set in the go-polybase module.
//
// client := polybase.New()
func New(configs ...*Config) Client {
var cfg *Config
if configs != nil {
cfg = configs[0]
} else {
cfg = new(Config)
}
cfg.configure()
return &client{cfg: cfg, doer: http.DefaultClient}
}
// MakeRequest method makes a request with the specified settings and decodes
// the JSON response.
func (c *client) MakeRequest(ctx context.Context, req *Request, resp any) error {
// Creating a new HTTP request.
rwc, err := c.newRequest(ctx, req)
if err != nil {
return err
}
// Sending an HTTP request and returns a HTTP response.
re, err := c.doer.Do(rwc)
if err != nil {
return err
}
defer re.Body.Close()
// Decoding the JSON returned response.
return json.NewDecoder(re.Body).Decode(resp)
}
// newRequest method returns a new HTTP request by client Request and configuration.
func (c *client) newRequest(ctx context.Context, req *Request) (*http.Request, error) {
var body io.Reader
// Creating a new body reader when body arguments not empty.
if req.Body.Args != nil {
b, err := json.Marshal(req.Body)
if err != nil {
return nil, err
}
body = bytes.NewReader(b)
}
url := c.cfg.URL + req.Endpoint
// Creating a new HTTP request with user context.
rwc, err := http.NewRequestWithContext(ctx, req.Method, url, body)
if err != nil {
return nil, err
}
name := clientHeaderPrefix + c.cfg.Name
// Addition HTTP headers for the request.
rwc.Header.Add(ClientHeaderKey, name)
return rwc, nil
}
func (c *client) Config() *Config { return c.cfg }