forked from hybrik/hybrik-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 1
/
client.go
218 lines (180 loc) · 4.86 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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package hybrik
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
"sync"
"time"
)
// APIInterface is interface for the underlying client object
type APIInterface interface {
connect() error
isExpired() bool
CallAPI(method string, apiPath string, params url.Values, body io.Reader) (string, error)
}
// ClientInterface is an interface for commonly used API calls
type ClientInterface interface {
// Generic
CallAPI(method string, apiPath string, params url.Values, body io.Reader) (string, error)
// Jobs API
QueueJob(string) (string, error)
GetJobInfo(string) (JobInfo, error)
GetJobResult(string) (JobResultResponse, error)
StopJob(string) error
// Presets API
GetPreset(string) (Preset, error)
CreatePreset(Preset) (Preset, error)
DeletePreset(string) error
}
// Client implements the ClientInterface
type Client struct {
client APIInterface
}
// Config represents the configuration params that are necessary for the API calls to Hybrik to work
type Config struct {
URL string
ComplianceDate string
OAPIKey string
OAPISecret string
AuthKey string
AuthSecret string
OAPIURL string
}
// API is the implementation of the HybrikAPI methods
type API struct {
Config Config
token string
expiration string
}
type connectResponse struct {
Token string `json:"token"`
ExpirationTime string `json:"expiration_time"`
}
// NewClient creates an instance of the HybrikAPI client
func NewClient(config Config) (*Client, error) {
switch {
case config.URL == "":
return &Client{}, ErrNoAPIURL
case config.OAPIKey == "":
return &Client{}, ErrNoOAPIKey
case config.OAPISecret == "":
return &Client{}, ErrNoOAPISecret
case config.AuthKey == "":
return &Client{}, ErrNoAuthKey
case config.AuthSecret == "":
return &Client{}, ErrNoAuthSecret
case config.ComplianceDate == "":
return &Client{}, ErrNoComplianceDate
case !regexp.MustCompile(`^\d{8}$`).MatchString(config.ComplianceDate):
return &Client{}, ErrNoComplianceDate
}
_, err := url.ParseRequestURI(config.URL)
if err != nil {
return &Client{}, ErrInvalidURL
}
parts := strings.Split(config.URL, "//")
if len(parts) < 2 {
return &Client{}, ErrInvalidURL
}
config.OAPIURL = fmt.Sprintf("%s//%s:%s@%s",
parts[0], config.OAPIKey, config.OAPISecret, parts[1],
)
return &Client{
client: &API{
Config: config,
},
}, nil
}
func (a *API) connect() error {
data := make(map[string]string)
data["auth_key"] = a.Config.AuthKey
data["auth_secret"] = a.Config.AuthSecret
jsonBody, err := json.Marshal(data)
if err != nil {
return err
}
req, err := http.NewRequest("POST", fmt.Sprintf("%s/login", a.Config.OAPIURL), bytes.NewReader(jsonBody))
if err != nil {
return err
}
req.Header.Set("X-Hybrik-Compliance", a.Config.ComplianceDate)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
callResp, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("Login failed w/ %d : %s", resp.StatusCode, string(callResp))
}
var cr connectResponse
err = json.Unmarshal(callResp, &cr)
if err != nil {
return err
}
a.token = cr.Token
a.expiration = cr.ExpirationTime
return nil
}
func (a *API) isExpired() bool {
if a.expiration == "" {
return true
}
t, err := time.Parse(`2006-01-02T15:04:05.999Z`, a.expiration)
if err != nil {
return true
}
return time.Now().After(t)
}
var connectLock sync.Mutex
// CallAPI is the general method to call for access to the API
func (a *API) CallAPI(method string, apiPath string, params url.Values, body io.Reader) (string, error) {
// Retrieves the 'token' and 'expiration_time'
connectLock.Lock()
if a.isExpired() {
err := a.connect()
if err != nil {
connectLock.Unlock()
return "", err
}
}
connectLock.Unlock()
// Does the necessary http call here
req, err := http.NewRequest(method, fmt.Sprintf("%s%s", a.Config.OAPIURL, apiPath), body)
if err != nil {
return "", err
}
req.Header.Set("X-Hybrik-Compliance", a.Config.ComplianceDate)
req.Header.Set("X-Hybrik-Sapiauth", a.token)
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
req.URL.RawQuery = params.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
callResp, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("%d - %s %s: %s", resp.StatusCode, method, apiPath, string(callResp))
}
return string(callResp), nil
}
// CallAPI can be used to make any GET/POST/PUT/DELETE API call to the Hybrik service
func (c *Client) CallAPI(method string, apiPath string, params url.Values, body io.Reader) (string, error) {
return c.client.CallAPI(method, apiPath, params, body)
}