workato-connector-sdk 1.2.0 → 1.3.1

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