workato-connector-sdk 1.0.3 → 1.2.0

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 (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