workato-connector-sdk 1.0.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -0
  3. data/lib/workato/cli/exec_command.rb +31 -8
  4. data/lib/workato/cli/multi_auth_selected_fallback.rb +33 -0
  5. data/lib/workato/cli/oauth2_command.rb +50 -12
  6. data/lib/workato/connector/sdk/connection.rb +84 -14
  7. data/lib/workato/connector/sdk/connector.rb +6 -4
  8. data/lib/workato/connector/sdk/dsl/aws.rb +5 -2
  9. data/lib/workato/connector/sdk/dsl/call.rb +5 -2
  10. data/lib/workato/connector/sdk/dsl/csv.rb +125 -0
  11. data/lib/workato/connector/sdk/dsl/execution_context.rb +44 -0
  12. data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +8 -1
  13. data/lib/workato/connector/sdk/dsl.rb +1 -0
  14. data/lib/workato/connector/sdk/errors.rb +69 -1
  15. data/lib/workato/connector/sdk/lookup_tables.rb +3 -1
  16. data/lib/workato/connector/sdk/object_definitions.rb +16 -10
  17. data/lib/workato/connector/sdk/operation.rb +24 -8
  18. data/lib/workato/connector/sdk/request.rb +136 -58
  19. data/lib/workato/connector/sdk/schema.rb +5 -3
  20. data/lib/workato/connector/sdk/settings.rb +4 -2
  21. data/lib/workato/connector/sdk/trigger.rb +42 -19
  22. data/lib/workato/connector/sdk/version.rb +1 -1
  23. data/lib/workato/connector/sdk.rb +3 -2
  24. data/lib/workato/extension/case_sensitive_headers.rb +0 -25
  25. data/lib/workato/extension/content_encoding_decoder.rb +67 -0
  26. data/lib/workato/extension/currency.rb +1 -1
  27. data/lib/workato/extension/extra_chain_cert.rb +0 -14
  28. data/lib/workato/extension/hash_with_indifferent_access.rb +19 -0
  29. data/lib/workato/extension/phone.rb +1 -1
  30. data/lib/workato/extension/string.rb +6 -2
  31. data/lib/workato/utilities/encoding.rb +57 -0
  32. data/lib/workato/{connector/sdk → utilities}/xml.rb +4 -4
  33. metadata +48 -22
@@ -0,0 +1,44 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'securerandom'
5
+
6
+ module Workato
7
+ module Connector
8
+ module Sdk
9
+ module Dsl
10
+ module ExecutionContext
11
+ extend T::Sig
12
+ extend T::Helpers
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ T.bind(self, Class)
17
+
18
+ # encrypted safe recipe_id
19
+ class_attribute :recipe_id, instance_predicate: false, default: SecureRandom.hex(32)
20
+ end
21
+
22
+ sig { returns(T::Hash[Symbol, T.untyped]) }
23
+ def execution_context
24
+ @execution_context ||= {
25
+ recipe_id: recipe_id
26
+ }.compact
27
+ end
28
+
29
+ # mock unencrypted recipe_id for testing only
30
+ def recipe_id!
31
+ recipe_id.reverse
32
+ end
33
+
34
+ module ClassMethods
35
+ # mock unencrypted recipe_id for testing only
36
+ def recipe_id!
37
+ T.unsafe(self).recipe_id.reverse
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -2,6 +2,9 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'jwt'
5
+ require_relative './csv'
6
+
7
+ using Workato::Extension::HashWithIndifferentAccess
5
8
 
6
9
  module Workato
7
10
  module Connector
@@ -74,7 +77,7 @@ module Workato
74
77
  raise "A RSA key of size #{JWT_RSA_KEY_MIN_LENGTH} bits or larger MUST be used with JWT."
75
78
  end
76
79
 
77
- header_fields = header_fields.present? ? header_fields.with_indifferent_access.except(:typ, :alg) : {}
80
+ header_fields = HashWithIndifferentAccess.wrap(header_fields).except(:typ, :alg)
78
81
  ::JWT.encode(payload, rsa_private, algorithm, header_fields)
79
82
  end
80
83
 
@@ -152,6 +155,10 @@ module Workato
152
155
  def pbkdf2_hmac_sha1(string, salt, iterations = 1000, key_len = 16)
153
156
  Extension::Binary.new(::OpenSSL::PKCS5.pbkdf2_hmac_sha1(string, salt, iterations, key_len))
154
157
  end
158
+
159
+ def csv
160
+ Csv
161
+ end
155
162
  end
156
163
  end
157
164
  end
@@ -12,6 +12,7 @@ require_relative './dsl/workato_code_lib'
12
12
  require_relative './dsl/workato_schema'
13
13
  require_relative './dsl/time'
14
14
  require_relative './dsl/aws'
15
+ require_relative './dsl/execution_context'
15
16
 
16
17
  module Workato
17
18
  module Connector
@@ -6,12 +6,62 @@ module Workato
6
6
  module Sdk
7
7
  InvalidDefinitionError = Class.new(StandardError)
8
8
 
9
- InvalidSchemaError = Class.new(StandardError)
9
+ class UnexpectedMethodDefinitionError < InvalidDefinitionError
10
+ attr_reader :name
11
+ attr_reader :definition
12
+
13
+ def initialize(name, definition)
14
+ super("Expected lambda for method '#{name}' definition, got: #{definition.class.name}")
15
+ @name = name
16
+ @definition = definition
17
+ end
18
+ end
19
+
20
+ class UndefinedMethodError < InvalidDefinitionError
21
+ attr_reader :name
22
+
23
+ def initialize(name)
24
+ super("Method '#{name}' does not exists")
25
+ @name = name
26
+ end
27
+ end
28
+
29
+ InvalidSchemaError = Class.new(InvalidDefinitionError)
10
30
 
11
31
  CustomRequestError = Class.new(StandardError)
12
32
 
33
+ InvalidMultiAuthDefinition = Class.new(InvalidDefinitionError)
34
+
35
+ class UnresolvedMultiAuthOptionError < InvalidMultiAuthDefinition
36
+ attr_reader :name
37
+
38
+ def initialize(name)
39
+ super("Cannot find multi-auth definition for '#{name}'")
40
+ @name = name
41
+ end
42
+ end
43
+
13
44
  RuntimeError = Class.new(StandardError)
14
45
 
46
+ class UnresolvedObjectDefinitionError < StandardError
47
+ attr_reader :name
48
+
49
+ def initialize(name)
50
+ super("Cannot find object definition for '#{name}'")
51
+ @name = name
52
+ end
53
+ end
54
+
55
+ class CircleReferenceObjectDefinitionError < StandardError
56
+ attr_reader :name
57
+
58
+ def initialize(name, backtrace = [])
59
+ super("Infinite recursion occurred in object definition for '#{name}'")
60
+ set_backtrace(backtrace)
61
+ @name = name
62
+ end
63
+ end
64
+
15
65
  class RequestError < StandardError
16
66
  attr_reader :method
17
67
  attr_reader :code
@@ -41,6 +91,24 @@ module Workato
41
91
  super(message)
42
92
  end
43
93
  end
94
+
95
+ RequestTLSCertificateFormatError = Class.new(StandardError)
96
+
97
+ RequestPayloadFormatError = Class.new(StandardError)
98
+
99
+ JSONRequestFormatError = Class.new(RequestPayloadFormatError)
100
+
101
+ JSONResponseFormatError = Class.new(RequestPayloadFormatError)
102
+
103
+ XMLRequestFormatError = Class.new(RequestPayloadFormatError)
104
+
105
+ XMLResponseFormatError = Class.new(RequestPayloadFormatError)
106
+
107
+ WWWFormURLEncodedRequestFormatError = Class.new(RequestPayloadFormatError)
108
+
109
+ MultipartFormRequestFormatError = Class.new(RequestPayloadFormatError)
110
+
111
+ RAWResponseFormatError = Class.new(RequestPayloadFormatError)
44
112
  end
45
113
  end
46
114
  end
@@ -4,6 +4,8 @@
4
4
  require 'csv'
5
5
  require 'singleton'
6
6
 
7
+ using Workato::Extension::HashWithIndifferentAccess
8
+
7
9
  module Workato
8
10
  module Connector
9
11
  module Sdk
@@ -40,7 +42,7 @@ module Workato
40
42
  @table_by_id ||= {}
41
43
  @table_by_name ||= {}
42
44
  data.each do |name, table|
43
- table = table.with_indifferent_access
45
+ table = HashWithIndifferentAccess.wrap(table)
44
46
  rows = table['rows'].freeze
45
47
  @table_by_id[table['id'].to_i] = rows
46
48
  @table_by_name[name] = rows
@@ -18,16 +18,22 @@ module Workato
18
18
 
19
19
  def lazy(settings = nil, config_fields = {})
20
20
  DupHashWithIndifferentAccess.new do |object_definitions, name|
21
- fields_proc = object_definitions_source[name][:fields]
22
- object_definitions[name] = Action.new(
23
- action: {
24
- execute: lambda do |connection, input|
25
- instance_exec(connection, input, object_definitions, &fields_proc)
26
- end
27
- },
28
- methods: methods_source,
29
- connection: connection
30
- ).execute(settings, config_fields)
21
+ fields_proc = object_definitions_source.dig(name, :fields)
22
+ raise Workato::Connector::Sdk::UnresolvedObjectDefinitionError, name unless fields_proc
23
+
24
+ begin
25
+ object_definitions[name] = Action.new(
26
+ action: {
27
+ execute: lambda do |connection, input|
28
+ instance_exec(connection, input, object_definitions, &fields_proc)
29
+ end
30
+ },
31
+ methods: methods_source,
32
+ connection: connection
33
+ ).execute(settings, config_fields)
34
+ rescue SystemStackError => e
35
+ raise Workato::Connector::Sdk::CircleReferenceObjectDefinitionError.new(name, e.backtrace)
36
+ end
31
37
  end
32
38
  end
33
39
 
@@ -5,6 +5,8 @@ require_relative './dsl'
5
5
  require_relative './block_invocation_refinements'
6
6
  require_relative './schema'
7
7
 
8
+ using Workato::Extension::HashWithIndifferentAccess
9
+
8
10
  module Workato
9
11
  module Connector
10
12
  module Sdk
@@ -45,6 +47,7 @@ module Workato
45
47
  include Dsl::HTTP
46
48
  include Dsl::Call
47
49
  include Dsl::Error
50
+ include Dsl::ExecutionContext
48
51
 
49
52
  using BlockInvocationRefinements
50
53
 
@@ -57,8 +60,8 @@ module Workato
57
60
  ).void
58
61
  end
59
62
  def initialize(operation: {}, methods: {}, connection: Connection.new, object_definitions: nil)
60
- @operation = T.let(operation.with_indifferent_access, HashWithIndifferentAccess)
61
- @_methods = T.let(methods.with_indifferent_access, HashWithIndifferentAccess)
63
+ @operation = T.let(HashWithIndifferentAccess.wrap(operation), HashWithIndifferentAccess)
64
+ @_methods = T.let(HashWithIndifferentAccess.wrap(methods), HashWithIndifferentAccess)
62
65
  @connection = T.let(connection, Connection)
63
66
  @object_definitions = T.let(object_definitions, T.nilable(ObjectDefinitions))
64
67
  end
@@ -80,13 +83,14 @@ module Workato
80
83
  connection.merge_settings!(settings) if settings
81
84
  request_or_result = T.unsafe(self).instance_exec(
82
85
  connection.settings,
83
- input.with_indifferent_access,
84
- Array.wrap(extended_input_schema).map(&:with_indifferent_access),
85
- Array.wrap(extended_output_schema).map(&:with_indifferent_access),
86
- continue.with_indifferent_access,
86
+ HashWithIndifferentAccess.wrap(input),
87
+ Array.wrap(extended_input_schema).map { |i| HashWithIndifferentAccess.wrap(i) },
88
+ Array.wrap(extended_output_schema).map { |i| HashWithIndifferentAccess.wrap(i) },
89
+ HashWithIndifferentAccess.wrap(continue),
87
90
  &block
88
91
  )
89
- resolve_request(request_or_result)
92
+ result = resolve_request(request_or_result)
93
+ try_convert_to_hash_with_indifferent_access(result)
90
94
  end
91
95
 
92
96
  sig do
@@ -225,7 +229,7 @@ module Workato
225
229
  end
226
230
  end
227
231
  when ::Hash
228
- request_or_result.inject(request_or_result.with_indifferent_access) do |acc, (key, value)|
232
+ request_or_result.inject(request_or_result) do |acc, (key, value)|
229
233
  response_value = resolve_request(value)
230
234
  if response_value.equal?(value)
231
235
  acc
@@ -238,6 +242,18 @@ module Workato
238
242
  end
239
243
  end
240
244
 
245
+ sig { params(value: T.untyped).returns(T.untyped) }
246
+ def try_convert_to_hash_with_indifferent_access(value)
247
+ case value
248
+ when ::Hash
249
+ HashWithIndifferentAccess.wrap(value)
250
+ when ::Array
251
+ value.map! { |i| try_convert_to_hash_with_indifferent_access(i) }
252
+ else
253
+ value
254
+ end
255
+ end
256
+
241
257
  sig { returns(ObjectDefinitions) }
242
258
  def object_definitions
243
259
  T.must(@object_definitions)
@@ -8,8 +8,12 @@ require 'gyoku'
8
8
  require 'net/http'
9
9
  require 'net/http/digest_auth'
10
10
 
11
+ require 'workato/utilities/encoding'
12
+ require 'workato/utilities/xml'
11
13
  require_relative './block_invocation_refinements'
12
14
 
15
+ using Workato::Extension::HashWithIndifferentAccess
16
+
13
17
  module Workato
14
18
  module Connector
15
19
  module Sdk
@@ -27,10 +31,10 @@ module Workato
27
31
  @action = action
28
32
  @headers = {}
29
33
  @case_sensitive_headers = {}
30
- @params = {}.with_indifferent_access
31
34
  @render_request = ->(payload) { payload }
32
35
  @parse_response = ->(payload) { payload }
33
36
  @after_response = ->(_response_code, parsed_response, _response_headers) { parsed_response }
37
+ @callstack_before_request = Array.wrap(Kernel.caller)
34
38
  end
35
39
 
36
40
  def method_missing(*args, &block)
@@ -38,36 +42,24 @@ module Workato
38
42
  end
39
43
 
40
44
  def execute!
41
- __getobj__ || __setobj__(
42
- authorized do
43
- begin
44
- request = build_request
45
- response = execute(request)
46
- rescue RestClient::Unauthorized => e
47
- Kernel.raise e unless @digest_auth
48
-
49
- @digest_auth = false
50
- headers('Authorization' => Net::HTTP::DigestAuth.new.auth_header(
51
- URI.parse(build_url),
52
- e.response.headers[:www_authenticate],
53
- method.to_s.upcase
54
- ))
55
- request = build_request
56
- response = execute(request)
57
- end
58
- detect_error!(response.body)
59
- parsed_response = @parse_response.call(response)
60
- detect_error!(parsed_response)
61
- within_action_context(response.code, parsed_response, response.headers, &@after_response)
62
- end
63
- )
45
+ __getobj__ || __setobj__(response)
64
46
  rescue RestClient::Exception => e
65
47
  if after_error_response_matches?(e)
66
48
  return apply_after_error_response(e)
67
49
  end
68
50
 
69
- Kernel.raise RequestError.new(response: e.response, message: e.message, method: current_verb,
70
- code: e.http_code)
51
+ Kernel.raise RequestError.new(
52
+ response: e.response,
53
+ message: e.message,
54
+ method: current_verb,
55
+ code: e.http_code
56
+ )
57
+ rescue StandardError => e
58
+ error_backtrace = Array.wrap(e.backtrace)
59
+ first_call_after_request_idx = error_backtrace.rindex { |s| s.start_with?(__FILE__) }
60
+ error_backtrace_after_request = error_backtrace[0..first_call_after_request_idx]
61
+ e.set_backtrace(error_backtrace_after_request + @callstack_before_request)
62
+ Kernel.raise e
71
63
  end
72
64
 
73
65
  def headers(headers)
@@ -81,7 +73,12 @@ module Workato
81
73
  end
82
74
 
83
75
  def params(params)
84
- @params.merge!(params)
76
+ if params.is_a?(Hash)
77
+ @params ||= HashWithIndifferentAccess.new
78
+ @params.merge!(params)
79
+ else
80
+ @params = params
81
+ end
85
82
  self
86
83
  end
87
84
 
@@ -142,13 +139,17 @@ module Workato
142
139
 
143
140
  def request_format_json
144
141
  @content_type_header = :json
145
- @render_request = ->(payload) { ActiveSupport::JSON.encode(payload) if payload }
142
+ @render_request = lambda_with_error_wrap(JSONRequestFormatError) do |payload|
143
+ ActiveSupport::JSON.encode(payload) if payload
144
+ end
146
145
  self
147
146
  end
148
147
 
149
148
  def response_format_json
150
149
  @accept_header = :json
151
- @parse_response = ->(payload) { ActiveSupport::JSON.decode(payload.presence || '{}') }
150
+ @parse_response = lambda_with_error_wrap(JSONResponseFormatError) do |payload|
151
+ ActiveSupport::JSON.decode(payload.presence || '{}')
152
+ end
152
153
  self
153
154
  end
154
155
 
@@ -158,17 +159,19 @@ module Workato
158
159
 
159
160
  def request_format_xml(root_element_name, namespaces = {})
160
161
  @content_type_header = :xml
161
- @render_request = Kernel.lambda { |payload|
162
+ @render_request = lambda_with_error_wrap(XMLRequestFormatError) do |payload|
162
163
  next unless payload
163
164
 
164
165
  Gyoku.xml({ root_element_name => payload.merge(namespaces).deep_symbolize_keys }, key_converter: :none)
165
- }
166
+ end
166
167
  self
167
168
  end
168
169
 
169
170
  def response_format_xml(strip_response_namespaces: false)
170
171
  @accept_header = :xml
171
- @parse_response = ->(payload) { Xml.parse_xml_to_hash(payload, strip_namespaces: strip_response_namespaces) }
172
+ @parse_response = lambda_with_error_wrap(XMLResponseFormatError) do |payload|
173
+ Workato::Utilities::Xml.parse_xml_to_hash(payload, strip_namespaces: strip_response_namespaces)
174
+ end
172
175
  self
173
176
  end
174
177
 
@@ -179,7 +182,7 @@ module Workato
179
182
  end
180
183
 
181
184
  def response_format_raw
182
- @parse_response = Kernel.lambda do |payload|
185
+ @parse_response = lambda_with_error_wrap(RAWResponseFormatError) do |payload|
183
186
  payload.body.force_encoding(::Encoding::BINARY)
184
187
  payload.body.valid_encoding? ? payload.body : payload.body.force_encoding(::Encoding::BINARY)
185
188
  end
@@ -189,7 +192,7 @@ module Workato
189
192
  def request_format_multipart_form
190
193
  @content_type_header = nil
191
194
 
192
- @render_request = Kernel.lambda do |payload|
195
+ @render_request = lambda_with_error_wrap(MultipartFormRequestFormatError) do |payload|
193
196
  payload&.each_with_object({}) do |(name, (value, content_type, original_filename)), rendered|
194
197
  rendered[name] = if content_type.present?
195
198
  Part.new(name, content_type, original_filename || ::File.basename(name), value.to_s)
@@ -204,7 +207,7 @@ module Workato
204
207
 
205
208
  def request_format_www_form_urlencoded
206
209
  @content_type_header = 'application/x-www-form-urlencoded'
207
- @render_request = Kernel.lambda { |payload| payload.to_param }
210
+ @render_request = lambda_with_error_wrap(WWWFormURLEncodedRequestFormatError, &:to_param)
208
211
  self
209
212
  end
210
213
 
@@ -228,6 +231,8 @@ module Workato
228
231
  OpenSSL::X509::Certificate.new(intermediate)
229
232
  end
230
233
  self
234
+ rescue OpenSSL::OpenSSLError => e
235
+ Kernel.raise(RequestTLSCertificateFormatError, e)
231
236
  end
232
237
 
233
238
  def tls_server_certs(certificates:, strict: true)
@@ -237,17 +242,47 @@ module Workato
237
242
  @ssl_cert_store.add_cert(OpenSSL::X509::Certificate.new(certificate))
238
243
  end
239
244
  self
245
+ rescue OpenSSL::OpenSSLError => e
246
+ Kernel.raise(RequestTLSCertificateFormatError, e)
240
247
  end
241
248
 
242
249
  def puts(*args)
243
250
  ::Kernel.puts(*args)
244
251
  end
245
252
 
253
+ def try(*args, &block)
254
+ execute!.try(*args, &block)
255
+ end
256
+
246
257
  private
247
258
 
248
259
  attr_reader :method
249
260
 
250
- def execute(request)
261
+ def response
262
+ authorized do
263
+ begin
264
+ request = RestClientRequest.new(rest_request_params)
265
+ response = execute_request(request)
266
+ rescue RestClient::Unauthorized => e
267
+ Kernel.raise e unless @digest_auth
268
+
269
+ @digest_auth = false
270
+ headers('Authorization' => Net::HTTP::DigestAuth.new.auth_header(
271
+ URI.parse(build_url),
272
+ e.response.headers[:www_authenticate],
273
+ method.to_s.upcase
274
+ ))
275
+ request = RestClientRequest.new(rest_request_params)
276
+ response = execute_request(request)
277
+ end
278
+ detect_error!(response.body)
279
+ parsed_response = @parse_response.call(response)
280
+ detect_error!(parsed_response)
281
+ apply_after_response(response.code, parsed_response, response.headers)
282
+ end
283
+ end
284
+
285
+ def execute_request(request)
251
286
  if @follow_redirection.nil?
252
287
  request.execute
253
288
  else
@@ -266,25 +301,22 @@ module Workato
266
301
  end
267
302
  end
268
303
 
269
- def build_request
270
- RestClient::Request.new(
271
- {
272
- method: method,
273
- url: build_url,
274
- headers: build_headers,
275
- payload: @render_request.call(@payload)
276
- }.tap do |request_hash|
277
- if @ssl_client_cert.present? && @ssl_client_key.present?
278
- request_hash[:ssl_client_cert] = @ssl_client_cert
279
- request_hash[:ssl_client_key] = @ssl_client_key
304
+ def rest_request_params
305
+ {
306
+ method: method,
307
+ url: build_url,
308
+ headers: build_headers,
309
+ payload: @render_request.call(@payload),
310
+ case_sensitive_headers: @case_sensitive_headers.transform_keys(&:to_s)
311
+ }.tap do |request_hash|
312
+ if @ssl_client_cert.present? && @ssl_client_key.present?
313
+ request_hash[:ssl_client_cert] = @ssl_client_cert
314
+ request_hash[:ssl_client_key] = @ssl_client_key
315
+ if @ssl_client_intermediate_certs.present?
316
+ request_hash[:ssl_extra_chain_cert] = @ssl_client_intermediate_certs
280
317
  end
281
- request_hash[:ssl_cert_store] = @ssl_cert_store if @ssl_cert_store
282
- end
283
- ).tap do |request|
284
- request.case_sensitive_headers = @case_sensitive_headers.transform_keys(&:to_s)
285
- if @ssl_client_intermediate_certs.present? && @ssl_client_cert.present? && @ssl_client_key.present?
286
- request.extra_chain_cert = @ssl_client_intermediate_certs
287
318
  end
319
+ request_hash[:ssl_cert_store] = @ssl_cert_store if @ssl_cert_store
288
320
  end
289
321
  end
290
322
 
@@ -295,14 +327,14 @@ module Workato
295
327
  URI.parse(@uri)
296
328
  end
297
329
 
298
- return uri.to_s unless @params.any? || @user || @password
330
+ return uri.to_s unless @params || @user || @password
299
331
 
300
332
  unless @digest_auth
301
333
  uri.user = URI.encode_www_form_component(@user) if @user
302
334
  uri.password = URI.encode_www_form_component(@password) if @password
303
335
  end
304
336
 
305
- return uri.to_s unless @params.any?
337
+ return uri.to_s unless @params
306
338
 
307
339
  query = uri.query.to_s.split('&').select(&:present?).join('&').presence
308
340
  params = @params.to_param.presence
@@ -354,12 +386,19 @@ module Workato
354
386
  within_action_context(
355
387
  exception.http_code,
356
388
  exception.http_body,
357
- exception.http_headers&.with_indifferent_access || {},
389
+ HashWithIndifferentAccess.wrap(exception.http_headers),
358
390
  exception.message,
359
391
  &@after_error_response
360
392
  )
361
393
  end
362
394
 
395
+ def apply_after_response(code, parsed_response, headers)
396
+ encoded_headers = (headers || {}).each_with_object(HashWithIndifferentAccess.new) do |(k, v), h|
397
+ h[k] = Workato::Utilities::Encoding.force_best_encoding!(v.to_s)
398
+ end
399
+ within_action_context(code, parsed_response, encoded_headers, &@after_response)
400
+ end
401
+
363
402
  def within_action_context(*args, &block)
364
403
  (@action || self).instance_exec(*args, &block)
365
404
  end
@@ -378,13 +417,13 @@ module Workato
378
417
  first = true
379
418
  begin
380
419
  settings = connection.settings
381
- if /oauth2/i =~ connection.authorization.type
420
+ if connection.authorization.oauth2?
382
421
  instance_exec(settings, settings[:access_token], @auth_type, &apply)
383
422
  else
384
423
  instance_exec(settings, @auth_type, &apply)
385
424
  end
386
425
  yield
387
- rescue StandardError => e
426
+ rescue RestClient::Exception, CustomRequestError => e
388
427
  Kernel.raise e unless first
389
428
  Kernel.raise e unless refresh_authorization!(e.try(:http_code), e.try(:http_body), e.message)
390
429
 
@@ -413,6 +452,16 @@ module Workato
413
452
  T.must(@connection)
414
453
  end
415
454
 
455
+ def lambda_with_error_wrap(error_type, &block)
456
+ Kernel.lambda do |payload|
457
+ begin
458
+ block.call(payload)
459
+ rescue StandardError => e
460
+ Kernel.raise error_type.new(e)
461
+ end
462
+ end
463
+ end
464
+
416
465
  class Part < StringIO
417
466
  def initialize(path, content_type, original_filename, *args)
418
467
  super(*args)
@@ -425,6 +474,35 @@ module Workato
425
474
  attr_reader :content_type
426
475
  attr_reader :original_filename
427
476
  end
477
+
478
+ class RestClientRequest < ::RestClient::Request
479
+ def initialize(args)
480
+ super
481
+ @ssl_opts[:extra_chain_cert] = args[:ssl_extra_chain_cert] if args.key?(:ssl_extra_chain_cert)
482
+ @case_sensitive_headers = args[:case_sensitive_headers]
483
+ @before_execution_proc = proc do |net_http_request, _args|
484
+ net_http_request.case_sensitive_headers = args[:case_sensitive_headers]
485
+ end
486
+ end
487
+
488
+ def ssl_extra_chain_cert
489
+ @ssl_opts[:extra_chain_cert]
490
+ end
491
+
492
+ def processed_headers
493
+ return @processed_headers if @case_sensitive_headers.blank?
494
+ return @case_sensitive_headers if @processed_headers.blank?
495
+
496
+ @processed_headers.merge(@case_sensitive_headers)
497
+ end
498
+
499
+ def net_http_object(hostname, port)
500
+ net = super(hostname, port)
501
+ net.extra_chain_cert = ssl_extra_chain_cert if ssl_extra_chain_cert
502
+ net
503
+ end
504
+ end
505
+ private_constant :RestClientRequest
428
506
  end
429
507
  end
430
508
  end
@@ -1,20 +1,22 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
+ using Workato::Extension::HashWithIndifferentAccess
5
+
4
6
  module Workato
5
7
  module Connector
6
8
  module Sdk
7
9
  class Schema < SimpleDelegator
8
10
  def initialize(schema: [])
9
- super(Fields.new(::Array.wrap(schema).map(&:with_indifferent_access)))
11
+ super(Fields.new(::Array.wrap(schema).map { |i| HashWithIndifferentAccess.wrap(i) }))
10
12
  end
11
13
 
12
14
  def trim(input)
13
- input.with_indifferent_access.keep_if { |property_name| includes_property?(property_name) }
15
+ HashWithIndifferentAccess.wrap(input).keep_if { |property_name| includes_property?(property_name) }
14
16
  end
15
17
 
16
18
  def apply(input, enforce_required:, &block)
17
- input.with_indifferent_access.tap do |input_with_indifferent_access|
19
+ HashWithIndifferentAccess.wrap(input).tap do |input_with_indifferent_access|
18
20
  apply_to_hash(self, input_with_indifferent_access, enforce_required: enforce_required, &block)
19
21
  end
20
22
  end