workato-connector-sdk 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.md +20 -0
- data/README.md +1093 -0
- data/exe/workato +5 -0
- data/lib/workato/cli/edit_command.rb +71 -0
- data/lib/workato/cli/exec_command.rb +191 -0
- data/lib/workato/cli/generate_command.rb +105 -0
- data/lib/workato/cli/generators/connector_generator.rb +94 -0
- data/lib/workato/cli/generators/master_key_generator.rb +56 -0
- data/lib/workato/cli/main.rb +142 -0
- data/lib/workato/cli/push_command.rb +208 -0
- data/lib/workato/connector/sdk/account_properties.rb +60 -0
- data/lib/workato/connector/sdk/action.rb +88 -0
- data/lib/workato/connector/sdk/block_invocation_refinements.rb +30 -0
- data/lib/workato/connector/sdk/connector.rb +230 -0
- data/lib/workato/connector/sdk/dsl/account_property.rb +15 -0
- data/lib/workato/connector/sdk/dsl/call.rb +17 -0
- data/lib/workato/connector/sdk/dsl/error.rb +15 -0
- data/lib/workato/connector/sdk/dsl/http.rb +60 -0
- data/lib/workato/connector/sdk/dsl/lookup_table.rb +15 -0
- data/lib/workato/connector/sdk/dsl/time.rb +21 -0
- data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +105 -0
- data/lib/workato/connector/sdk/dsl/workato_schema.rb +15 -0
- data/lib/workato/connector/sdk/dsl.rb +46 -0
- data/lib/workato/connector/sdk/errors.rb +30 -0
- data/lib/workato/connector/sdk/lookup_tables.rb +62 -0
- data/lib/workato/connector/sdk/object_definitions.rb +74 -0
- data/lib/workato/connector/sdk/operation.rb +217 -0
- data/lib/workato/connector/sdk/request.rb +399 -0
- data/lib/workato/connector/sdk/settings.rb +130 -0
- data/lib/workato/connector/sdk/summarize.rb +61 -0
- data/lib/workato/connector/sdk/trigger.rb +96 -0
- data/lib/workato/connector/sdk/version.rb +9 -0
- data/lib/workato/connector/sdk/workato_schemas.rb +37 -0
- data/lib/workato/connector/sdk/xml.rb +35 -0
- data/lib/workato/connector/sdk.rb +58 -0
- data/lib/workato/extension/array.rb +124 -0
- data/lib/workato/extension/case_sensitive_headers.rb +51 -0
- data/lib/workato/extension/currency.rb +15 -0
- data/lib/workato/extension/date.rb +14 -0
- data/lib/workato/extension/enumerable.rb +55 -0
- data/lib/workato/extension/extra_chain_cert.rb +40 -0
- data/lib/workato/extension/hash.rb +13 -0
- data/lib/workato/extension/integer.rb +17 -0
- data/lib/workato/extension/nil_class.rb +17 -0
- data/lib/workato/extension/object.rb +38 -0
- data/lib/workato/extension/phone.rb +14 -0
- data/lib/workato/extension/string.rb +268 -0
- data/lib/workato/extension/symbol.rb +13 -0
- data/lib/workato/extension/time.rb +13 -0
- data/lib/workato/testing/vcr_encrypted_cassette_serializer.rb +38 -0
- data/lib/workato/testing/vcr_multipart_body_matcher.rb +32 -0
- data/lib/workato-connector-sdk.rb +3 -0
- data/templates/.rspec.erb +3 -0
- data/templates/Gemfile.erb +10 -0
- data/templates/connector.rb.erb +37 -0
- data/templates/spec/action_spec.rb.erb +36 -0
- data/templates/spec/connector_spec.rb.erb +18 -0
- data/templates/spec/method_spec.rb.erb +13 -0
- data/templates/spec/object_definition_spec.rb.erb +18 -0
- data/templates/spec/pick_list_spec.rb.erb +13 -0
- data/templates/spec/spec_helper.rb.erb +38 -0
- data/templates/spec/trigger_spec.rb.erb +61 -0
- 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,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,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
|