workato-connector-sdk 1.2.0 → 1.3.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +8 -0
  3. data/README.md +2 -6
  4. data/VERSION +1 -0
  5. data/lib/workato/cli/edit_command.rb +3 -1
  6. data/lib/workato/cli/exec_command.rb +76 -10
  7. data/lib/workato/cli/generate_command.rb +3 -2
  8. data/lib/workato/cli/main.rb +18 -10
  9. data/lib/workato/cli/oauth2_command.rb +4 -4
  10. data/lib/workato/cli/push_command.rb +23 -7
  11. data/lib/workato/cli/schema_command.rb +20 -6
  12. data/lib/workato/connector/sdk/account_properties.rb +3 -3
  13. data/lib/workato/connector/sdk/action.rb +20 -70
  14. data/lib/workato/connector/sdk/block_invocation_refinements.rb +4 -11
  15. data/lib/workato/connector/sdk/connection.rb +44 -21
  16. data/lib/workato/connector/sdk/connector.rb +73 -76
  17. data/lib/workato/connector/sdk/core.rb +62 -0
  18. data/lib/workato/connector/sdk/dsl/aws.rb +13 -3
  19. data/lib/workato/connector/sdk/dsl/call.rb +1 -1
  20. data/lib/workato/connector/sdk/dsl/csv_package.rb +133 -0
  21. data/lib/workato/connector/sdk/dsl/error.rb +2 -0
  22. data/lib/workato/connector/sdk/dsl/execution_context.rb +3 -0
  23. data/lib/workato/connector/sdk/dsl/http.rb +7 -2
  24. data/lib/workato/connector/sdk/dsl/reinvoke_after.rb +84 -0
  25. data/lib/workato/connector/sdk/dsl/stream_package.rb +59 -0
  26. data/lib/workato/connector/sdk/dsl/time.rb +0 -14
  27. data/lib/workato/connector/sdk/dsl/workato_package.rb +152 -0
  28. data/lib/workato/connector/sdk/dsl.rb +65 -10
  29. data/lib/workato/connector/sdk/errors.rb +28 -11
  30. data/lib/workato/connector/sdk/object_definitions.rb +59 -18
  31. data/lib/workato/connector/sdk/operation.rb +10 -3
  32. data/lib/workato/connector/sdk/request.rb +67 -26
  33. data/lib/workato/connector/sdk/schema/field/convertors.rb +2 -2
  34. data/lib/workato/connector/sdk/schema.rb +10 -7
  35. data/lib/workato/connector/sdk/settings.rb +13 -2
  36. data/lib/workato/connector/sdk/stream.rb +262 -0
  37. data/lib/workato/connector/sdk/streams.rb +72 -0
  38. data/lib/workato/connector/sdk/summarize.rb +4 -2
  39. data/lib/workato/connector/sdk/trigger.rb +14 -7
  40. data/lib/workato/connector/sdk/version.rb +1 -1
  41. data/lib/workato/connector/sdk.rb +20 -46
  42. data/lib/workato/extension/array.rb +2 -0
  43. data/lib/workato/extension/case_sensitive_headers.rb +0 -1
  44. data/lib/workato/extension/content_encoding_decoder.rb +2 -0
  45. data/lib/workato/extension/currency/countries.rb +79 -0
  46. data/lib/workato/extension/currency/countries.yml +18433 -0
  47. data/lib/workato/extension/currency/currencies.rb +55 -0
  48. data/lib/workato/extension/currency/currencies.yml +479 -0
  49. data/lib/workato/extension/currency.rb +73 -5
  50. data/lib/workato/extension/enumerable.rb +2 -2
  51. data/lib/workato/extension/metadata_fix_wrap_kw_args.rb +11 -0
  52. data/lib/workato/extension/string.rb +23 -111
  53. data/lib/workato/testing/vcr_encrypted_cassette_serializer.rb +2 -0
  54. data/lib/workato/testing/vcr_multipart_body_matcher.rb +1 -0
  55. data/lib/workato/types/binary.rb +99 -0
  56. data/lib/workato/types/unicode_string.rb +62 -0
  57. metadata +34 -62
  58. data/lib/workato/connector/sdk/dsl/csv.rb +0 -125
  59. data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +0 -167
  60. data/lib/workato/connector/sdk/schema/type/unicode_string.rb +0 -23
@@ -0,0 +1,262 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ using Workato::Extension::HashWithIndifferentAccess
5
+
6
+ module Workato
7
+ module Connector
8
+ module Sdk
9
+ module SorbetTypes
10
+ StreamProc = T.type_alias do
11
+ T.proc.params(
12
+ input: SorbetTypes::StreamInputHash,
13
+ from: Integer,
14
+ to: Integer,
15
+ frame_size: Integer
16
+ ).returns(SorbetTypes::StreamOutput)
17
+ end
18
+
19
+ StreamInProc = T.type_alias do
20
+ T.proc.params(
21
+ data: T.untyped,
22
+ from: Integer,
23
+ to: T.nilable(Integer),
24
+ eof: T::Boolean,
25
+ next_from: T.nilable(Integer)
26
+ ).void
27
+ end
28
+
29
+ StreamInputHash = T.type_alias { T::Hash[T.any(Symbol, String), T.untyped] }
30
+
31
+ StreamOutput = T.type_alias { [T.untyped, T::Boolean] }
32
+ end
33
+
34
+ class Stream < Operation
35
+ extend T::Sig
36
+
37
+ DEFAULT_FRAME_SIZE = T.let(10.megabytes, Integer)
38
+
39
+ using BlockInvocationRefinements
40
+ include Dsl::ReinvokeAfter
41
+
42
+ sig do
43
+ params(
44
+ stream: SorbetTypes::StreamProc,
45
+ methods: SorbetTypes::SourceHash,
46
+ connection: Connection
47
+ ).void
48
+ end
49
+ def initialize(stream:, methods: {}, connection: Connection.new)
50
+ super(methods: methods, connection: connection)
51
+ @stream_proc = stream
52
+ end
53
+
54
+ sig do
55
+ params(
56
+ input: SorbetTypes::StreamInputHash,
57
+ from: Integer,
58
+ to: Integer,
59
+ frame_size: Integer
60
+ ).returns(SorbetTypes::StreamOutput)
61
+ end
62
+ def chunk(input = {}, from = 0, to = from + DEFAULT_FRAME_SIZE, frame_size = DEFAULT_FRAME_SIZE)
63
+ raise "'frame_size' must be a positive integer number" unless frame_size.positive?
64
+
65
+ stream_proc = @stream_proc
66
+ execute(nil, { input: input, from: from, to: to, size: frame_size }) do |_, input| # rubocop:disable Lint/ShadowingOuterLocalVariable
67
+ T.unsafe(self).instance_exec(input['input'], input['from'], input['to'], input['size'], &stream_proc)
68
+ end
69
+ end
70
+
71
+ sig { params(input: SorbetTypes::StreamInputHash, frame_size: Integer).returns(T.untyped) }
72
+ def invoke(input = {}, frame_size = DEFAULT_FRAME_SIZE)
73
+ proxy = Proxy.new(name: '', input: input, stream: self)
74
+ reader = Reader.new(stream: proxy, frame_size: frame_size)
75
+ data = T.let(nil, T.untyped)
76
+ reader.each_chunk do |chunk|
77
+ data = data.nil? ? chunk : data + chunk
78
+ end
79
+ data
80
+ end
81
+
82
+ class << self
83
+ extend T::Sig
84
+
85
+ sig do
86
+ params(
87
+ stream: T.any(Proxy, T::Hash[T.untyped, T.untyped], String),
88
+ from: T.nilable(Integer),
89
+ frame_size: T.nilable(Integer),
90
+ blk: SorbetTypes::StreamInProc
91
+ ).void
92
+ end
93
+ def each_chunk(stream:, from:, frame_size: nil, &blk)
94
+ Reader.new(stream: stream, from: from, frame_size: frame_size).each_chunk(&blk)
95
+ end
96
+ end
97
+
98
+ class Reader
99
+ extend T::Sig
100
+
101
+ ProxyReadProc = T.type_alias do
102
+ T.proc.params(
103
+ data: T.untyped,
104
+ from: Integer,
105
+ eof: T::Boolean,
106
+ next_from: T.nilable(Integer)
107
+ ).void
108
+ end
109
+
110
+ sig do
111
+ params(
112
+ stream: T.any(Proxy, T::Hash[T.untyped, T.untyped], String),
113
+ from: T.nilable(Integer),
114
+ frame_size: T.nilable(Integer)
115
+ ).void
116
+ end
117
+ def initialize(stream:, from: nil, frame_size: nil)
118
+ @stream = T.let(
119
+ stream.is_a?(Hash) && stream[:__stream__] ? from_mock(stream) : stream,
120
+ T.any(Proxy, Mock, T::Hash[T.untyped, T.untyped], String)
121
+ )
122
+ @from = T.let(from || 0, Integer)
123
+ @frame_size = T.let(frame_size || DEFAULT_FRAME_SIZE, Integer)
124
+ end
125
+
126
+ sig { params(_blk: SorbetTypes::StreamInProc).void }
127
+ def each_chunk(&_blk)
128
+ case @stream
129
+ when Proxy, Mock
130
+ @stream.read(from: @from, frame_size: @frame_size) do |chunk, from, eof, next_from|
131
+ yield(chunk, from, calculate_byte_to(chunk, from), eof, next_from)
132
+ end
133
+ when Hash
134
+ chunk = @stream[:data][@from..]
135
+ yield(chunk, @from, calculate_byte_to(chunk, @from), @stream[:eof], nil)
136
+ else
137
+ chunk = @stream[@from..]
138
+ yield(@stream[@from..], @from, calculate_byte_to(chunk, @from), true, nil)
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ sig { params(chunk: T.untyped, from: Integer).returns(T.nilable(Integer)) }
145
+ def calculate_byte_to(chunk, from)
146
+ (chunk_size = chunk.try(:bytesize) || 0).zero? ? nil : from + chunk_size - 1
147
+ end
148
+
149
+ sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T.any(Proxy, Mock)) }
150
+ def from_mock(hash)
151
+ case hash[:chunks]
152
+ when Proc
153
+ Proxy.new(
154
+ name: hash[:name],
155
+ input: HashWithIndifferentAccess.wrap(hash[:input] || {}),
156
+ stream: Stream.new(
157
+ stream: hash[:chunks],
158
+ connection: Connection.new(
159
+ connection: hash[:connection] || {},
160
+ settings: hash[:settings] || {}
161
+ )
162
+ )
163
+ )
164
+ when Hash
165
+ Mock.new(chunks: hash[:chunks])
166
+ else
167
+ raise 'Mock streams with Proc or Hash. Read spec/examples/stream/connector_spec.rb for examples'
168
+ end
169
+ end
170
+ end
171
+
172
+ private_constant :Reader
173
+
174
+ # @api private
175
+ class Proxy
176
+ extend T::Sig
177
+
178
+ sig { params(name: String, input: SorbetTypes::StreamInputHash, stream: Stream).void }
179
+ def initialize(name:, input:, stream:)
180
+ @name = name
181
+ @input = input
182
+ @stream = stream
183
+ end
184
+
185
+ sig { params(_options: T.untyped).returns(T::Hash[String, T.untyped]) }
186
+ def as_json(_options = nil)
187
+ {
188
+ __stream__: true,
189
+ name: name,
190
+ input: input
191
+ }
192
+ end
193
+
194
+ sig { params(from: Integer, frame_size: Integer, _blk: Reader::ProxyReadProc).void }
195
+ def read(from:, frame_size:, &_blk)
196
+ next_from = from
197
+ loop do
198
+ res = read_chunk(next_from, frame_size)
199
+ yield(res.data, res.from, res.eof, res.next_from)
200
+ break if res.eof
201
+
202
+ next_from = T.must(res.next_from)
203
+ end
204
+ end
205
+
206
+ private
207
+
208
+ sig { returns(String) }
209
+ attr_reader :name
210
+
211
+ sig { returns(SorbetTypes::StreamInputHash) }
212
+ attr_reader :input
213
+
214
+ class Chunk < T::Struct
215
+ const :data, T.untyped
216
+ const :from, Integer
217
+ const :eof, T::Boolean
218
+ const :next_from, T.nilable(Integer)
219
+ end
220
+ private_constant :Chunk
221
+
222
+ sig { params(from: Integer, frame_size: Integer).returns(Chunk) }
223
+ def read_chunk(from, frame_size)
224
+ data, eof = @stream.chunk(input, from, from + frame_size - 1, frame_size)
225
+ next_from = from + (data&.length || 0)
226
+ next_from = nil if eof
227
+ Chunk.new(data: data, from: from, eof: eof, next_from: next_from)
228
+ end
229
+ end
230
+
231
+ class Mock
232
+ extend T::Sig
233
+
234
+ sig { params(chunks: T::Hash[T.any(Integer, String), T.untyped]).void }
235
+ def initialize(chunks:)
236
+ @chunks = T.let(chunks.transform_keys(&:to_i), T::Hash[Integer, T.untyped])
237
+ end
238
+
239
+ sig { params(from: Integer, frame_size: Integer, _blk: Reader::ProxyReadProc).void }
240
+ def read(from:, frame_size:, &_blk)
241
+ last_from = chunks.keys.last
242
+ chunks.each do |chunk_from, data|
243
+ next if chunk_from < from
244
+
245
+ eof = chunk_from == last_from
246
+ next_from = eof ? nil : chunk_from + frame_size
247
+
248
+ yield(data, chunk_from, eof, next_from)
249
+ end
250
+ end
251
+
252
+ private
253
+
254
+ sig { returns(T.untyped) }
255
+ attr_reader :chunks
256
+ end
257
+
258
+ private_constant :Mock
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,72 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ using Workato::Extension::HashWithIndifferentAccess
5
+
6
+ module Workato
7
+ module Connector
8
+ module Sdk
9
+ class Streams
10
+ extend T::Sig
11
+
12
+ sig do
13
+ params(
14
+ streams: SorbetTypes::SourceHash,
15
+ methods: SorbetTypes::SourceHash,
16
+ connection: Connection
17
+ ).void
18
+ end
19
+ def initialize(streams: {}, methods: {}, connection: Connection.new)
20
+ @methods_source = methods
21
+ @connection = connection
22
+ @streams = T.let({}, T::Hash[T.any(Symbol, String), Stream])
23
+ @streams_source = streams
24
+ define_action_methods(streams)
25
+ end
26
+
27
+ sig { params(stream: T.any(String, Symbol)).returns(Stream) }
28
+ def [](stream)
29
+ @streams[stream] ||= Stream.new(
30
+ stream: @streams_source.fetch(stream),
31
+ methods: methods_source,
32
+ connection: connection
33
+ )
34
+ end
35
+
36
+ private
37
+
38
+ sig { params(streams_source: SorbetTypes::SourceHash).void }
39
+ def define_action_methods(streams_source)
40
+ streams_source.each do |stream, _stream_proc|
41
+ define_singleton_method(stream) do |input = {}, from = 0, to = nil, frame_size = Stream::DEFAULT_FRAME_SIZE|
42
+ to ||= from + frame_size
43
+ self[stream].chunk(input, from, to, frame_size)
44
+ end
45
+
46
+ define_singleton_method("#{stream}!") do |input = {}, frame_size = Stream::DEFAULT_FRAME_SIZE|
47
+ self[stream].invoke(input, frame_size)
48
+ end
49
+ end
50
+ end
51
+
52
+ sig { returns(SorbetTypes::SourceHash) }
53
+ attr_reader :methods_source
54
+
55
+ sig { returns(Connection) }
56
+ attr_reader :connection
57
+ end
58
+
59
+ # @api private
60
+ class ProhibitedStreams < Streams
61
+ extend T::Sig
62
+
63
+ sig { void }
64
+ def initialize
65
+ @streams = Hash.new do
66
+ raise 'Streams are not available in this context. Access streams in actions or triggers'
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -34,8 +34,8 @@ module Workato
34
34
  attr_reader :paths
35
35
 
36
36
  def above_summarization_limit?(candidate)
37
- candidate.is_a?(::Array) && candidate.length > ARRAY_SUMMARIZATION_LIMIT ||
38
- candidate.is_a?(::String) && candidate.length > STRING_SUMMARIZATION_LIMIT
37
+ (candidate.is_a?(::Array) && candidate.length > ARRAY_SUMMARIZATION_LIMIT) ||
38
+ (candidate.is_a?(::String) && candidate.length > STRING_SUMMARIZATION_LIMIT)
39
39
  end
40
40
 
41
41
  def apply_summarization_limit(summarized, steps)
@@ -57,6 +57,8 @@ module Workato
57
57
  end
58
58
  end
59
59
  end
60
+
61
+ private_constant :Summarize
60
62
  end
61
63
  end
62
64
  end
@@ -34,15 +34,18 @@ module Workato
34
34
  trigger: SorbetTypes::SourceHash,
35
35
  methods: SorbetTypes::SourceHash,
36
36
  connection: Connection,
37
- object_definitions: T.nilable(ObjectDefinitions)
37
+ object_definitions: T.nilable(ObjectDefinitions),
38
+ streams: Streams
38
39
  ).void
39
40
  end
40
- def initialize(trigger:, methods: {}, connection: Connection.new, object_definitions: nil)
41
+ def initialize(trigger:, methods: {}, connection: Connection.new, object_definitions: nil,
42
+ streams: ProhibitedStreams.new)
41
43
  super(
42
44
  operation: trigger,
43
45
  connection: connection,
44
46
  methods: methods,
45
- object_definitions: object_definitions
47
+ object_definitions: object_definitions,
48
+ streams: streams
46
49
  )
47
50
  end
48
51
 
@@ -118,7 +121,7 @@ module Workato
118
121
  headers: T::Hash[T.any(String, Symbol), T.untyped],
119
122
  params: T::Hash[T.any(String, Symbol), T.untyped],
120
123
  settings: T.nilable(SorbetTypes::SettingsHash),
121
- webhook_subscribe_output: SorbetTypes::WebhookSubscribeOutputHash
124
+ webhook_subscribe_output: T.nilable(SorbetTypes::WebhookSubscribeOutputHash)
122
125
  ).returns(
123
126
  SorbetTypes::WebhookNotificationOutputHash
124
127
  )
@@ -134,8 +137,7 @@ module Workato
134
137
  webhook_subscribe_output = {}
135
138
  )
136
139
  connection.merge_settings!(settings) if settings
137
- output = Dsl::WithDsl.execute(
138
- connection,
140
+ output = global_dsl_context.execute(
139
141
  HashWithIndifferentAccess.wrap(input),
140
142
  payload,
141
143
  Array.wrap(extended_input_schema).map { |i| HashWithIndifferentAccess.wrap(i) },
@@ -190,7 +192,7 @@ module Workato
190
192
  payload: T::Hash[T.any(String, Symbol), T.untyped],
191
193
  headers: T::Hash[T.any(String, Symbol), T.untyped],
192
194
  params: T::Hash[T.any(String, Symbol), T.untyped],
193
- webhook_subscribe_output: SorbetTypes::WebhookSubscribeOutputHash
195
+ webhook_subscribe_output: T.nilable(SorbetTypes::WebhookSubscribeOutputHash)
194
196
  ).returns(
195
197
  T.any(SorbetTypes::WebhookNotificationOutputHash, SorbetTypes::PollOutputHash)
196
198
  )
@@ -232,6 +234,11 @@ module Workato
232
234
  def webhook_notification?
233
235
  trigger[:webhook_notification].present?
234
236
  end
237
+
238
+ sig { returns(Dsl::WithDsl) }
239
+ def global_dsl_context
240
+ Dsl::WithDsl.new(connection, streams)
241
+ end
235
242
  end
236
243
  end
237
244
  end
@@ -4,7 +4,7 @@
4
4
  module Workato
5
5
  module Connector
6
6
  module Sdk
7
- VERSION = '1.2.0'
7
+ VERSION = T.let(File.read(File.expand_path('../../../../VERSION', __dir__)).strip, String)
8
8
  end
9
9
  end
10
10
  end
@@ -2,41 +2,14 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'sorbet-runtime'
5
- ::Method.prepend(T::CompatibilityPatches::MethodExtensions)
6
-
7
- module Workato
8
- module Connector
9
- module Sdk
10
- DEFAULT_MASTER_KEY_ENV = 'WORKATO_CONNECTOR_MASTER_KEY'
11
- DEFAULT_MASTER_KEY_PATH = 'master.key'
12
-
13
- DEFAULT_CONNECTOR_PATH = 'connector.rb'
14
-
15
- DEFAULT_SETTINGS_PATH = 'settings.yaml'
16
- DEFAULT_ENCRYPTED_SETTINGS_PATH = 'settings.yaml.enc'
17
-
18
- DEFAULT_ACCOUNT_PROPERTIES_PATH = 'account_properties.yaml'
19
- DEFAULT_ENCRYPTED_ACCOUNT_PROPERTIES_PATH = 'account_properties.yaml.enc'
20
-
21
- DEFAULT_LOOKUP_TABLES_PATH = 'lookup_tables.yaml'
22
-
23
- DEFAULT_TIME_ZONE = 'Pacific Time (US & Canada)'
24
-
25
- DEFAULT_SCHEMAS_PATH = 'workato_schemas.json'
26
-
27
- WORKATO_API_EMAIL_ENV = 'WORKATO_API_EMAIL'
28
- WORKATO_API_TOKEN_ENV = 'WORKATO_API_TOKEN'
29
-
30
- WORKATO_BASE_URL_ENV = 'WORKATO_BASE_URL'
31
- DEFAULT_WORKATO_BASE_URL = 'https://app.workato.com'
32
- WORKATO_BASE_URL = T.let(ENV.fetch(WORKATO_BASE_URL_ENV, DEFAULT_WORKATO_BASE_URL), String)
33
- end
34
- end
35
- end
36
5
 
37
6
  # Global libs and monkey patches
38
7
  require 'active_support/all'
39
8
  require 'active_support/json'
9
+
10
+ require_relative '../types/binary'
11
+ require_relative '../types/unicode_string'
12
+
40
13
  require_relative '../extension/array'
41
14
  require_relative '../extension/case_sensitive_headers'
42
15
  require_relative '../extension/content_encoding_decoder'
@@ -54,18 +27,19 @@ require_relative '../extension/string'
54
27
  require_relative '../extension/symbol'
55
28
  require_relative '../extension/time'
56
29
 
57
- require_relative './sdk/errors'
58
- require_relative './sdk/account_properties'
59
- require_relative './sdk/action'
60
- require_relative './sdk/connection'
61
- require_relative './sdk/connector'
62
- require_relative './sdk/dsl'
63
- require_relative './sdk/lookup_tables'
64
- require_relative './sdk/object_definitions'
65
- require_relative './sdk/operation'
66
- require_relative './sdk/request'
67
- require_relative './sdk/settings'
68
- require_relative './sdk/summarize'
69
- require_relative './sdk/trigger'
70
- require_relative './sdk/version'
71
- require_relative './sdk/workato_schemas'
30
+ require_relative './sdk/core'
31
+
32
+ begin
33
+ tz = ENV.fetch('TZ', nil)
34
+ if tz.present? && tz != 'UTC'
35
+ warn "WARNING: TZ environment variable is set to '#{tz}'. Set TZ=UTC for consistency with Workato platform'"
36
+ else
37
+ ENV['TZ'] = 'UTC'
38
+ end
39
+ Time.zone = Workato::Connector::Sdk::DEFAULT_TIME_ZONE
40
+ rescue TZInfo::DataSourceNotFound
41
+ puts ''
42
+ puts "tzinfo-data is not present. Please install gem 'tzinfo-data' by 'gem install tzinfo-data'"
43
+ puts ''
44
+ exit!
45
+ end
@@ -1,6 +1,8 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'delegate'
5
+
4
6
  module Workato
5
7
  module Extension
6
8
  module Array
@@ -1,7 +1,6 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'rest-client'
5
4
  require 'net/http'
6
5
 
7
6
  module Workato
@@ -1,6 +1,8 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'rest-client'
5
+
4
6
  module Workato
5
7
  module Extension
6
8
  module ContentEncodingDecoder
@@ -0,0 +1,79 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Workato
5
+ module Extension
6
+ module Currency
7
+ class Countries
8
+ extend T::Sig
9
+
10
+ include Singleton
11
+
12
+ class << self
13
+ extend T::Sig
14
+
15
+ sig { params(value: ::String).returns(T.nilable(Country)) }
16
+ def find_country(value)
17
+ instance.find_country(value)
18
+ end
19
+ end
20
+
21
+ class State < T::Struct
22
+ const :name, ::String
23
+ const :names, T::Array[::String]
24
+ end
25
+
26
+ private_constant :State
27
+
28
+ class Country < T::Struct
29
+ const :name, ::String
30
+ const :names, T::Array[::String]
31
+ const :states, T::Hash[::String, State]
32
+ const :alpha2, ::String
33
+ const :alpha3, ::String
34
+ const :number, ::String
35
+ const :currency_code, T.nilable(::String)
36
+ end
37
+
38
+ private_constant :Country
39
+
40
+ def initialize
41
+ @countries = load_countries
42
+ end
43
+
44
+ sig { params(value: ::String).returns(T.nilable(Country)) }
45
+ def find_country(value)
46
+ value = value.upcase
47
+ countries.find do |c|
48
+ c.alpha2 == value || c.alpha3 == value || c.number == value || # already in upper case
49
+ c.name.upcase == value || c.names.any? { |name| name.upcase == value }
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ sig { returns(T::Array[Country]) }
56
+ attr_reader :countries
57
+
58
+ sig { returns(T::Array[Country]) }
59
+ def load_countries
60
+ YAML.load_file(File.expand_path('./countries.yml', __dir__)).map do |data|
61
+ Country.new(
62
+ name: data['name'].freeze,
63
+ names: data['names'].map!(&:freeze).freeze,
64
+ alpha2: data['alpha2'].freeze,
65
+ alpha3: data['alpha3'].freeze,
66
+ number: data['number'].freeze,
67
+ currency_code: data['currency']&.freeze,
68
+ states: data['states'].transform_values do |s|
69
+ State.new(name: s['name'].freeze, names: ::Array.wrap(s['names']).map!(&:freeze).freeze)
70
+ end.freeze
71
+ ).freeze
72
+ end.freeze
73
+ end
74
+ end
75
+
76
+ private_constant :Countries
77
+ end
78
+ end
79
+ end