workato-connector-sdk 0.1.0

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