forked from nimajalali/go-force
-
Notifications
You must be signed in to change notification settings - Fork 1
/
force.go
232 lines (199 loc) · 5.88 KB
/
force.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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// A Go package that provides bindings to the force.com REST API
//
// See http://www.salesforce.com/us/developer/docs/api_rest/
package force
import (
"fmt"
"net/http"
"os"
"regexp"
)
const (
testVersion = "v36.0"
testClientId = "3MVG9A2kN3Bn17hs8MIaQx1voVGy662rXlC37svtmLmt6wO_iik8Hnk3DlcYjKRvzVNGWLFlGRH1ryHwS217h"
testClientSecret = "4165772184959202901"
testUserName = "go-force@jalali.net"
testPassword = "golangrocks3"
testSecurityToken = "kAlicVmti9nWRKRiWG3Zvqtte"
testEnvironment = "production"
)
const (
DefaultAPIVersion = "v58.0"
)
type APIConfig func(*ForceApi)
func WithClient(c *http.Client) APIConfig {
return func(f *ForceApi) {
f.httpClient = c
}
}
func WithOAuth(version, clientId, clientSecret, userName, password, securityToken, environment string) APIConfig {
return func(f *ForceApi) {
f.oauth = &forceOauth{
clientId: clientId,
clientSecret: clientSecret,
userName: userName,
password: password,
securityToken: securityToken,
environment: environment,
}
}
}
var versionCheck = regexp.MustCompile(`v\d+\.\d+`)
func WithApiVersion(v string) APIConfig {
return func(f *ForceApi) {
f.apiVersion = v
}
}
func WithAccessToken(clientId, accessToken, instanceUrl string) APIConfig {
return func(f *ForceApi) {
f.oauth = &forceOauth{
clientId: clientId,
AccessToken: accessToken,
InstanceUrl: instanceUrl,
}
}
}
func WithRefreshToken(clientId, clientSecret, refreshToken string) APIConfig {
return func(f *ForceApi) {
if f.oauth == nil {
f.oauth = &forceOauth{
clientId: clientId,
clientSecret: clientSecret,
refreshToken: refreshToken,
}
return
}
f.oauth.clientId = clientId
f.oauth.clientSecret = clientSecret
f.oauth.refreshToken = refreshToken
}
}
func NewClient(cfg ...APIConfig) (*ForceApi, error) {
f := &ForceApi{
apiResources: make(map[string]string),
apiSObjects: make(map[string]*SObjectMetaData),
apiSObjectDescriptions: make(map[string]*SObjectDescription),
apiVersion: "v58.0",
httpClient: http.DefaultClient,
}
for _, c := range cfg {
c(f)
}
if !versionCheck.MatchString(f.apiVersion) {
return nil, fmt.Errorf("invalid API version '%s' specified", f.apiVersion)
}
if f.oauth == nil {
return nil, fmt.Errorf("missing OAuth config")
}
var oauthInitMethod = f.oauth.Authenticate
if f.oauth.AccessToken != "" {
if f.oauth.refreshToken != "" {
if err := f.RefreshToken(); err != nil {
return nil, fmt.Errorf("failed to refresh token: %w", err)
}
}
oauthInitMethod = f.oauth.Validate
}
// Init oauth
err := oauthInitMethod()
if err != nil {
return nil, fmt.Errorf("failed to initialize oauth: %w", err)
}
// Init Api Resources
err = f.getApiResources()
if err != nil {
return nil, err
}
err = f.getApiSObjects()
if err != nil {
return nil, err
}
return f, nil
}
func Create(version, clientId, clientSecret, userName, password, securityToken,
environment string) (*ForceApi, error) {
return NewClient(
WithOAuth(version, clientId, clientSecret, userName, password, securityToken, environment),
WithClient(http.DefaultClient),
)
}
func CreateWithAccessToken(version, clientId, accessToken, instanceUrl string, httpClient *http.Client) (*ForceApi, error) {
return NewClient(
WithAccessToken(clientId, accessToken, instanceUrl),
WithClient(httpClient),
)
}
// TODO: This likely never has worked because the refresh token passed in forceApi.RefreshToken() is always an empty string?
func CreateWithRefreshToken(version, clientId, accessToken, instanceUrl string) (*ForceApi, error) {
oauth := &forceOauth{
clientId: clientId,
AccessToken: accessToken,
InstanceUrl: instanceUrl,
}
forceApi := &ForceApi{
apiResources: make(map[string]string),
apiSObjects: make(map[string]*SObjectMetaData),
apiSObjectDescriptions: make(map[string]*SObjectDescription),
apiVersion: DefaultAPIVersion,
oauth: oauth,
}
// obtain access token
if err := forceApi.RefreshToken(); err != nil {
return nil, err
}
// We need to check for oath correctness here, since we are not generating the token ourselves.
if err := forceApi.oauth.Validate(); err != nil {
return nil, err
}
// Init Api Resources
err := forceApi.getApiResources()
if err != nil {
return nil, err
}
err = forceApi.getApiSObjects()
if err != nil {
return nil, err
}
return forceApi, nil
}
// Used when running tests.
func createTest() *ForceApi {
forceApi, err := Create(testVersion, testClientId, testClientSecret, testUserName, testPassword, testSecurityToken, testEnvironment)
if err != nil {
fmt.Printf("Unable to create ForceApi for test: %v", err)
os.Exit(1)
}
return forceApi
}
type ForceApiLogger interface {
Printf(format string, v ...interface{})
}
// TraceOn turns on logging for this ForceApi. After this is called, all
// requests, responses, and raw response bodies will be sent to the logger.
// If prefix is a non-empty string, it will be written to the front of all
// logged strings, which can aid in filtering log lines.
//
// Use TraceOn if you want to spy on the ForceApi requests and responses.
//
// Note that the base log.Logger type satisfies ForceApiLogger, but adapters
// can easily be written for other logging packages (e.g., the
// golang-sanctioned glog framework).
func (forceApi *ForceApi) TraceOn(prefix string, logger ForceApiLogger) {
forceApi.logger = logger
if prefix == "" {
forceApi.logPrefix = prefix
} else {
forceApi.logPrefix = fmt.Sprintf("%s ", prefix)
}
}
// TraceOff turns off tracing. It is idempotent.
func (forceApi *ForceApi) TraceOff() {
forceApi.logger = nil
forceApi.logPrefix = ""
}
func (forceApi *ForceApi) trace(name string, value interface{}, format string) {
if forceApi.logger != nil {
logMsg := "%s%s " + format + "\n"
forceApi.logger.Printf(logMsg, forceApi.logPrefix, name, value)
}
}