workato-connector-sdk 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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