workato-connector-sdk 0.1.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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +20 -0
  3. data/README.md +1093 -0
  4. data/exe/workato +5 -0
  5. data/lib/workato/cli/edit_command.rb +71 -0
  6. data/lib/workato/cli/exec_command.rb +191 -0
  7. data/lib/workato/cli/generate_command.rb +105 -0
  8. data/lib/workato/cli/generators/connector_generator.rb +94 -0
  9. data/lib/workato/cli/generators/master_key_generator.rb +56 -0
  10. data/lib/workato/cli/main.rb +142 -0
  11. data/lib/workato/cli/push_command.rb +208 -0
  12. data/lib/workato/connector/sdk/account_properties.rb +60 -0
  13. data/lib/workato/connector/sdk/action.rb +88 -0
  14. data/lib/workato/connector/sdk/block_invocation_refinements.rb +30 -0
  15. data/lib/workato/connector/sdk/connector.rb +230 -0
  16. data/lib/workato/connector/sdk/dsl/account_property.rb +15 -0
  17. data/lib/workato/connector/sdk/dsl/call.rb +17 -0
  18. data/lib/workato/connector/sdk/dsl/error.rb +15 -0
  19. data/lib/workato/connector/sdk/dsl/http.rb +60 -0
  20. data/lib/workato/connector/sdk/dsl/lookup_table.rb +15 -0
  21. data/lib/workato/connector/sdk/dsl/time.rb +21 -0
  22. data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +105 -0
  23. data/lib/workato/connector/sdk/dsl/workato_schema.rb +15 -0
  24. data/lib/workato/connector/sdk/dsl.rb +46 -0
  25. data/lib/workato/connector/sdk/errors.rb +30 -0
  26. data/lib/workato/connector/sdk/lookup_tables.rb +62 -0
  27. data/lib/workato/connector/sdk/object_definitions.rb +74 -0
  28. data/lib/workato/connector/sdk/operation.rb +217 -0
  29. data/lib/workato/connector/sdk/request.rb +399 -0
  30. data/lib/workato/connector/sdk/settings.rb +130 -0
  31. data/lib/workato/connector/sdk/summarize.rb +61 -0
  32. data/lib/workato/connector/sdk/trigger.rb +96 -0
  33. data/lib/workato/connector/sdk/version.rb +9 -0
  34. data/lib/workato/connector/sdk/workato_schemas.rb +37 -0
  35. data/lib/workato/connector/sdk/xml.rb +35 -0
  36. data/lib/workato/connector/sdk.rb +58 -0
  37. data/lib/workato/extension/array.rb +124 -0
  38. data/lib/workato/extension/case_sensitive_headers.rb +51 -0
  39. data/lib/workato/extension/currency.rb +15 -0
  40. data/lib/workato/extension/date.rb +14 -0
  41. data/lib/workato/extension/enumerable.rb +55 -0
  42. data/lib/workato/extension/extra_chain_cert.rb +40 -0
  43. data/lib/workato/extension/hash.rb +13 -0
  44. data/lib/workato/extension/integer.rb +17 -0
  45. data/lib/workato/extension/nil_class.rb +17 -0
  46. data/lib/workato/extension/object.rb +38 -0
  47. data/lib/workato/extension/phone.rb +14 -0
  48. data/lib/workato/extension/string.rb +268 -0
  49. data/lib/workato/extension/symbol.rb +13 -0
  50. data/lib/workato/extension/time.rb +13 -0
  51. data/lib/workato/testing/vcr_encrypted_cassette_serializer.rb +38 -0
  52. data/lib/workato/testing/vcr_multipart_body_matcher.rb +32 -0
  53. data/lib/workato-connector-sdk.rb +3 -0
  54. data/templates/.rspec.erb +3 -0
  55. data/templates/Gemfile.erb +10 -0
  56. data/templates/connector.rb.erb +37 -0
  57. data/templates/spec/action_spec.rb.erb +36 -0
  58. data/templates/spec/connector_spec.rb.erb +18 -0
  59. data/templates/spec/method_spec.rb.erb +13 -0
  60. data/templates/spec/object_definition_spec.rb.erb +18 -0
  61. data/templates/spec/pick_list_spec.rb.erb +13 -0
  62. data/templates/spec/spec_helper.rb.erb +38 -0
  63. data/templates/spec/trigger_spec.rb.erb +61 -0
  64. metadata +372 -0
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './operation'
4
+ require_relative './block_invocation_refinements'
5
+
6
+ module Workato
7
+ module Connector
8
+ module Sdk
9
+ class Action < Operation
10
+ using BlockInvocationRefinements
11
+
12
+ RETRY_DEFAULT_CODES = [429, 500, 502, 503, 504, 507].freeze
13
+ RETRY_DEFAULT_METHODS = %i[get head].freeze
14
+ RETRY_DELAY = 5.seconds
15
+ MAX_RETRIES = 3
16
+
17
+ def initialize(action:, connection: {}, methods: {}, settings: {}, object_definitions: nil)
18
+ super(
19
+ operation: action,
20
+ connection: connection,
21
+ methods: methods,
22
+ settings: settings,
23
+ object_definitions: object_definitions
24
+ )
25
+
26
+ initialize_retry
27
+ end
28
+
29
+ def execute(settings = nil, input = {}, extended_input_schema = [], extended_output_schema = [], &block)
30
+ raise InvalidDefinitionError, "'execute' block is required for action" unless block || action[:execute]
31
+
32
+ super(settings, input, extended_input_schema, extended_output_schema, &(block || action[:execute]))
33
+ rescue RequestError => e
34
+ raise e unless retry?(e)
35
+
36
+ @retries_left -= 1
37
+ sleep(RETRY_DELAY) && retry
38
+ end
39
+
40
+ def checkpoint!(continue:, temp_output: nil)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def reinvoke_after(seconds:, continue:, temp_output: nil)
45
+ raise NotImplementedError
46
+ end
47
+
48
+ private
49
+
50
+ def retry_on_response
51
+ Array(action[:retry_on_response])
52
+ end
53
+
54
+ def retry_on_request
55
+ Array(action[:retry_on_request])
56
+ end
57
+
58
+ def max_retries
59
+ action[:max_retries]
60
+ end
61
+
62
+ def initialize_retry
63
+ @retries_left = 0
64
+ return if retry_on_response.blank?
65
+
66
+ @retry_codes = []
67
+ @retry_matchers = []
68
+ retry_on_response.each { |m| m.is_a?(::Integer) ? @retry_codes << m : @retry_matchers << m }
69
+ @retry_codes = RETRY_DEFAULT_CODES if @retry_codes.empty?
70
+ @retry_methods = (retry_on_request.presence || RETRY_DEFAULT_METHODS).map(&:to_s).map(&:downcase)
71
+ @retries_left = [[max_retries.is_a?(::Integer) && max_retries || MAX_RETRIES, MAX_RETRIES].min, 0].max
72
+ end
73
+
74
+ def retry?(exception)
75
+ return unless @retries_left.positive?
76
+ return unless @retry_codes.include?(exception.code.to_i)
77
+ return unless @retry_matchers.empty? || @retry_matchers.any? do |m|
78
+ m === exception.message || m === exception.response
79
+ end
80
+
81
+ @retry_methods.include?(exception.method.to_s.downcase)
82
+ end
83
+
84
+ alias action operation
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Workato
4
+ module Connector
5
+ module Sdk
6
+ # match proc's arguments, even if it's a lambda.
7
+ module BlockInvocationRefinements
8
+ module CallRefinement
9
+ def call(*args, &block)
10
+ super(*args.take(parameters.length), &block)
11
+ end
12
+ end
13
+
14
+ refine Proc do
15
+ prepend CallRefinement
16
+ end
17
+
18
+ module InstanceExecRefinement
19
+ def instance_exec(*args, &block)
20
+ super(*args.take(block.parameters.length), &block)
21
+ end
22
+ end
23
+
24
+ refine BasicObject do
25
+ prepend InstanceExecRefinement
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Workato
4
+ module Connector
5
+ module Sdk
6
+ class Connector
7
+ attr_reader :source
8
+
9
+ def self.from_file(path_to_source_code = DEFAULT_CONNECTOR_PATH, settings = {})
10
+ new(eval(File.read(path_to_source_code), binding, path_to_source_code), settings) # rubocop:disable Security/Eval
11
+ end
12
+
13
+ def initialize(definition, settings = {})
14
+ @source = definition.with_indifferent_access
15
+ @settings = settings.with_indifferent_access
16
+ @connection_source = @source[:connection] || {}
17
+ @methods_source = @source[:methods] || {}
18
+ end
19
+
20
+ def title
21
+ @source[:title]
22
+ end
23
+
24
+ def actions
25
+ @actions ||= ActionsProxy.new(
26
+ actions: source[:actions].presence || {},
27
+ object_definitions: object_definitions,
28
+ methods: methods_source,
29
+ connection: connection_source,
30
+ settings: settings
31
+ )
32
+ end
33
+
34
+ def methods
35
+ @methods ||= MethodsProxy.new(
36
+ methods: methods_source,
37
+ connection: connection_source,
38
+ settings: settings
39
+ )
40
+ end
41
+
42
+ def test(settings = nil)
43
+ @test ||= Action.new(
44
+ action: {
45
+ execute: source[:test]
46
+ },
47
+ methods: methods_source,
48
+ connection: connection_source,
49
+ settings: send(:settings)
50
+ ).execute(settings)
51
+ end
52
+
53
+ def triggers
54
+ @triggers ||= TriggersProxy.new(
55
+ triggers: source[:triggers].presence || {},
56
+ object_definitions: object_definitions,
57
+ methods: methods_source,
58
+ connection: connection_source,
59
+ settings: settings
60
+ )
61
+ end
62
+
63
+ def object_definitions
64
+ @object_definitions ||= ObjectDefinitions.new(
65
+ object_definitions: source[:object_definitions].presence || {},
66
+ methods: methods_source,
67
+ connection: connection_source,
68
+ settings: settings
69
+ )
70
+ end
71
+
72
+ def pick_lists
73
+ @pick_lists ||= PickListsProxy.new(
74
+ pick_lists: source[:pick_lists].presence || {},
75
+ methods: methods_source,
76
+ connection: connection_source,
77
+ settings: settings
78
+ )
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :methods_source,
84
+ :connection_source,
85
+ :settings
86
+ end
87
+
88
+ class ActionsProxy
89
+ def initialize(actions:, object_definitions:, methods:, connection:, settings:)
90
+ @methods = methods
91
+ @connection = connection
92
+ @object_definitions = object_definitions
93
+ @settings = settings
94
+ define_action_methods(actions)
95
+ end
96
+
97
+ def [](action)
98
+ public_send(action)
99
+ end
100
+
101
+ private
102
+
103
+ attr_reader :methods,
104
+ :connection,
105
+ :object_definitions,
106
+ :settings
107
+
108
+ def define_action_methods(actions)
109
+ actions.each do |action, definition|
110
+ define_singleton_method(action) do
111
+ @actions ||= {}
112
+ @actions[action] ||= Action.new(
113
+ action: definition,
114
+ object_definitions: object_definitions,
115
+ methods: methods,
116
+ connection: connection,
117
+ settings: settings
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ class MethodsProxy
125
+ def initialize(methods:, connection:, settings:)
126
+ @methods = methods
127
+ @connection = connection
128
+ @settings = settings
129
+ define_action_methods
130
+ end
131
+
132
+ private
133
+
134
+ attr_reader :methods,
135
+ :connection,
136
+ :settings
137
+
138
+ def define_action_methods
139
+ methods.each do |method, _definition|
140
+ define_singleton_method(method) do |*args|
141
+ @actions ||= {}
142
+ @actions[method] ||= Action.new(
143
+ action: {
144
+ execute: -> { call(method, *args) }
145
+ },
146
+ methods: methods,
147
+ connection: connection,
148
+ settings: settings
149
+ )
150
+ @actions[method].execute
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ class PickListsProxy
157
+ def initialize(pick_lists:, methods:, connection:, settings:)
158
+ @methods = methods
159
+ @connection = connection
160
+ @settings = settings
161
+ define_action_methods(pick_lists)
162
+ end
163
+
164
+ private
165
+
166
+ attr_reader :methods,
167
+ :connection,
168
+ :settings
169
+
170
+ def define_action_methods(pick_lists)
171
+ pick_lists.each do |pick_list, pick_list_proc|
172
+ define_singleton_method(pick_list) do |settings = nil, args = {}|
173
+ @actions ||= {}
174
+ @actions[pick_list] ||= Action.new(
175
+ action: {
176
+ execute: lambda do |connection, input|
177
+ case pick_list_proc.parameters.length
178
+ when 0
179
+ instance_exec(&pick_list_proc)
180
+ when 1
181
+ instance_exec(connection, &pick_list_proc)
182
+ else
183
+ instance_exec(connection, **input.symbolize_keys, &pick_list_proc)
184
+ end
185
+ end
186
+ },
187
+ methods: methods,
188
+ connection: connection,
189
+ settings: send(:settings)
190
+ )
191
+ @actions[pick_list].execute(settings, args)
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ class TriggersProxy
198
+ def initialize(triggers:, object_definitions:, methods:, connection:, settings:)
199
+ @methods = methods
200
+ @connection = connection
201
+ @object_definitions = object_definitions
202
+ @settings = settings
203
+ @triggers = {}
204
+ define_trigger_methods(triggers)
205
+ end
206
+
207
+ private
208
+
209
+ attr_reader :methods,
210
+ :connection,
211
+ :object_definitions,
212
+ :settings
213
+
214
+ def define_trigger_methods(triggers)
215
+ triggers.each do |trigger, definition|
216
+ define_singleton_method(trigger) do
217
+ @triggers[trigger] ||= Trigger.new(
218
+ trigger: definition,
219
+ object_definitions: object_definitions,
220
+ methods: methods,
221
+ connection: connection,
222
+ settings: settings
223
+ )
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Workato
4
+ module Connector
5
+ module Sdk
6
+ module Dsl
7
+ module AccountProperty
8
+ def account_property(name)
9
+ AccountProperties.get(name)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Workato
4
+ module Connector
5
+ module Sdk
6
+ module Dsl
7
+ module Call
8
+ def call(method, *args)
9
+ raise InvalidDefinitionError, "method '#{method}' does not exists" unless @_methods[method]
10
+
11
+ instance_exec(*args, &@_methods[method])
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Workato
4
+ module Connector
5
+ module Sdk
6
+ module Dsl
7
+ module Error
8
+ def error(message)
9
+ raise message
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Workato
4
+ module Connector
5
+ module Sdk
6
+ module Dsl
7
+ # https://docs.workato.com/developing-connectors/sdk/sdk-reference/http.html#http-methods
8
+ module HTTP
9
+ def get(url, params = {})
10
+ http_request(url, method: 'GET').params(params).response_format_json
11
+ end
12
+
13
+ def options(url, params = {})
14
+ http_request(url, method: 'OPTIONS').params(params).response_format_json
15
+ end
16
+
17
+ def head(url, params = {})
18
+ http_request(url, method: 'HEAD').params(params).response_format_json
19
+ end
20
+
21
+ def post(url, payload = nil)
22
+ http_request(url, method: 'POST').payload(payload).format_json
23
+ end
24
+
25
+ def patch(url, payload = nil)
26
+ http_request(url, method: 'PATCH').payload(payload).format_json
27
+ end
28
+
29
+ def put(url, payload = nil)
30
+ http_request(url, method: 'PUT').payload(payload).format_json
31
+ end
32
+
33
+ def delete(url, payload = nil)
34
+ http_request(url, method: 'DELETE').payload(payload).format_json
35
+ end
36
+
37
+ def copy(url, payload = nil)
38
+ http_request(url, method: 'COPY').payload(payload).format_json
39
+ end
40
+
41
+ def move(url, payload = nil)
42
+ http_request(url, method: 'MOVE').payload(payload).format_json
43
+ end
44
+
45
+ private
46
+
47
+ def http_request(url, method:)
48
+ Request.new(
49
+ url,
50
+ method: method,
51
+ connection: connection,
52
+ settings: settings,
53
+ action: self
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end