workato-connector-sdk 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/lib/workato/cli/edit_command.rb +4 -3
  3. data/lib/workato/cli/exec_command.rb +24 -35
  4. data/lib/workato/cli/generate_command.rb +1 -0
  5. data/lib/workato/cli/generators/connector_generator.rb +1 -0
  6. data/lib/workato/cli/generators/master_key_generator.rb +1 -0
  7. data/lib/workato/cli/main.rb +1 -0
  8. data/lib/workato/cli/oauth2_command.rb +6 -5
  9. data/lib/workato/cli/push_command.rb +5 -4
  10. data/lib/workato/cli/schema_command.rb +4 -3
  11. data/lib/workato/connector/sdk/account_properties.rb +1 -0
  12. data/lib/workato/connector/sdk/action.rb +78 -20
  13. data/lib/workato/connector/sdk/block_invocation_refinements.rb +1 -0
  14. data/lib/workato/connector/sdk/connection.rb +202 -44
  15. data/lib/workato/connector/sdk/connector.rb +200 -65
  16. data/lib/workato/connector/sdk/dsl/account_property.rb +1 -0
  17. data/lib/workato/connector/sdk/dsl/aws.rb +23 -27
  18. data/lib/workato/connector/sdk/dsl/call.rb +1 -0
  19. data/lib/workato/connector/sdk/dsl/error.rb +1 -0
  20. data/lib/workato/connector/sdk/dsl/http.rb +2 -7
  21. data/lib/workato/connector/sdk/dsl/lookup_table.rb +1 -0
  22. data/lib/workato/connector/sdk/dsl/time.rb +6 -0
  23. data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +38 -0
  24. data/lib/workato/connector/sdk/dsl/workato_schema.rb +1 -0
  25. data/lib/workato/connector/sdk/dsl.rb +19 -4
  26. data/lib/workato/connector/sdk/errors.rb +4 -3
  27. data/lib/workato/connector/sdk/lookup_tables.rb +1 -0
  28. data/lib/workato/connector/sdk/object_definitions.rb +7 -8
  29. data/lib/workato/connector/sdk/operation.rb +127 -88
  30. data/lib/workato/connector/sdk/request.rb +45 -17
  31. data/lib/workato/connector/sdk/schema/field/array.rb +1 -0
  32. data/lib/workato/connector/sdk/schema/field/convertors.rb +1 -0
  33. data/lib/workato/connector/sdk/schema/field/date.rb +1 -0
  34. data/lib/workato/connector/sdk/schema/field/date_time.rb +1 -0
  35. data/lib/workato/connector/sdk/schema/field/integer.rb +1 -0
  36. data/lib/workato/connector/sdk/schema/field/number.rb +1 -0
  37. data/lib/workato/connector/sdk/schema/field/object.rb +1 -0
  38. data/lib/workato/connector/sdk/schema/field/string.rb +1 -0
  39. data/lib/workato/connector/sdk/schema/type/time.rb +1 -0
  40. data/lib/workato/connector/sdk/schema/type/unicode_string.rb +1 -0
  41. data/lib/workato/connector/sdk/schema.rb +1 -0
  42. data/lib/workato/connector/sdk/settings.rb +9 -4
  43. data/lib/workato/connector/sdk/summarize.rb +3 -2
  44. data/lib/workato/connector/sdk/trigger.rb +81 -7
  45. data/lib/workato/connector/sdk/version.rb +2 -1
  46. data/lib/workato/connector/sdk/workato_schemas.rb +1 -0
  47. data/lib/workato/connector/sdk/xml.rb +1 -0
  48. data/lib/workato/connector/sdk.rb +4 -0
  49. data/lib/workato/extension/array.rb +1 -0
  50. data/lib/workato/extension/case_sensitive_headers.rb +1 -0
  51. data/lib/workato/extension/currency.rb +1 -0
  52. data/lib/workato/extension/date.rb +1 -0
  53. data/lib/workato/extension/enumerable.rb +1 -0
  54. data/lib/workato/extension/extra_chain_cert.rb +1 -0
  55. data/lib/workato/extension/hash.rb +1 -0
  56. data/lib/workato/extension/integer.rb +1 -0
  57. data/lib/workato/extension/nil_class.rb +1 -0
  58. data/lib/workato/extension/object.rb +1 -0
  59. data/lib/workato/extension/phone.rb +1 -0
  60. data/lib/workato/extension/string.rb +2 -1
  61. data/lib/workato/extension/symbol.rb +1 -0
  62. data/lib/workato/extension/time.rb +1 -0
  63. data/lib/workato/testing/vcr_encrypted_cassette_serializer.rb +5 -0
  64. data/lib/workato/testing/vcr_multipart_body_matcher.rb +1 -0
  65. data/lib/workato/web/app.rb +1 -0
  66. data/lib/workato-connector-sdk.rb +1 -0
  67. metadata +64 -7
@@ -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: source,
22
- methods: methods_source,
23
- settings: @settings
59
+ connection: self,
60
+ authorization: source[:authorization],
61
+ methods: methods_source
24
62
  )
25
63
  end
26
64
 
27
- def base_uri(settings = {})
28
- source[:base_uri]&.call(settings.with_indifferent_access)
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
- attr_reader :source
37
-
38
- def initialize(connection: {}, methods: {}, settings: {})
39
- @connection_source = connection.with_indifferent_access
40
- @source = (connection[:authorization] || {}).with_indifferent_access
41
- @methods_source = methods.with_indifferent_access
42
- @settings = settings
43
- end
102
+ extend T::Sig
44
103
 
45
- def token_url?
46
- source[:token_url].present?
47
- end
104
+ sig { returns(HashWithIndifferentAccess) }
105
+ attr_reader :source
48
106
 
49
- def acquire?
50
- source[:acquire].present?
107
+ sig do
108
+ params(
109
+ connection: Connection,
110
+ authorization: HashWithIndifferentAccess,
111
+ methods: HashWithIndifferentAccess
112
+ ).void
51
113
  end
52
-
53
- def refresh?
54
- source[:refresh].present?
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
- def client_id(settings = {})
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
- Dsl::WithDsl.execute(settings.with_indifferent_access, &client_id)
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
- def client_secret(settings = {})
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
- Dsl::WithDsl.execute(settings.with_indifferent_access, &client_secret_source)
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
- def authorization_url(settings = {})
90
- source[:authorization_url]&.call(settings.with_indifferent_access)
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
- def token_url(settings = {})
94
- source[:token_url]&.call(settings.with_indifferent_access)
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
- def acquire(settings = {}, oauth2_code = nil, redirect_url = nil)
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,61 @@ 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
- def refresh(settings = {}, refresh_token = nil)
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
+ refresh_on = self.refresh_on
206
+ refresh_on.blank? || refresh_on.any? do |pattern|
207
+ pattern.is_a?(::Integer) && pattern == http_code ||
208
+ pattern === exception&.to_s ||
209
+ pattern === http_body
210
+ end
211
+ end
212
+
213
+ sig { params(settings: HashWithIndifferentAccess).returns(T.nilable(HashWithIndifferentAccess)) }
214
+ def refresh!(settings)
215
+ if /oauth2/i =~ type
216
+ refresh_oauth2_token(settings)
217
+ elsif source[:acquire].present?
218
+ acquire(settings)
219
+ end
220
+ end
221
+
222
+ sig do
223
+ params(
224
+ settings: T.nilable(SorbetTypes::SettingsHash),
225
+ refresh_token: T.nilable(String)
226
+ ).returns(
227
+ T.any([HashWithIndifferentAccess, T.nilable(String)], HashWithIndifferentAccess)
228
+ )
229
+ end
230
+ def refresh(settings = nil, refresh_token = nil)
119
231
  refresh_proc = source[:refresh]
120
232
  raise InvalidDefinitionError, "Expect 'refresh' block" unless refresh_proc
121
233
 
122
234
  Workato::Connector::Sdk::Operation.new(
123
235
  connection: Connection.new(
124
236
  methods: methods_source,
125
- settings: @settings
237
+ settings: @connection.settings!
126
238
  ),
127
- methods: methods_source,
128
- settings: @settings
239
+ methods: methods_source
129
240
  ).execute(settings, { refresh_token: refresh_token }) do |connection, input|
130
241
  instance_exec(connection, input[:refresh_token], &refresh_proc)
131
242
  end
@@ -133,8 +244,55 @@ module Workato
133
244
 
134
245
  private
135
246
 
136
- attr_reader :connection_source,
137
- :methods_source
247
+ sig { returns(HashWithIndifferentAccess) }
248
+ attr_reader :connection_source
249
+
250
+ sig { returns(HashWithIndifferentAccess) }
251
+ attr_reader :methods_source
252
+
253
+ sig { params(settings: HashWithIndifferentAccess).returns(HashWithIndifferentAccess) }
254
+ def refresh_oauth2_token(settings)
255
+ if source[:refresh].present?
256
+ refresh_oauth2_token_using_refresh(settings)
257
+ elsif source[:token_url].present?
258
+ refresh_oauth2_token_using_token_url(settings)
259
+ else
260
+ raise InvalidDefinitionError, "'refresh' block or 'token_url' is required for refreshing the token"
261
+ end
262
+ end
263
+
264
+ sig { params(settings: HashWithIndifferentAccess).returns(HashWithIndifferentAccess) }
265
+ def refresh_oauth2_token_using_refresh(settings)
266
+ new_tokens, new_settings = refresh(settings, settings[:refresh_token])
267
+ new_tokens.with_indifferent_access.merge(new_settings || {})
268
+ end
269
+
270
+ sig { params(settings: HashWithIndifferentAccess).returns(HashWithIndifferentAccess) }
271
+ def refresh_oauth2_token_using_token_url(settings)
272
+ if settings[:refresh_token].blank?
273
+ raise NotImplementedError, 'refresh_token is empty. ' \
274
+ 'Use workato oauth2 command to acquire access_token and refresh_token'
275
+ end
276
+
277
+ response = RestClient::Request.execute(
278
+ url: token_url(settings),
279
+ method: :post,
280
+ payload: {
281
+ client_id: client_id(settings),
282
+ client_secret: client_secret(settings),
283
+ grant_type: :refresh_token,
284
+ refresh_token: settings[:refresh_token]
285
+ },
286
+ headers: {
287
+ accept: :json
288
+ }
289
+ )
290
+ tokens = JSON.parse(response.body)
291
+ {
292
+ access_token: tokens['access_token'],
293
+ refresh_token: tokens['refresh_token'].presence || settings[:refresh_token]
294
+ }.with_indifferent_access
295
+ end
138
296
  end
139
297
 
140
298
  private_constant :Authorization