workato-connector-sdk 1.2.0 → 1.3.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -0
  4. data/lib/workato/cli/edit_command.rb +3 -1
  5. data/lib/workato/cli/exec_command.rb +76 -10
  6. data/lib/workato/cli/generate_command.rb +2 -2
  7. data/lib/workato/cli/main.rb +17 -10
  8. data/lib/workato/cli/oauth2_command.rb +4 -4
  9. data/lib/workato/cli/push_command.rb +2 -2
  10. data/lib/workato/cli/schema_command.rb +2 -2
  11. data/lib/workato/connector/sdk/account_properties.rb +2 -2
  12. data/lib/workato/connector/sdk/action.rb +20 -70
  13. data/lib/workato/connector/sdk/block_invocation_refinements.rb +2 -10
  14. data/lib/workato/connector/sdk/connection.rb +36 -19
  15. data/lib/workato/connector/sdk/connector.rb +65 -77
  16. data/lib/workato/connector/sdk/core.rb +62 -0
  17. data/lib/workato/connector/sdk/dsl/aws.rb +3 -3
  18. data/lib/workato/connector/sdk/dsl/call.rb +1 -1
  19. data/lib/workato/connector/sdk/dsl/csv_package.rb +133 -0
  20. data/lib/workato/connector/sdk/dsl/execution_context.rb +1 -0
  21. data/lib/workato/connector/sdk/dsl/http.rb +1 -1
  22. data/lib/workato/connector/sdk/dsl/reinvoke_after.rb +84 -0
  23. data/lib/workato/connector/sdk/dsl/stream_package.rb +65 -0
  24. data/lib/workato/connector/sdk/dsl/time.rb +0 -14
  25. data/lib/workato/connector/sdk/dsl/workato_package.rb +146 -0
  26. data/lib/workato/connector/sdk/dsl.rb +63 -10
  27. data/lib/workato/connector/sdk/errors.rb +28 -11
  28. data/lib/workato/connector/sdk/operation.rb +9 -2
  29. data/lib/workato/connector/sdk/request.rb +63 -25
  30. data/lib/workato/connector/sdk/schema/field/convertors.rb +2 -2
  31. data/lib/workato/connector/sdk/schema/type/unicode_string.rb +1 -1
  32. data/lib/workato/connector/sdk/schema.rb +7 -5
  33. data/lib/workato/connector/sdk/settings.rb +10 -1
  34. data/lib/workato/connector/sdk/stream.rb +243 -0
  35. data/lib/workato/connector/sdk/streams.rb +71 -0
  36. data/lib/workato/connector/sdk/summarize.rb +2 -2
  37. data/lib/workato/connector/sdk/trigger.rb +14 -7
  38. data/lib/workato/connector/sdk/version.rb +1 -1
  39. data/lib/workato/connector/sdk.rb +19 -46
  40. data/lib/workato/extension/array.rb +2 -0
  41. data/lib/workato/extension/case_sensitive_headers.rb +0 -1
  42. data/lib/workato/extension/content_encoding_decoder.rb +2 -0
  43. data/lib/workato/extension/currency/countries.rb +79 -0
  44. data/lib/workato/extension/currency/countries.yml +18433 -0
  45. data/lib/workato/extension/currency/currencies.rb +55 -0
  46. data/lib/workato/extension/currency/currencies.yml +479 -0
  47. data/lib/workato/extension/currency.rb +73 -5
  48. data/lib/workato/extension/enumerable.rb +2 -2
  49. data/lib/workato/extension/metadata_fix_wrap_kw_args.rb +11 -0
  50. data/lib/workato/extension/string.rb +14 -111
  51. data/lib/workato/testing/vcr_encrypted_cassette_serializer.rb +2 -0
  52. data/lib/workato/types/binary.rb +55 -0
  53. metadata +46 -61
  54. data/lib/workato/connector/sdk/dsl/csv.rb +0 -125
  55. data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +0 -167
@@ -32,14 +32,15 @@ module Workato
32
32
 
33
33
  class Connection
34
34
  extend T::Sig
35
+ include MonitorMixin
35
36
 
36
37
  using BlockInvocationRefinements
37
38
 
38
39
  sig { returns(HashWithIndifferentAccess) }
39
40
  attr_reader :source
40
41
 
41
- cattr_accessor :on_settings_update
42
- cattr_accessor :multi_auth_selected_fallback
42
+ class_attribute :on_settings_update, instance_predicate: false
43
+ class_attribute :multi_auth_selected_fallback, instance_predicate: false
43
44
 
44
45
  sig do
45
46
  params(
@@ -49,6 +50,7 @@ module Workato
49
50
  ).void
50
51
  end
51
52
  def initialize(connection: {}, methods: {}, settings: {})
53
+ super()
52
54
  @methods_source = T.let(HashWithIndifferentAccess.wrap(methods), HashWithIndifferentAccess)
53
55
  @source = T.let(HashWithIndifferentAccess.wrap(connection), HashWithIndifferentAccess)
54
56
  @settings = T.let(settings, SorbetTypes::SettingsHash)
@@ -63,7 +65,9 @@ module Workato
63
65
  def settings
64
66
  # we can't freeze or memoise because some developers modify it for storing something temporary in it.
65
67
  # always return a new copy
66
- @settings.with_indifferent_access
68
+ synchronize do
69
+ @settings.with_indifferent_access
70
+ end
67
71
  end
68
72
 
69
73
  sig { params(settings: SorbetTypes::SettingsHash).returns(SorbetTypes::SettingsHash) }
@@ -93,16 +97,17 @@ module Workato
93
97
  return unless source[:base_uri]
94
98
 
95
99
  merge_settings!(settings) if settings
96
- Dsl::WithDsl.execute(self, self.settings, &source[:base_uri])
100
+ global_dsl_context.execute(self.settings, &source['base_uri'])
97
101
  end
98
102
 
99
103
  sig do
100
104
  params(
101
105
  message: String,
106
+ settings_before: SorbetTypes::SettingsHash,
102
107
  refresher: T.proc.returns(T.nilable(SorbetTypes::SettingsHash))
103
108
  ).returns(T::Boolean)
104
109
  end
105
- def update_settings!(message, &refresher)
110
+ def update_settings!(message, settings_before, &refresher)
106
111
  updater = lambda do
107
112
  new_settings = refresher.call
108
113
  next unless new_settings
@@ -110,16 +115,18 @@ module Workato
110
115
  settings.merge(new_settings)
111
116
  end
112
117
 
113
- new_settings = if on_settings_update
114
- on_settings_update.call(message, &updater)
115
- else
116
- updater.call
117
- end
118
- return false unless new_settings
118
+ synchronize do
119
+ new_settings = if on_settings_update
120
+ T.must(on_settings_update).call(message, settings_before, updater)
121
+ else
122
+ updater.call
123
+ end
124
+ return false unless new_settings
119
125
 
120
- merge_settings!(new_settings)
126
+ merge_settings!(new_settings)
121
127
 
122
- true
128
+ true
129
+ end
123
130
  end
124
131
 
125
132
  private
@@ -127,6 +134,11 @@ module Workato
127
134
  sig { returns(HashWithIndifferentAccess) }
128
135
  attr_reader :methods_source
129
136
 
137
+ sig { returns(Dsl::WithDsl) }
138
+ def global_dsl_context
139
+ Dsl::WithDsl.new(self)
140
+ end
141
+
130
142
  class Authorization
131
143
  extend T::Sig
132
144
 
@@ -175,7 +187,7 @@ module Workato
175
187
  client_id = source[:client_id]
176
188
 
177
189
  if client_id.is_a?(Proc)
178
- Dsl::WithDsl.execute(@connection, @connection.settings, &client_id)
190
+ global_dsl_context.execute(@connection.settings, &client_id)
179
191
  else
180
192
  client_id
181
193
  end
@@ -187,7 +199,7 @@ module Workato
187
199
  client_secret_source = source[:client_secret]
188
200
 
189
201
  if client_secret_source.is_a?(Proc)
190
- Dsl::WithDsl.execute(@connection, @connection.settings, &client_secret_source)
202
+ global_dsl_context.execute(@connection.settings, &client_secret_source)
191
203
  else
192
204
  client_secret_source
193
205
  end
@@ -198,7 +210,7 @@ module Workato
198
210
  @connection.merge_settings!(settings) if settings
199
211
  return unless source[:authorization_url]
200
212
 
201
- Dsl::WithDsl.execute(@connection, @connection.settings, &source[:authorization_url])
213
+ global_dsl_context.execute(@connection.settings, &source[:authorization_url])
202
214
  end
203
215
 
204
216
  sig { params(settings: T.nilable(SorbetTypes::SettingsHash)).returns(T.nilable(String)) }
@@ -206,7 +218,7 @@ module Workato
206
218
  @connection.merge_settings!(settings) if settings
207
219
  return unless source[:token_url]
208
220
 
209
- Dsl::WithDsl.execute(@connection, @connection.settings, &source[:token_url])
221
+ global_dsl_context.execute(@connection.settings, &source[:token_url])
210
222
  end
211
223
 
212
224
  sig do
@@ -249,7 +261,7 @@ module Workato
249
261
 
250
262
  refresh_on = self.refresh_on
251
263
  refresh_on.blank? || refresh_on.any? do |pattern|
252
- pattern.is_a?(::Integer) && pattern == http_code ||
264
+ (pattern.is_a?(::Integer) && pattern == http_code) ||
253
265
  pattern === exception&.to_s ||
254
266
  pattern === http_body
255
267
  end
@@ -341,7 +353,7 @@ module Workato
341
353
  def refresh_oauth2_token_using_token_url(settings)
342
354
  if settings[:refresh_token].blank?
343
355
  raise NotImplementedError, 'refresh_token is empty. ' \
344
- 'Use workato oauth2 command to acquire access_token and refresh_token'
356
+ 'Use workato oauth2 command to acquire access_token and refresh_token'
345
357
  end
346
358
 
347
359
  response = RestClient::Request.execute(
@@ -363,6 +375,11 @@ module Workato
363
375
  refresh_token: tokens['refresh_token'].presence || settings[:refresh_token]
364
376
  }.with_indifferent_access
365
377
  end
378
+
379
+ sig { returns(Dsl::WithDsl) }
380
+ def global_dsl_context
381
+ Dsl::WithDsl.new(@connection)
382
+ end
366
383
  end
367
384
 
368
385
  private_constant :Authorization
@@ -29,11 +29,6 @@ module Workato
29
29
  @methods_source = T.let(HashWithIndifferentAccess.wrap(@source[:methods]), HashWithIndifferentAccess)
30
30
  end
31
31
 
32
- sig { params(path: String, params: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
33
- def invoke(path, params = {})
34
- InvokePath.new(path: path, connector: self, params: params).call
35
- end
36
-
37
32
  sig { returns(T.nilable(String)) }
38
33
  def title
39
34
  @source[:title]
@@ -46,7 +41,8 @@ module Workato
46
41
  actions: source[:actions].presence || {},
47
42
  methods: methods_source,
48
43
  object_definitions: object_definitions,
49
- connection: connection
44
+ connection: connection,
45
+ streams: streams
50
46
  )
51
47
  end
52
48
 
@@ -55,7 +51,8 @@ module Workato
55
51
  @methods = T.let(@methods, T.nilable(MethodsProxy))
56
52
  @methods ||= MethodsProxy.new(
57
53
  methods: methods_source,
58
- connection: connection
54
+ connection: connection,
55
+ streams: streams
59
56
  )
60
57
  end
61
58
 
@@ -79,7 +76,8 @@ module Workato
79
76
  triggers: source[:triggers].presence || {},
80
77
  methods: methods_source,
81
78
  connection: connection,
82
- object_definitions: object_definitions
79
+ object_definitions: object_definitions,
80
+ streams: streams
83
81
  )
84
82
  end
85
83
 
@@ -113,8 +111,34 @@ module Workato
113
111
  )
114
112
  end
115
113
 
114
+ sig { returns(Streams) }
115
+ def streams
116
+ @streams = T.let(@streams, T.nilable(Streams))
117
+ @streams ||= Streams.new(
118
+ streams: streams_sources,
119
+ methods: methods_source,
120
+ connection: connection
121
+ )
122
+ end
123
+
116
124
  private
117
125
 
126
+ sig { returns(HashWithIndifferentAccess) }
127
+ def streams_sources
128
+ @streams_sources = T.let(@streams_sources, T.nilable(HashWithIndifferentAccess))
129
+ return @streams_sources if @streams_sources
130
+
131
+ @streams_sources = HashWithIndifferentAccess.new
132
+ @streams_sources.merge!(source[:streams].presence || {})
133
+ (source[:actions] || {}).values.map do |action|
134
+ @streams_sources.merge!(action[:streams] || {})
135
+ end
136
+ (source[:trigger] || {}).values.map do |trigger|
137
+ @streams_sources.merge!(trigger[:streams] || {})
138
+ end
139
+ @streams_sources
140
+ end
141
+
118
142
  sig { returns(HashWithIndifferentAccess) }
119
143
  attr_reader :methods_source
120
144
 
@@ -133,18 +157,20 @@ module Workato
133
157
  actions: HashWithIndifferentAccess,
134
158
  object_definitions: ObjectDefinitions,
135
159
  methods: HashWithIndifferentAccess,
136
- connection: Connection
160
+ connection: Connection,
161
+ streams: Streams
137
162
  ).void
138
163
  end
139
- def initialize(actions:, object_definitions:, methods:, connection:)
164
+ def initialize(actions:, object_definitions:, methods:, connection:, streams:)
140
165
  @methods = methods
141
166
  @connection = connection
142
167
  @object_definitions = object_definitions
168
+ @streams = streams
143
169
  @actions = T.let({}, T::Hash[T.any(Symbol, String), Action])
144
170
  define_action_methods(actions)
145
171
  end
146
172
 
147
- sig { params(action: T.any(Symbol, String)).returns(T.untyped) }
173
+ sig { params(action: T.any(Symbol, String)).returns(Action) }
148
174
  def [](action)
149
175
  public_send(action)
150
176
  end
@@ -157,6 +183,9 @@ module Workato
157
183
  sig { returns(Connection) }
158
184
  attr_reader :connection
159
185
 
186
+ sig { returns(Streams) }
187
+ attr_reader :streams
188
+
160
189
  sig { returns(ObjectDefinitions) }
161
190
  attr_reader :object_definitions
162
191
 
@@ -168,7 +197,8 @@ module Workato
168
197
  action: definition,
169
198
  object_definitions: object_definitions,
170
199
  methods: methods,
171
- connection: connection
200
+ connection: connection,
201
+ streams: streams
172
202
  )
173
203
  return @actions[action] if input_.nil?
174
204
 
@@ -184,12 +214,14 @@ module Workato
184
214
  sig do
185
215
  params(
186
216
  methods: HashWithIndifferentAccess,
187
- connection: Connection
217
+ connection: Connection,
218
+ streams: Streams
188
219
  ).void
189
220
  end
190
- def initialize(methods:, connection:)
221
+ def initialize(methods:, connection:, streams:)
191
222
  @methods = methods
192
223
  @connection = connection
224
+ @streams = streams
193
225
  @actions = T.let({}, T::Hash[T.any(Symbol, String), Action])
194
226
  define_action_methods
195
227
  end
@@ -202,6 +234,9 @@ module Workato
202
234
  sig { returns(Connection) }
203
235
  attr_reader :connection
204
236
 
237
+ sig { returns(Streams) }
238
+ attr_reader :streams
239
+
205
240
  sig { void }
206
241
  def define_action_methods
207
242
  methods.each do |method, _definition|
@@ -211,7 +246,8 @@ module Workato
211
246
  execute: -> { T.unsafe(self).call(method, *args) }
212
247
  },
213
248
  methods: methods,
214
- connection: connection
249
+ connection: connection,
250
+ streams: streams
215
251
  )
216
252
  T.must(@actions[method]).execute
217
253
  end
@@ -278,17 +314,24 @@ module Workato
278
314
  triggers: HashWithIndifferentAccess,
279
315
  object_definitions: ObjectDefinitions,
280
316
  methods: HashWithIndifferentAccess,
281
- connection: Connection
317
+ connection: Connection,
318
+ streams: Streams
282
319
  ).void
283
320
  end
284
- def initialize(triggers:, object_definitions:, methods:, connection:)
321
+ def initialize(triggers:, object_definitions:, methods:, connection:, streams:)
285
322
  @methods = methods
286
323
  @connection = connection
287
324
  @object_definitions = object_definitions
325
+ @streams = streams
288
326
  @triggers = T.let({}, T::Hash[T.any(Symbol, String), Trigger])
289
327
  define_trigger_methods(triggers)
290
328
  end
291
329
 
330
+ sig { params(trigger: T.any(Symbol, String)).returns(Trigger) }
331
+ def [](trigger)
332
+ public_send(trigger)
333
+ end
334
+
292
335
  private
293
336
 
294
337
  sig { returns(HashWithIndifferentAccess) }
@@ -297,6 +340,9 @@ module Workato
297
340
  sig { returns(Connection) }
298
341
  attr_reader :connection
299
342
 
343
+ sig { returns(Streams) }
344
+ attr_reader :streams
345
+
300
346
  sig { returns(ObjectDefinitions) }
301
347
  attr_reader :object_definitions
302
348
 
@@ -308,7 +354,8 @@ module Workato
308
354
  trigger: definition,
309
355
  object_definitions: object_definitions,
310
356
  methods: methods,
311
- connection: connection
357
+ connection: connection,
358
+ streams: streams
312
359
  )
313
360
 
314
361
  return @triggers[trigger] if input_.nil?
@@ -318,65 +365,6 @@ module Workato
318
365
  end
319
366
  end
320
367
  end
321
-
322
- class InvokePath
323
- extend T::Sig
324
-
325
- sig do
326
- params(
327
- path: String,
328
- connector: Connector,
329
- params: T::Hash[Symbol, T.untyped]
330
- ).void
331
- end
332
- def initialize(path:, connector:, params:)
333
- @path = T.let(path, String)
334
- @connector = T.let(connector, Connector)
335
- @params = T.let(params, T::Hash[Symbol, T.untyped])
336
- end
337
-
338
- sig { returns(T.untyped) }
339
- def call
340
- invoke_path
341
- end
342
-
343
- private
344
-
345
- sig { returns(String) }
346
- attr_reader :path
347
-
348
- sig { returns(Connector) }
349
- attr_reader :connector
350
-
351
- sig { returns(T::Hash[Symbol, T.untyped]) }
352
- attr_reader :params
353
-
354
- sig { returns(T.untyped) }
355
- def invoke_path
356
- methods = path.split('.')
357
- method = methods.pop
358
- raise ArgumentError, 'path is not found' unless method
359
-
360
- object = methods.inject(connector) { |obj, m| obj.public_send(m) }
361
- output = invoke_method(object, method)
362
- if output.respond_to?(:invoke)
363
- invoke_method(output, :invoke)
364
- else
365
- output
366
- end
367
- end
368
-
369
- sig { params(object: T.untyped, method: T.any(Symbol, String)).returns(T.untyped) }
370
- def invoke_method(object, method)
371
- parameters = object.method(method).parameters.reject { |p| p[0] == :block }.map(&:second)
372
- args = params.values_at(*parameters)
373
- if parameters.last == :args
374
- args = args.take(args.length - 1) + Array.wrap(args.last).flatten(1)
375
- end
376
- object.public_send(method, *args)
377
- end
378
- end
379
- private_constant :InvokePath
380
368
  end
381
369
  end
382
370
  end
@@ -0,0 +1,62 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+ require 'active_support'
6
+ require 'active_support/core_ext/object/deep_dup'
7
+ require 'active_support/core_ext/object/try'
8
+ require 'active_support/core_ext/hash/indifferent_access'
9
+ require 'active_support/core_ext/array/wrap'
10
+ require 'active_support/core_ext/array/access'
11
+ require 'active_support/core_ext/numeric/bytes'
12
+ require 'active_support/core_ext/numeric/conversions'
13
+ require 'active_support/core_ext/numeric/time'
14
+
15
+ module Workato
16
+ module Connector
17
+ module Sdk
18
+ DEFAULT_MASTER_KEY_ENV = 'WORKATO_CONNECTOR_MASTER_KEY'
19
+ DEFAULT_MASTER_KEY_PATH = 'master.key'
20
+
21
+ DEFAULT_CONNECTOR_PATH = 'connector.rb'
22
+
23
+ DEFAULT_SETTINGS_PATH = 'settings.yaml'
24
+ DEFAULT_ENCRYPTED_SETTINGS_PATH = 'settings.yaml.enc'
25
+
26
+ DEFAULT_ACCOUNT_PROPERTIES_PATH = 'account_properties.yaml'
27
+ DEFAULT_ENCRYPTED_ACCOUNT_PROPERTIES_PATH = 'account_properties.yaml.enc'
28
+
29
+ DEFAULT_LOOKUP_TABLES_PATH = 'lookup_tables.yaml'
30
+
31
+ DEFAULT_TIME_ZONE = 'Pacific Time (US & Canada)'
32
+
33
+ DEFAULT_SCHEMAS_PATH = 'workato_schemas.json'
34
+
35
+ WORKATO_API_EMAIL_ENV = 'WORKATO_API_EMAIL'
36
+ WORKATO_API_TOKEN_ENV = 'WORKATO_API_TOKEN'
37
+
38
+ WORKATO_BASE_URL_ENV = 'WORKATO_BASE_URL'
39
+ DEFAULT_WORKATO_BASE_URL = 'https://app.workato.com'
40
+ WORKATO_BASE_URL = T.let(ENV.fetch(WORKATO_BASE_URL_ENV, DEFAULT_WORKATO_BASE_URL), String)
41
+ end
42
+ end
43
+ end
44
+
45
+ require_relative '../../extension/hash_with_indifferent_access'
46
+
47
+ require_relative './errors'
48
+ require_relative './account_properties'
49
+ require_relative './operation'
50
+ require_relative './connection'
51
+ require_relative './stream'
52
+ require_relative './streams'
53
+ require_relative './action'
54
+ require_relative './lookup_tables'
55
+ require_relative './object_definitions'
56
+ require_relative './request'
57
+ require_relative './settings'
58
+ require_relative './summarize'
59
+ require_relative './trigger'
60
+ require_relative './version'
61
+ require_relative './workato_schemas'
62
+ require_relative './connector'
@@ -16,9 +16,9 @@ module Workato
16
16
  DUMMY_AWS_IAM_EXTERNAL_ID = 'dummy-aws-iam-external-id'
17
17
  DUMMY_AWS_WORKATO_ACCOUNT_ID = 'dummy-aws-workato-account-id'
18
18
 
19
- AMAZON_ROLE_CLIENT_ID = ENV['AMAZON_ROLE_CLIENT_ID']
20
- AMAZON_ROLE_CLIENT_KEY = ENV['AMAZON_ROLE_CLIENT_KEY']
21
- AMAZON_ROLE_CLIENT_SECRET = ENV['AMAZON_ROLE_CLIENT_SECRET']
19
+ AMAZON_ROLE_CLIENT_ID = ENV.fetch('AMAZON_ROLE_CLIENT_ID', nil)
20
+ AMAZON_ROLE_CLIENT_KEY = ENV.fetch('AMAZON_ROLE_CLIENT_KEY', nil)
21
+ AMAZON_ROLE_CLIENT_SECRET = ENV.fetch('AMAZON_ROLE_CLIENT_SECRET', nil)
22
22
 
23
23
  WWW_FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
24
24
 
@@ -6,7 +6,7 @@ module Workato
6
6
  module Sdk
7
7
  module Dsl
8
8
  module Call
9
- def call(method, *args)
9
+ ruby2_keywords def call(method, *args)
10
10
  method_proc = @_methods[method]
11
11
 
12
12
  raise UndefinedMethodError, method unless method_proc
@@ -0,0 +1,133 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'csv'
5
+
6
+ module Workato
7
+ module Connector
8
+ module Sdk
9
+ CsvError = Class.new(Sdk::RuntimeError)
10
+
11
+ CsvFormatError = Class.new(CsvError)
12
+
13
+ class CsvFileTooBigError < CsvError
14
+ extend T::Sig
15
+
16
+ sig { returns(Integer) }
17
+ attr_reader :size
18
+
19
+ sig { returns(Integer) }
20
+ attr_reader :max
21
+
22
+ sig { params(size: Integer, max: Integer).void }
23
+ def initialize(size, max)
24
+ super("CSV file is too big. Max allowed: #{max.to_s(:human_size)}, got: #{size.to_s(:human_size)}")
25
+ @size = T.let(size, Integer)
26
+ @max = T.let(max, Integer)
27
+ end
28
+ end
29
+
30
+ class CsvFileTooManyLinesError < CsvError
31
+ extend T::Sig
32
+
33
+ sig { returns(Integer) }
34
+ attr_reader :max
35
+
36
+ sig { params(max: Integer).void }
37
+ def initialize(max)
38
+ super("CSV file has too many lines. Max allowed: #{max}")
39
+ @max = T.let(max, Integer)
40
+ end
41
+ end
42
+
43
+ module Dsl
44
+ class CsvPackage
45
+ extend T::Sig
46
+
47
+ MAX_FILE_SIZE_FOR_PARSE = T.let(30.megabytes, Integer)
48
+ private_constant :MAX_FILE_SIZE_FOR_PARSE
49
+
50
+ MAX_LINES_FOR_PARSE = 65_000
51
+ private_constant :MAX_LINES_FOR_PARSE
52
+
53
+ sig do
54
+ params(
55
+ str: String,
56
+ headers: T.any(T::Boolean, T::Array[String], String),
57
+ col_sep: T.nilable(String),
58
+ row_sep: T.nilable(String),
59
+ quote_char: T.nilable(String),
60
+ skip_blanks: T.nilable(T::Boolean),
61
+ skip_first_line: T::Boolean
62
+ ).returns(
63
+ T::Array[T::Hash[String, T.untyped]]
64
+ )
65
+ end
66
+ def parse(str, headers:, col_sep: nil, row_sep: nil, quote_char: nil, skip_blanks: nil,
67
+ skip_first_line: false)
68
+ if str.bytesize > MAX_FILE_SIZE_FOR_PARSE
69
+ raise CsvFileTooBigError.new(str.bytesize, MAX_FILE_SIZE_FOR_PARSE)
70
+ end
71
+
72
+ index = 0
73
+ options = { col_sep: col_sep, row_sep: row_sep, quote_char: quote_char, headers: headers,
74
+ skip_blanks: skip_blanks }.compact
75
+ Enumerator.new do |consumer|
76
+ CSV.parse(str, **options) do |row|
77
+ if index.zero? && skip_first_line
78
+ index += 1
79
+ next
80
+ end
81
+ if index == MAX_LINES_FOR_PARSE
82
+ raise CsvFileTooManyLinesError, MAX_LINES_FOR_PARSE
83
+ end
84
+
85
+ index += 1
86
+ consumer.yield(T.cast(row, CSV::Row).to_hash)
87
+ end
88
+ end.to_a
89
+ rescue CSV::MalformedCSVError => e
90
+ raise CsvFormatError, e
91
+ rescue ArgumentError => e
92
+ raise Sdk::RuntimeError, e.message
93
+ end
94
+
95
+ sig do
96
+ params(
97
+ str: T.nilable(String),
98
+ headers: T.nilable(T::Array[String]),
99
+ col_sep: T.nilable(String),
100
+ row_sep: T.nilable(String),
101
+ quote_char: T.nilable(String),
102
+ force_quotes: T.nilable(T::Boolean),
103
+ blk: T.proc.params(csv: CSV).void
104
+ ).returns(
105
+ String
106
+ )
107
+ end
108
+ def generate(str = nil, headers: nil, col_sep: nil, row_sep: nil, quote_char: nil, force_quotes: nil, &blk)
109
+ options = { col_sep: col_sep, row_sep: row_sep, quote_char: quote_char, headers: headers,
110
+ force_quotes: force_quotes }.compact
111
+ options[:write_headers] = options[:headers].present?
112
+
113
+ ::CSV.generate(str || String.new, **options, &blk)
114
+ rescue ArgumentError => e
115
+ raise Sdk::RuntimeError, e.message
116
+ end
117
+
118
+ private
119
+
120
+ T::Sig::WithoutRuntime.sig { params(symbol: T.any(String, Symbol), _args: T.untyped).void }
121
+ def method_missing(symbol, *_args)
122
+ raise UndefinedStdLibMethodError.new(symbol.to_s, 'workato.csv')
123
+ end
124
+
125
+ T::Sig::WithoutRuntime.sig { params(_args: T.untyped).returns(T::Boolean) }
126
+ def respond_to_missing?(*_args)
127
+ false
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'securerandom'
5
+ require 'active_support/core_ext/class/attribute'
5
6
 
6
7
  module Workato
7
8
  module Connector
@@ -53,7 +53,7 @@ module Workato
53
53
  exception = nil
54
54
  begin
55
55
  response = request.execute!
56
- rescue RequestError, RuntimeError => e
56
+ rescue StandardError => e
57
57
  exception = e.to_s
58
58
  end
59
59
  result[PARALLEL_SUCCESS_INDEX] &&= exception.nil?