workato-connector-sdk 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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?