workato-connector-sdk 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -11
- data/lib/workato/cli/edit_command.rb +4 -3
- data/lib/workato/cli/exec_command.rb +27 -35
- data/lib/workato/cli/generate_command.rb +1 -0
- data/lib/workato/cli/generators/connector_generator.rb +1 -0
- data/lib/workato/cli/generators/master_key_generator.rb +1 -0
- data/lib/workato/cli/main.rb +44 -11
- data/lib/workato/cli/oauth2_command.rb +6 -5
- data/lib/workato/cli/push_command.rb +8 -5
- data/lib/workato/cli/schema_command.rb +6 -7
- data/lib/workato/connector/sdk/account_properties.rb +1 -0
- data/lib/workato/connector/sdk/action.rb +78 -20
- data/lib/workato/connector/sdk/block_invocation_refinements.rb +1 -0
- data/lib/workato/connector/sdk/connection.rb +204 -44
- data/lib/workato/connector/sdk/connector.rb +200 -65
- data/lib/workato/connector/sdk/dsl/account_property.rb +1 -0
- data/lib/workato/connector/sdk/dsl/aws.rb +23 -27
- data/lib/workato/connector/sdk/dsl/call.rb +6 -2
- data/lib/workato/connector/sdk/dsl/error.rb +1 -0
- data/lib/workato/connector/sdk/dsl/http.rb +2 -7
- data/lib/workato/connector/sdk/dsl/lookup_table.rb +1 -0
- data/lib/workato/connector/sdk/dsl/time.rb +6 -0
- data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +38 -0
- data/lib/workato/connector/sdk/dsl/workato_schema.rb +1 -0
- data/lib/workato/connector/sdk/dsl.rb +19 -4
- data/lib/workato/connector/sdk/errors.rb +62 -4
- data/lib/workato/connector/sdk/lookup_tables.rb +1 -0
- data/lib/workato/connector/sdk/object_definitions.rb +22 -17
- data/lib/workato/connector/sdk/operation.rb +127 -88
- data/lib/workato/connector/sdk/request.rb +95 -31
- data/lib/workato/connector/sdk/schema/field/array.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/convertors.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/date.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/date_time.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/integer.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/number.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/object.rb +1 -0
- data/lib/workato/connector/sdk/schema/field/string.rb +1 -0
- data/lib/workato/connector/sdk/schema/type/time.rb +1 -0
- data/lib/workato/connector/sdk/schema/type/unicode_string.rb +1 -0
- data/lib/workato/connector/sdk/schema.rb +1 -0
- data/lib/workato/connector/sdk/settings.rb +9 -4
- data/lib/workato/connector/sdk/summarize.rb +3 -2
- data/lib/workato/connector/sdk/trigger.rb +106 -10
- data/lib/workato/connector/sdk/version.rb +2 -1
- data/lib/workato/connector/sdk/workato_schemas.rb +1 -0
- data/lib/workato/connector/sdk/xml.rb +1 -0
- data/lib/workato/connector/sdk.rb +8 -0
- data/lib/workato/extension/array.rb +1 -0
- data/lib/workato/extension/case_sensitive_headers.rb +1 -0
- data/lib/workato/extension/currency.rb +2 -1
- data/lib/workato/extension/date.rb +1 -0
- data/lib/workato/extension/enumerable.rb +1 -0
- data/lib/workato/extension/extra_chain_cert.rb +1 -0
- data/lib/workato/extension/hash.rb +1 -0
- data/lib/workato/extension/integer.rb +1 -0
- data/lib/workato/extension/nil_class.rb +1 -0
- data/lib/workato/extension/object.rb +1 -0
- data/lib/workato/extension/phone.rb +2 -1
- data/lib/workato/extension/string.rb +6 -2
- data/lib/workato/extension/symbol.rb +1 -0
- data/lib/workato/extension/time.rb +1 -0
- data/lib/workato/testing/vcr_encrypted_cassette_serializer.rb +5 -0
- data/lib/workato/testing/vcr_multipart_body_matcher.rb +1 -0
- data/lib/workato/utilities/encoding.rb +57 -0
- data/lib/workato/web/app.rb +1 -0
- data/lib/workato-connector-sdk.rb +1 -0
- metadata +88 -17
@@ -1,3 +1,4 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require_relative './block_invocation_refinements'
|
@@ -6,95 +7,174 @@ module Workato
|
|
6
7
|
module Connector
|
7
8
|
module Sdk
|
8
9
|
class Connection
|
10
|
+
extend T::Sig
|
11
|
+
|
9
12
|
using BlockInvocationRefinements
|
10
13
|
|
14
|
+
sig { returns(HashWithIndifferentAccess) }
|
11
15
|
attr_reader :source
|
12
16
|
|
17
|
+
cattr_accessor :on_settings_update
|
18
|
+
|
19
|
+
sig do
|
20
|
+
params(
|
21
|
+
connection: SorbetTypes::SourceHash,
|
22
|
+
methods: SorbetTypes::SourceHash,
|
23
|
+
settings: SorbetTypes::SettingsHash
|
24
|
+
).void
|
25
|
+
end
|
13
26
|
def initialize(connection: {}, methods: {}, settings: {})
|
14
|
-
@methods_source = methods.with_indifferent_access
|
15
|
-
@source = connection.with_indifferent_access
|
16
|
-
@settings = settings
|
27
|
+
@methods_source = T.let(methods.with_indifferent_access, HashWithIndifferentAccess)
|
28
|
+
@source = T.let(connection.with_indifferent_access, HashWithIndifferentAccess)
|
29
|
+
@settings = T.let(settings, SorbetTypes::SettingsHash)
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { returns(SorbetTypes::SettingsHash) }
|
33
|
+
def settings!
|
34
|
+
@settings
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { returns(HashWithIndifferentAccess) }
|
38
|
+
def settings
|
39
|
+
# we can't freeze or memoise because some developers modify it for storing something temporary in it.
|
40
|
+
@settings.with_indifferent_access
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { params(settings: SorbetTypes::SettingsHash).returns(SorbetTypes::SettingsHash) }
|
44
|
+
def merge_settings!(settings)
|
45
|
+
@settings.merge!(settings)
|
17
46
|
end
|
18
47
|
|
48
|
+
sig { returns(T::Boolean) }
|
49
|
+
def authorization?
|
50
|
+
source[:authorization].present?
|
51
|
+
end
|
52
|
+
|
53
|
+
sig { returns(Authorization) }
|
19
54
|
def authorization
|
55
|
+
raise ::NotImplementedError, 'define authorization: before use' if source[:authorization].blank?
|
56
|
+
|
57
|
+
@authorization = T.let(@authorization, T.nilable(Authorization))
|
20
58
|
@authorization ||= Authorization.new(
|
21
|
-
connection:
|
22
|
-
|
23
|
-
|
59
|
+
connection: self,
|
60
|
+
authorization: source[:authorization],
|
61
|
+
methods: methods_source
|
24
62
|
)
|
25
63
|
end
|
26
64
|
|
27
|
-
|
28
|
-
|
65
|
+
sig { params(settings: T.nilable(SorbetTypes::SettingsHash)).returns(T.nilable(String)) }
|
66
|
+
def base_uri(settings = nil)
|
67
|
+
source[:base_uri]&.call(settings ? settings.with_indifferent_access.freeze : self.settings)
|
68
|
+
end
|
69
|
+
|
70
|
+
sig do
|
71
|
+
params(
|
72
|
+
message: String,
|
73
|
+
refresher: T.proc.returns(T.nilable(SorbetTypes::SettingsHash))
|
74
|
+
).returns(T::Boolean)
|
75
|
+
end
|
76
|
+
def update_settings!(message, &refresher)
|
77
|
+
updater = lambda do
|
78
|
+
new_settings = refresher.call
|
79
|
+
next unless new_settings
|
80
|
+
|
81
|
+
settings.merge(new_settings)
|
82
|
+
end
|
83
|
+
|
84
|
+
new_settings = if on_settings_update
|
85
|
+
on_settings_update.call(message, &updater)
|
86
|
+
else
|
87
|
+
updater.call
|
88
|
+
end
|
89
|
+
return false unless new_settings
|
90
|
+
|
91
|
+
merge_settings!(new_settings)
|
92
|
+
|
93
|
+
true
|
29
94
|
end
|
30
95
|
|
31
96
|
private
|
32
97
|
|
98
|
+
sig { returns(HashWithIndifferentAccess) }
|
33
99
|
attr_reader :methods_source
|
34
100
|
|
35
101
|
class Authorization
|
36
|
-
|
102
|
+
extend T::Sig
|
37
103
|
|
38
|
-
|
39
|
-
|
40
|
-
@source = (connection[:authorization] || {}).with_indifferent_access
|
41
|
-
@methods_source = methods.with_indifferent_access
|
42
|
-
@settings = settings
|
43
|
-
end
|
44
|
-
|
45
|
-
def token_url?
|
46
|
-
source[:token_url].present?
|
47
|
-
end
|
104
|
+
sig { returns(HashWithIndifferentAccess) }
|
105
|
+
attr_reader :source
|
48
106
|
|
49
|
-
|
50
|
-
|
107
|
+
sig do
|
108
|
+
params(
|
109
|
+
connection: Connection,
|
110
|
+
authorization: HashWithIndifferentAccess,
|
111
|
+
methods: HashWithIndifferentAccess
|
112
|
+
).void
|
51
113
|
end
|
52
|
-
|
53
|
-
|
54
|
-
source
|
114
|
+
def initialize(connection:, authorization:, methods:)
|
115
|
+
@connection = T.let(connection, Connection)
|
116
|
+
@connection_source = T.let(connection.source, HashWithIndifferentAccess)
|
117
|
+
@source = T.let(authorization, HashWithIndifferentAccess)
|
118
|
+
@methods_source = T.let(methods, HashWithIndifferentAccess)
|
55
119
|
end
|
56
120
|
|
121
|
+
sig { returns(String) }
|
57
122
|
def type
|
58
|
-
source[:type]
|
123
|
+
(source[:type].presence || 'none').to_s
|
59
124
|
end
|
60
125
|
|
126
|
+
sig { returns(T::Array[T.any(String, Symbol, Regexp, Integer)]) }
|
61
127
|
def refresh_on
|
62
128
|
Array.wrap(source[:refresh_on]).compact
|
63
129
|
end
|
64
130
|
|
131
|
+
sig { returns(T::Array[T.any(String, Symbol, Regexp, Integer)]) }
|
65
132
|
def detect_on
|
66
133
|
Array.wrap(source[:detect_on]).compact
|
67
134
|
end
|
68
135
|
|
69
|
-
|
136
|
+
sig { params(settings: T.nilable(SorbetTypes::SettingsHash)).returns(T.nilable(String)) }
|
137
|
+
def client_id(settings = nil)
|
70
138
|
client_id = source[:client_id]
|
71
139
|
|
72
140
|
if client_id.is_a?(Proc)
|
73
|
-
|
141
|
+
@connection.merge_settings!(settings) if settings
|
142
|
+
Dsl::WithDsl.execute(@connection, @connection.settings, &client_id)
|
74
143
|
else
|
75
144
|
client_id
|
76
145
|
end
|
77
146
|
end
|
78
147
|
|
79
|
-
|
148
|
+
sig { params(settings: T.nilable(SorbetTypes::SettingsHash)).returns(T.nilable(String)) }
|
149
|
+
def client_secret(settings = nil)
|
80
150
|
client_secret_source = source[:client_secret]
|
81
151
|
|
82
152
|
if client_secret_source.is_a?(Proc)
|
83
|
-
|
153
|
+
@connection.merge_settings!(settings) if settings
|
154
|
+
Dsl::WithDsl.execute(@connection, @connection.settings, &client_secret_source)
|
84
155
|
else
|
85
156
|
client_secret_source
|
86
157
|
end
|
87
158
|
end
|
88
159
|
|
89
|
-
|
90
|
-
|
160
|
+
sig { params(settings: T.nilable(SorbetTypes::SettingsHash)).returns(T.nilable(String)) }
|
161
|
+
def authorization_url(settings = nil)
|
162
|
+
source[:authorization_url]&.call(settings&.with_indifferent_access || @connection.settings)
|
91
163
|
end
|
92
164
|
|
93
|
-
|
94
|
-
|
165
|
+
sig { params(settings: T.nilable(SorbetTypes::SettingsHash)).returns(T.nilable(String)) }
|
166
|
+
def token_url(settings = nil)
|
167
|
+
source[:token_url]&.call(settings&.with_indifferent_access || @connection.settings)
|
95
168
|
end
|
96
169
|
|
97
|
-
|
170
|
+
sig do
|
171
|
+
params(
|
172
|
+
settings: T.nilable(SorbetTypes::SettingsHash),
|
173
|
+
oauth2_code: T.nilable(String),
|
174
|
+
redirect_url: T.nilable(String)
|
175
|
+
).returns(HashWithIndifferentAccess)
|
176
|
+
end
|
177
|
+
def acquire(settings = nil, oauth2_code = nil, redirect_url = nil)
|
98
178
|
acquire_proc = source[:acquire]
|
99
179
|
raise InvalidDefinitionError, "Expect 'acquire' block" unless acquire_proc
|
100
180
|
|
@@ -102,30 +182,63 @@ module Workato
|
|
102
182
|
connection: Connection.new(
|
103
183
|
connection: connection_source.merge(
|
104
184
|
authorization: source.merge(
|
105
|
-
apply: nil
|
185
|
+
apply: nil # only skip apply authorization for re-authorization request
|
106
186
|
)
|
107
187
|
),
|
108
188
|
methods: methods_source,
|
109
|
-
settings: @settings
|
189
|
+
settings: @connection.settings!
|
110
190
|
),
|
111
|
-
methods: methods_source
|
112
|
-
settings: @settings
|
191
|
+
methods: methods_source
|
113
192
|
).execute(settings, { auth_code: oauth2_code, redirect_url: redirect_url }) do |connection, input|
|
114
193
|
instance_exec(connection, input[:auth_code], input[:redirect_url], &acquire_proc)
|
115
194
|
end
|
116
195
|
end
|
117
196
|
|
118
|
-
|
197
|
+
sig do
|
198
|
+
params(
|
199
|
+
http_code: T.nilable(Integer),
|
200
|
+
http_body: T.nilable(String),
|
201
|
+
exception: T.nilable(String)
|
202
|
+
).returns(T::Boolean)
|
203
|
+
end
|
204
|
+
def refresh?(http_code, http_body, exception)
|
205
|
+
return false unless /oauth2/i =~ type || source[:acquire].present?
|
206
|
+
|
207
|
+
refresh_on = self.refresh_on
|
208
|
+
refresh_on.blank? || refresh_on.any? do |pattern|
|
209
|
+
pattern.is_a?(::Integer) && pattern == http_code ||
|
210
|
+
pattern === exception&.to_s ||
|
211
|
+
pattern === http_body
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
sig { params(settings: HashWithIndifferentAccess).returns(T.nilable(HashWithIndifferentAccess)) }
|
216
|
+
def refresh!(settings)
|
217
|
+
if /oauth2/i =~ type
|
218
|
+
refresh_oauth2_token(settings)
|
219
|
+
elsif source[:acquire].present?
|
220
|
+
acquire(settings)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
sig do
|
225
|
+
params(
|
226
|
+
settings: T.nilable(SorbetTypes::SettingsHash),
|
227
|
+
refresh_token: T.nilable(String)
|
228
|
+
).returns(
|
229
|
+
T.any([HashWithIndifferentAccess, T.nilable(String)], HashWithIndifferentAccess)
|
230
|
+
)
|
231
|
+
end
|
232
|
+
def refresh(settings = nil, refresh_token = nil)
|
119
233
|
refresh_proc = source[:refresh]
|
120
234
|
raise InvalidDefinitionError, "Expect 'refresh' block" unless refresh_proc
|
121
235
|
|
122
236
|
Workato::Connector::Sdk::Operation.new(
|
123
237
|
connection: Connection.new(
|
124
238
|
methods: methods_source,
|
125
|
-
settings: @settings
|
239
|
+
settings: @connection.settings!
|
126
240
|
),
|
127
|
-
methods: methods_source
|
128
|
-
settings: @settings
|
241
|
+
methods: methods_source
|
129
242
|
).execute(settings, { refresh_token: refresh_token }) do |connection, input|
|
130
243
|
instance_exec(connection, input[:refresh_token], &refresh_proc)
|
131
244
|
end
|
@@ -133,8 +246,55 @@ module Workato
|
|
133
246
|
|
134
247
|
private
|
135
248
|
|
136
|
-
|
137
|
-
|
249
|
+
sig { returns(HashWithIndifferentAccess) }
|
250
|
+
attr_reader :connection_source
|
251
|
+
|
252
|
+
sig { returns(HashWithIndifferentAccess) }
|
253
|
+
attr_reader :methods_source
|
254
|
+
|
255
|
+
sig { params(settings: HashWithIndifferentAccess).returns(HashWithIndifferentAccess) }
|
256
|
+
def refresh_oauth2_token(settings)
|
257
|
+
if source[:refresh].present?
|
258
|
+
refresh_oauth2_token_using_refresh(settings)
|
259
|
+
elsif source[:token_url].present?
|
260
|
+
refresh_oauth2_token_using_token_url(settings)
|
261
|
+
else
|
262
|
+
raise InvalidDefinitionError, "'refresh' block or 'token_url' is required for refreshing the token"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
sig { params(settings: HashWithIndifferentAccess).returns(HashWithIndifferentAccess) }
|
267
|
+
def refresh_oauth2_token_using_refresh(settings)
|
268
|
+
new_tokens, new_settings = refresh(settings, settings[:refresh_token])
|
269
|
+
new_tokens.with_indifferent_access.merge(new_settings || {})
|
270
|
+
end
|
271
|
+
|
272
|
+
sig { params(settings: HashWithIndifferentAccess).returns(HashWithIndifferentAccess) }
|
273
|
+
def refresh_oauth2_token_using_token_url(settings)
|
274
|
+
if settings[:refresh_token].blank?
|
275
|
+
raise NotImplementedError, 'refresh_token is empty. ' \
|
276
|
+
'Use workato oauth2 command to acquire access_token and refresh_token'
|
277
|
+
end
|
278
|
+
|
279
|
+
response = RestClient::Request.execute(
|
280
|
+
url: token_url(settings),
|
281
|
+
method: :post,
|
282
|
+
payload: {
|
283
|
+
client_id: client_id(settings),
|
284
|
+
client_secret: client_secret(settings),
|
285
|
+
grant_type: :refresh_token,
|
286
|
+
refresh_token: settings[:refresh_token]
|
287
|
+
},
|
288
|
+
headers: {
|
289
|
+
accept: :json
|
290
|
+
}
|
291
|
+
)
|
292
|
+
tokens = JSON.parse(response.body)
|
293
|
+
{
|
294
|
+
access_token: tokens['access_token'],
|
295
|
+
refresh_token: tokens['refresh_token'].presence || settings[:refresh_token]
|
296
|
+
}.with_indifferent_access
|
297
|
+
end
|
138
298
|
end
|
139
299
|
|
140
300
|
private_constant :Authorization
|