-
Notifications
You must be signed in to change notification settings - Fork 2
/
client.go
140 lines (115 loc) · 3.45 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
package appserver
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)
type APIClient struct {
appName string
credentials Credentials
tokenStore *tokenStore
httpClient *http.Client
}
func newAPIClient(httpClient *http.Client, appName string, credentials Credentials, tokenStore *tokenStore) *APIClient {
return &APIClient{
appName: appName,
credentials: credentials,
tokenStore: tokenStore,
httpClient: httpClient,
}
}
func (c *APIClient) Request(ctx context.Context, method string, path string, payload interface{}) (*http.Response, error) {
token, err := c.getTokenForShop(c.credentials.ShopID)
if err != nil {
return nil, fmt.Errorf("get token: %v", err)
}
var pdata []byte
if payload != nil {
pdata, err = json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("encode payload: %v", err)
}
}
req, err := http.NewRequestWithContext(ctx, method, c.credentials.ShopURL+path, bytes.NewReader(pdata))
if err != nil {
return nil, err
}
req.Header.Set("accept", "application/json")
req.Header.Set("content-type", "application/json")
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
return c.httpClient.Do(req)
}
func (c *APIClient) GetAppConfig(ctx context.Context) (map[string]interface{}, error) {
resp, err := c.Request(ctx, http.MethodGet, "/api/_action/system-config?domain="+c.appName+".config", nil)
if err != nil {
return nil, err
}
bodyString, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
resp.Body.Close()
out := map[string]interface{}{}
err = json.Unmarshal(bodyString, &out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *APIClient) getTokenForShop(shopID string) (*oauth2.Token, error) {
if token, ok := c.tokenStore.Get(shopID); ok {
return token, nil
}
cc := clientcredentials.Config{
ClientID: c.credentials.APIKey,
ClientSecret: c.credentials.SecretKey,
TokenURL: c.credentials.ShopURL + "/api/oauth/token",
AuthStyle: oauth2.AuthStyleInParams,
}
token, err := cc.Token(context.Background())
if err != nil {
return nil, err
}
c.tokenStore.Store(shopID, token)
return token, nil
}
const (
TotalCountModeDefault = 0
TotalCountModeExact = 1
TotalCountModeNextPage = 2
SearchFilterTypeEquals = "equals"
SearchFilterTypeEqualsAny = "equalsAny"
SearchSortDirectionAscending = "ASC"
SearchSortDirectionDescending = "DESC"
)
type Search struct {
Includes map[string][]string `json:"includes,omitempty"`
Page int64 `json:"page,omitempty"`
Limit int64 `json:"limit,omitempty"`
IDs []string `json:"ids,omitempty"`
Filter []SearchFilter `json:"filter,omitempty"`
PostFilter []SearchFilter `json:"postFilter,omitempty"`
Sort []SearchSort `json:"sort,omitempty"`
Term string `json:"term,omitempty"`
TotalCountMode int `json:"totalCountMode,omitempty"`
}
type SearchFilter struct {
Type string `json:"type"`
Field string `json:"field"`
Value interface{} `json:"value"`
}
type SearchSort struct {
Direction string `json:"order"`
Field string `json:"field"`
NaturalSorting bool `json:"naturalSorting"`
}
type SearchResponse struct {
Total int64 `json:"total"`
Data interface{} `json:"data"`
Aggregations interface{} `json:"aggregations"`
}