workato-connector-sdk 1.0.2 → 1.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.
- checksums.yaml +5 -5
- data/README.md +17 -11
- data/lib/workato/cli/exec_command.rb +3 -0
- data/lib/workato/cli/main.rb +43 -11
- data/lib/workato/cli/push_command.rb +3 -1
- data/lib/workato/cli/schema_command.rb +2 -4
- data/lib/workato/connector/sdk/connection.rb +2 -0
- data/lib/workato/connector/sdk/dsl/call.rb +5 -2
- data/lib/workato/connector/sdk/errors.rb +58 -1
- data/lib/workato/connector/sdk/object_definitions.rb +16 -10
- data/lib/workato/connector/sdk/request.rb +50 -14
- data/lib/workato/connector/sdk/trigger.rb +34 -12
- data/lib/workato/connector/sdk/version.rb +1 -1
- data/lib/workato/connector/sdk.rb +4 -0
- data/lib/workato/extension/currency.rb +1 -1
- data/lib/workato/extension/phone.rb +1 -1
- data/lib/workato/extension/string.rb +4 -1
- data/lib/workato/utilities/encoding.rb +57 -0
- metadata +32 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 053e48199f2b1406ef47a84fb43d7b7d0cc74d74ce0f1f1b650c01fc422bc683
|
4
|
+
data.tar.gz: e2155ca61c45230234b105ff0f4ab033205c86b49b35838d88e8a8e473a957b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d34393bda1fc5eef5e138c7e742a2a05a7c1ced8fb5a4a8969a1770473c4534cf015c7096cf17a18eb69c669301ca8761ff51a88b3ee8a04ad4550467a23588
|
7
|
+
data.tar.gz: b293f65e21f45ef2bab573d687cd12cd77981a7375bec619ce45561ead9e96f895f633e65910dad7226213daacb38b95453c890b96e7b7a957b5085e5055ff94
|
data/README.md
CHANGED
@@ -248,8 +248,9 @@ Usage:
|
|
248
248
|
workato edit <PATH>
|
249
249
|
|
250
250
|
Options:
|
251
|
-
-k, [--key=KEY] # Path to file with encrypt/decrypt key.
|
252
|
-
|
251
|
+
-k, [--key=KEY] # Path to file with encrypt/decrypt key.
|
252
|
+
# NOTE: key from WORKATO_CONNECTOR_MASTER_KEY has higher priority
|
253
|
+
[--verbose], [--no-verbose]
|
253
254
|
|
254
255
|
Edit encrypted file, e.g. settings.yaml.enc
|
255
256
|
```
|
@@ -261,6 +262,7 @@ Edit encrypted file, e.g. settings.yaml.enc
|
|
261
262
|
### 3.3 workato exec
|
262
263
|
```
|
263
264
|
workato help exec
|
265
|
+
|
264
266
|
Usage:
|
265
267
|
workato exec <PATH>
|
266
268
|
|
@@ -268,7 +270,8 @@ Options:
|
|
268
270
|
-c, [--connector=CONNECTOR] # Path to connector source code
|
269
271
|
-s, [--settings=SETTINGS] # Path to plain or encrypted file with connection configs, passwords, tokens, secrets etc
|
270
272
|
-n, [--connection=CONNECTION] # Connection name if settings file contains multiple settings
|
271
|
-
-k, [--key=KEY] # Path to file with encrypt/decrypt key.
|
273
|
+
-k, [--key=KEY] # Path to file with encrypt/decrypt key.
|
274
|
+
# NOTE: key from WORKATO_CONNECTOR_MASTER_KEY has higher priority
|
272
275
|
-i, [--input=INPUT] # Path to file with input JSON
|
273
276
|
[--closure=CLOSURE] # Path to file with next poll closure JSON
|
274
277
|
[--continue=CONTINUE] # Path to file with next multistep action continue closure JSON
|
@@ -286,7 +289,7 @@ Options:
|
|
286
289
|
[--redirect-url=REDIRECT_URL] # OAuth2 callback url
|
287
290
|
[--refresh-token=REFRESH_TOKEN] # OAuth2 refresh token
|
288
291
|
[--debug], [--no-debug]
|
289
|
-
[--verbose], [--no-verbose]
|
292
|
+
[--verbose], [--no-verbose]
|
290
293
|
|
291
294
|
Description:
|
292
295
|
The 'workato exec' executes connector's lambda block at <PATH>. Lambda's parameters can be provided if needed, see options part.
|
@@ -394,13 +397,14 @@ Options:
|
|
394
397
|
-c, [--connector=CONNECTOR] # Path to connector source code
|
395
398
|
-s, [--settings=SETTINGS] # Path to plain or encrypted file with connection configs, passwords, tokens, secrets etc
|
396
399
|
-n, [--connection=CONNECTION] # Connection name if settings file contains multiple settings
|
397
|
-
-k, [--key=KEY] # Path to file with encrypt/decrypt key.
|
400
|
+
-k, [--key=KEY] # Path to file with encrypt/decrypt key.
|
401
|
+
# NOTE: key from WORKATO_CONNECTOR_MASTER_KEY has higher priority
|
398
402
|
[--port=PORT] # Listen requests on specific port
|
399
403
|
# Default: 45555
|
400
404
|
[--ip=IP] # Listen requests on specific interface
|
401
405
|
# Default: 127.0.0.1
|
402
406
|
[--https], [--no-https] # Start HTTPS server using self-signed certificate
|
403
|
-
[--verbose], [--no-verbose]
|
407
|
+
[--verbose], [--no-verbose]
|
404
408
|
|
405
409
|
Implements OAuth Authorization Code flow
|
406
410
|
```
|
@@ -420,11 +424,13 @@ Options:
|
|
420
424
|
-l, [--logo=LOGO] # Path to connector logo: png or jpeg file
|
421
425
|
-n, [--notes=NOTES] # Release notes
|
422
426
|
-c, [--connector=CONNECTOR] # Path to connector source code
|
423
|
-
[--api-email=API_EMAIL] # Email for accessing Workato API
|
424
|
-
|
425
|
-
[--
|
426
|
-
#
|
427
|
-
|
427
|
+
[--api-email=API_EMAIL] # Email for accessing Workato API.
|
428
|
+
# If present overrides value from WORKATO_API_EMAIL environment variable.
|
429
|
+
[--api-token=API_TOKEN] # Token for accessing Workato API.
|
430
|
+
# If present overrides value from WORKATO_API_TOKEN environment variable.
|
431
|
+
[--environment=ENVIRONMENT] # Data center specific URL to push connector code.
|
432
|
+
# If present overrides value from WORKATO_BASE_URL environment variable.
|
433
|
+
# Examples: 'https://app.workato.com', 'https://app.eu.workato.com'
|
428
434
|
[--folder=FOLDER] # Folder ID if you what to push to folder other than Home
|
429
435
|
[--verbose], [--no-verbose]
|
430
436
|
|
@@ -89,7 +89,10 @@ module Workato
|
|
89
89
|
$stdout.pause if verbose?
|
90
90
|
say('')
|
91
91
|
say(message)
|
92
|
+
|
92
93
|
new_settings = refresher.call
|
94
|
+
break unless new_settings
|
95
|
+
|
93
96
|
loop do
|
94
97
|
answer = ask('Updated settings file with new connection attributes? (Yes or No)').to_s.downcase
|
95
98
|
break new_settings if %w[n no].include?(answer)
|
data/lib/workato/cli/main.rb
CHANGED
@@ -41,8 +41,8 @@ module Workato
|
|
41
41
|
desc: 'Connection name if settings file contains multiple settings'
|
42
42
|
method_option :key, type: :string, aliases: '-k',
|
43
43
|
lazy_default: Workato::Connector::Sdk::DEFAULT_MASTER_KEY_PATH,
|
44
|
-
desc:
|
45
|
-
|
44
|
+
desc: "Path to file with encrypt/decrypt key.\n"\
|
45
|
+
"NOTE: key from #{Workato::Connector::Sdk::DEFAULT_MASTER_KEY_ENV} has higher priority"
|
46
46
|
method_option :input, type: :string, aliases: '-i', desc: 'Path to file with input JSON'
|
47
47
|
method_option :closure, type: :string, desc: 'Path to file with next poll closure JSON'
|
48
48
|
method_option :continue, type: :string, desc: 'Path to file with next multistep action continue closure JSON'
|
@@ -75,7 +75,7 @@ module Workato
|
|
75
75
|
|
76
76
|
method_option :key, type: :string, aliases: '-k',
|
77
77
|
lazy_default: Workato::Connector::Sdk::DEFAULT_MASTER_KEY_PATH,
|
78
|
-
desc:
|
78
|
+
desc: "Path to file with encrypt/decrypt key.\n"\
|
79
79
|
"NOTE: key from #{Workato::Connector::Sdk::DEFAULT_MASTER_KEY_ENV} has higher priority"
|
80
80
|
|
81
81
|
def edit(path)
|
@@ -127,17 +127,20 @@ module Workato
|
|
127
127
|
lazy_default: Workato::Connector::Sdk::DEFAULT_CONNECTOR_PATH
|
128
128
|
method_option :api_email,
|
129
129
|
type: :string,
|
130
|
-
desc:
|
131
|
-
"
|
130
|
+
desc: "Email for accessing Workato API.\n"\
|
131
|
+
"If present overrides value from #{Workato::Connector::Sdk::WORKATO_API_EMAIL_ENV} "\
|
132
|
+
'environment variable.'
|
132
133
|
method_option :api_token,
|
133
134
|
type: :string,
|
134
|
-
desc:
|
135
|
-
"
|
135
|
+
desc: "Token for accessing Workato API.\n" \
|
136
|
+
"If present overrides value from #{Workato::Connector::Sdk::WORKATO_API_TOKEN_ENV} "\
|
137
|
+
'environment variable.'
|
136
138
|
method_option :environment,
|
137
139
|
type: :string,
|
138
|
-
|
139
|
-
|
140
|
-
|
140
|
+
desc: "Data center specific URL to push connector code.\n"\
|
141
|
+
"If present overrides value from #{Workato::Connector::Sdk::WORKATO_BASE_URL_ENV} "\
|
142
|
+
"environment variable.\n"\
|
143
|
+
"Examples: 'https://app.workato.com', 'https://app.eu.workato.com'"
|
141
144
|
method_option :folder,
|
142
145
|
type: :string,
|
143
146
|
desc: 'Folder ID if you what to push to folder other than Home'
|
@@ -168,7 +171,7 @@ module Workato
|
|
168
171
|
type: :string,
|
169
172
|
aliases: '-k',
|
170
173
|
lazy_default: Workato::Connector::Sdk::DEFAULT_MASTER_KEY_PATH,
|
171
|
-
desc:
|
174
|
+
desc: "Path to file with encrypt/decrypt key.\n"\
|
172
175
|
"NOTE: key from #{Workato::Connector::Sdk::DEFAULT_MASTER_KEY_ENV} has higher priority"
|
173
176
|
method_option :port,
|
174
177
|
type: :string,
|
@@ -187,6 +190,35 @@ module Workato
|
|
187
190
|
options: options
|
188
191
|
).call
|
189
192
|
end
|
193
|
+
|
194
|
+
class << self
|
195
|
+
def print_options(shell, options, group_name = nil)
|
196
|
+
return if options.empty?
|
197
|
+
|
198
|
+
list = []
|
199
|
+
padding = options.map { |o| o.aliases.size }.max.to_i * 4
|
200
|
+
|
201
|
+
options.each do |option|
|
202
|
+
next if option.hide
|
203
|
+
|
204
|
+
description = []
|
205
|
+
description_lines = option.description ? option.description.split("\n") : []
|
206
|
+
first_line = description_lines.shift
|
207
|
+
description << [option.usage(padding), first_line ? "# #{first_line}" : '']
|
208
|
+
description_lines.each do |line|
|
209
|
+
description << ['', "# #{line}"]
|
210
|
+
end
|
211
|
+
|
212
|
+
list.concat(description)
|
213
|
+
list << ['', "# Default: #{option.default}"] if option.show_default?
|
214
|
+
list << ['', "# Possible values: #{option.enum.join(', ')}"] if option.enum
|
215
|
+
end
|
216
|
+
|
217
|
+
shell.say(group_name ? "#{group_name} options:" : 'Options:')
|
218
|
+
shell.print_table(list, indent: 2)
|
219
|
+
shell.say ''
|
220
|
+
end
|
221
|
+
end
|
190
222
|
end
|
191
223
|
end
|
192
224
|
end
|
@@ -32,7 +32,9 @@ module Workato
|
|
32
32
|
|
33
33
|
def initialize(options:)
|
34
34
|
@options = options
|
35
|
-
@api_base_url = ENVIRONMENTS.fetch(options[:environment])
|
35
|
+
@api_base_url = ENVIRONMENTS.fetch(options[:environment]) do
|
36
|
+
options[:environment].presence || Workato::Connector::Sdk::WORKATO_BASE_URL
|
37
|
+
end
|
36
38
|
@api_email = options[:api_email] || ENV[Workato::Connector::Sdk::WORKATO_API_EMAIL_ENV]
|
37
39
|
@api_token = options[:api_token] || ENV[Workato::Connector::Sdk::WORKATO_API_TOKEN_ENV]
|
38
40
|
@folder_id = options[:folder]
|
@@ -9,7 +9,6 @@ module Workato
|
|
9
9
|
SAMPLE_TO_SCHEMA_SUPPORT_TYPES = %w[csv json].freeze
|
10
10
|
CSV_SEPARATORS = %w[comma space tab colon semicolon pipe].freeze
|
11
11
|
|
12
|
-
WORKATO_BASE_URL = ENV['WORKATO_BASE_URL'] || 'https://app.workato.com'
|
13
12
|
API_GENERATE_SCHEMA_PATH = '/api/sdk/generate_schema'
|
14
13
|
|
15
14
|
def initialize(options:)
|
@@ -53,7 +52,7 @@ module Workato
|
|
53
52
|
end
|
54
53
|
|
55
54
|
def sample_to_schema(sample)
|
56
|
-
url = "#{WORKATO_BASE_URL}#{API_GENERATE_SCHEMA_PATH}/#{sample.delete(:type)}"
|
55
|
+
url = "#{Workato::Connector::Sdk::WORKATO_BASE_URL}#{API_GENERATE_SCHEMA_PATH}/#{sample.delete(:type)}"
|
57
56
|
response = RestClient.post(
|
58
57
|
url,
|
59
58
|
sample.to_json,
|
@@ -75,8 +74,7 @@ module Workato
|
|
75
74
|
}
|
76
75
|
end
|
77
76
|
|
78
|
-
private_constant :API_GENERATE_SCHEMA_PATH
|
79
|
-
:WORKATO_BASE_URL
|
77
|
+
private_constant :API_GENERATE_SCHEMA_PATH
|
80
78
|
end
|
81
79
|
end
|
82
80
|
end
|
@@ -202,6 +202,8 @@ module Workato
|
|
202
202
|
).returns(T::Boolean)
|
203
203
|
end
|
204
204
|
def refresh?(http_code, http_body, exception)
|
205
|
+
return false unless /oauth2/i =~ type || source[:acquire].present?
|
206
|
+
|
205
207
|
refresh_on = self.refresh_on
|
206
208
|
refresh_on.blank? || refresh_on.any? do |pattern|
|
207
209
|
pattern.is_a?(::Integer) && pattern == http_code ||
|
@@ -7,9 +7,12 @@ module Workato
|
|
7
7
|
module Dsl
|
8
8
|
module Call
|
9
9
|
def call(method, *args)
|
10
|
-
|
10
|
+
method_proc = @_methods[method]
|
11
11
|
|
12
|
-
|
12
|
+
raise UndefinedMethodError, method unless method_proc
|
13
|
+
raise UnexpectedMethodDefinitionError.new(method, method_proc) unless method_proc.is_a?(Proc)
|
14
|
+
|
15
|
+
instance_exec(*args, &method_proc)
|
13
16
|
end
|
14
17
|
end
|
15
18
|
end
|
@@ -6,12 +6,51 @@ module Workato
|
|
6
6
|
module Sdk
|
7
7
|
InvalidDefinitionError = Class.new(StandardError)
|
8
8
|
|
9
|
-
|
9
|
+
class UnexpectedMethodDefinitionError < InvalidDefinitionError
|
10
|
+
attr_reader :name
|
11
|
+
attr_reader :definition
|
12
|
+
|
13
|
+
def initialize(name, definition)
|
14
|
+
super("Expected lambda for method '#{name}' definition, got: #{definition.class.name}")
|
15
|
+
@name = name
|
16
|
+
@definition = definition
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class UndefinedMethodError < InvalidDefinitionError
|
21
|
+
attr_reader :name
|
22
|
+
|
23
|
+
def initialize(name)
|
24
|
+
super("Method '#{name}' does not exists")
|
25
|
+
@name = name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
InvalidSchemaError = Class.new(InvalidDefinitionError)
|
10
30
|
|
11
31
|
CustomRequestError = Class.new(StandardError)
|
12
32
|
|
13
33
|
RuntimeError = Class.new(StandardError)
|
14
34
|
|
35
|
+
class UnresolvedObjectDefinitionError < StandardError
|
36
|
+
attr_reader :name
|
37
|
+
|
38
|
+
def initialize(name)
|
39
|
+
super("Cannot find object definition for '#{name}'")
|
40
|
+
@name = name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class CircleReferenceObjectDefinitionError < StandardError
|
45
|
+
attr_reader :name
|
46
|
+
|
47
|
+
def initialize(name, backtrace = [])
|
48
|
+
super("Infinite recursion occurred in object definition for '#{name}'")
|
49
|
+
set_backtrace(backtrace)
|
50
|
+
@name = name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
15
54
|
class RequestError < StandardError
|
16
55
|
attr_reader :method
|
17
56
|
attr_reader :code
|
@@ -41,6 +80,24 @@ module Workato
|
|
41
80
|
super(message)
|
42
81
|
end
|
43
82
|
end
|
83
|
+
|
84
|
+
RequestTLSCertificateFormatError = Class.new(StandardError)
|
85
|
+
|
86
|
+
RequestPayloadFormatError = Class.new(StandardError)
|
87
|
+
|
88
|
+
JSONRequestFormatError = Class.new(RequestPayloadFormatError)
|
89
|
+
|
90
|
+
JSONResponseFormatError = Class.new(RequestPayloadFormatError)
|
91
|
+
|
92
|
+
XMLRequestFormatError = Class.new(RequestPayloadFormatError)
|
93
|
+
|
94
|
+
XMLResponseFormatError = Class.new(RequestPayloadFormatError)
|
95
|
+
|
96
|
+
WWWFormURLEncodedRequestFormatError = Class.new(RequestPayloadFormatError)
|
97
|
+
|
98
|
+
MultipartFormRequestFormatError = Class.new(RequestPayloadFormatError)
|
99
|
+
|
100
|
+
RAWResponseFormatError = Class.new(RequestPayloadFormatError)
|
44
101
|
end
|
45
102
|
end
|
46
103
|
end
|
@@ -18,16 +18,22 @@ module Workato
|
|
18
18
|
|
19
19
|
def lazy(settings = nil, config_fields = {})
|
20
20
|
DupHashWithIndifferentAccess.new do |object_definitions, name|
|
21
|
-
fields_proc = object_definitions_source
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
fields_proc = object_definitions_source.dig(name, :fields)
|
22
|
+
raise Workato::Connector::Sdk::UnresolvedObjectDefinitionError, name unless fields_proc
|
23
|
+
|
24
|
+
begin
|
25
|
+
object_definitions[name] = Action.new(
|
26
|
+
action: {
|
27
|
+
execute: lambda do |connection, input|
|
28
|
+
instance_exec(connection, input, object_definitions, &fields_proc)
|
29
|
+
end
|
30
|
+
},
|
31
|
+
methods: methods_source,
|
32
|
+
connection: connection
|
33
|
+
).execute(settings, config_fields)
|
34
|
+
rescue SystemStackError => e
|
35
|
+
raise Workato::Connector::Sdk::CircleReferenceObjectDefinitionError.new(name, e.backtrace)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
33
39
|
|
@@ -9,6 +9,7 @@ require 'net/http'
|
|
9
9
|
require 'net/http/digest_auth'
|
10
10
|
|
11
11
|
require_relative './block_invocation_refinements'
|
12
|
+
require_relative './../../utilities/encoding'
|
12
13
|
|
13
14
|
module Workato
|
14
15
|
module Connector
|
@@ -27,7 +28,6 @@ module Workato
|
|
27
28
|
@action = action
|
28
29
|
@headers = {}
|
29
30
|
@case_sensitive_headers = {}
|
30
|
-
@params = {}.with_indifferent_access
|
31
31
|
@render_request = ->(payload) { payload }
|
32
32
|
@parse_response = ->(payload) { payload }
|
33
33
|
@after_response = ->(_response_code, parsed_response, _response_headers) { parsed_response }
|
@@ -58,7 +58,7 @@ module Workato
|
|
58
58
|
detect_error!(response.body)
|
59
59
|
parsed_response = @parse_response.call(response)
|
60
60
|
detect_error!(parsed_response)
|
61
|
-
|
61
|
+
apply_after_response(response.code, parsed_response, response.headers)
|
62
62
|
end
|
63
63
|
)
|
64
64
|
rescue RestClient::Exception => e
|
@@ -81,7 +81,12 @@ module Workato
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def params(params)
|
84
|
-
|
84
|
+
if params.is_a?(Hash)
|
85
|
+
@params ||= HashWithIndifferentAccess.new
|
86
|
+
@params.merge!(params)
|
87
|
+
else
|
88
|
+
@params = params
|
89
|
+
end
|
85
90
|
self
|
86
91
|
end
|
87
92
|
|
@@ -142,13 +147,17 @@ module Workato
|
|
142
147
|
|
143
148
|
def request_format_json
|
144
149
|
@content_type_header = :json
|
145
|
-
@render_request =
|
150
|
+
@render_request = lambda_with_error_wrap(JSONRequestFormatError) do |payload|
|
151
|
+
ActiveSupport::JSON.encode(payload) if payload
|
152
|
+
end
|
146
153
|
self
|
147
154
|
end
|
148
155
|
|
149
156
|
def response_format_json
|
150
157
|
@accept_header = :json
|
151
|
-
@parse_response =
|
158
|
+
@parse_response = lambda_with_error_wrap(JSONResponseFormatError) do |payload|
|
159
|
+
ActiveSupport::JSON.decode(payload.presence || '{}')
|
160
|
+
end
|
152
161
|
self
|
153
162
|
end
|
154
163
|
|
@@ -158,17 +167,19 @@ module Workato
|
|
158
167
|
|
159
168
|
def request_format_xml(root_element_name, namespaces = {})
|
160
169
|
@content_type_header = :xml
|
161
|
-
@render_request =
|
170
|
+
@render_request = lambda_with_error_wrap(XMLRequestFormatError) do |payload|
|
162
171
|
next unless payload
|
163
172
|
|
164
173
|
Gyoku.xml({ root_element_name => payload.merge(namespaces).deep_symbolize_keys }, key_converter: :none)
|
165
|
-
|
174
|
+
end
|
166
175
|
self
|
167
176
|
end
|
168
177
|
|
169
178
|
def response_format_xml(strip_response_namespaces: false)
|
170
179
|
@accept_header = :xml
|
171
|
-
@parse_response =
|
180
|
+
@parse_response = lambda_with_error_wrap(XMLResponseFormatError) do |payload|
|
181
|
+
Xml.parse_xml_to_hash(payload, strip_namespaces: strip_response_namespaces)
|
182
|
+
end
|
172
183
|
self
|
173
184
|
end
|
174
185
|
|
@@ -179,7 +190,7 @@ module Workato
|
|
179
190
|
end
|
180
191
|
|
181
192
|
def response_format_raw
|
182
|
-
@parse_response =
|
193
|
+
@parse_response = lambda_with_error_wrap(RAWResponseFormatError) do |payload|
|
183
194
|
payload.body.force_encoding(::Encoding::BINARY)
|
184
195
|
payload.body.valid_encoding? ? payload.body : payload.body.force_encoding(::Encoding::BINARY)
|
185
196
|
end
|
@@ -189,7 +200,7 @@ module Workato
|
|
189
200
|
def request_format_multipart_form
|
190
201
|
@content_type_header = nil
|
191
202
|
|
192
|
-
@render_request =
|
203
|
+
@render_request = lambda_with_error_wrap(MultipartFormRequestFormatError) do |payload|
|
193
204
|
payload&.each_with_object({}) do |(name, (value, content_type, original_filename)), rendered|
|
194
205
|
rendered[name] = if content_type.present?
|
195
206
|
Part.new(name, content_type, original_filename || ::File.basename(name), value.to_s)
|
@@ -204,7 +215,7 @@ module Workato
|
|
204
215
|
|
205
216
|
def request_format_www_form_urlencoded
|
206
217
|
@content_type_header = 'application/x-www-form-urlencoded'
|
207
|
-
@render_request =
|
218
|
+
@render_request = lambda_with_error_wrap(WWWFormURLEncodedRequestFormatError, &:to_param)
|
208
219
|
self
|
209
220
|
end
|
210
221
|
|
@@ -228,6 +239,8 @@ module Workato
|
|
228
239
|
OpenSSL::X509::Certificate.new(intermediate)
|
229
240
|
end
|
230
241
|
self
|
242
|
+
rescue OpenSSL::OpenSSLError => e
|
243
|
+
Kernel.raise(RequestTLSCertificateFormatError, e)
|
231
244
|
end
|
232
245
|
|
233
246
|
def tls_server_certs(certificates:, strict: true)
|
@@ -237,12 +250,18 @@ module Workato
|
|
237
250
|
@ssl_cert_store.add_cert(OpenSSL::X509::Certificate.new(certificate))
|
238
251
|
end
|
239
252
|
self
|
253
|
+
rescue OpenSSL::OpenSSLError => e
|
254
|
+
Kernel.raise(RequestTLSCertificateFormatError, e)
|
240
255
|
end
|
241
256
|
|
242
257
|
def puts(*args)
|
243
258
|
::Kernel.puts(*args)
|
244
259
|
end
|
245
260
|
|
261
|
+
def try(*args, &block)
|
262
|
+
execute!.try(*args, &block)
|
263
|
+
end
|
264
|
+
|
246
265
|
private
|
247
266
|
|
248
267
|
attr_reader :method
|
@@ -295,14 +314,14 @@ module Workato
|
|
295
314
|
URI.parse(@uri)
|
296
315
|
end
|
297
316
|
|
298
|
-
return uri.to_s unless @params
|
317
|
+
return uri.to_s unless @params || @user || @password
|
299
318
|
|
300
319
|
unless @digest_auth
|
301
320
|
uri.user = URI.encode_www_form_component(@user) if @user
|
302
321
|
uri.password = URI.encode_www_form_component(@password) if @password
|
303
322
|
end
|
304
323
|
|
305
|
-
return uri.to_s unless @params
|
324
|
+
return uri.to_s unless @params
|
306
325
|
|
307
326
|
query = uri.query.to_s.split('&').select(&:present?).join('&').presence
|
308
327
|
params = @params.to_param.presence
|
@@ -360,6 +379,13 @@ module Workato
|
|
360
379
|
)
|
361
380
|
end
|
362
381
|
|
382
|
+
def apply_after_response(code, parsed_response, headers)
|
383
|
+
encoded_headers = (headers || {}).each_with_object(HashWithIndifferentAccess.new) do |(k, v), h|
|
384
|
+
h[k] = Workato::Utilities::Encoding.force_best_encoding!(v.to_s)
|
385
|
+
end
|
386
|
+
within_action_context(code, parsed_response, encoded_headers, &@after_response)
|
387
|
+
end
|
388
|
+
|
363
389
|
def within_action_context(*args, &block)
|
364
390
|
(@action || self).instance_exec(*args, &block)
|
365
391
|
end
|
@@ -384,7 +410,7 @@ module Workato
|
|
384
410
|
instance_exec(settings, @auth_type, &apply)
|
385
411
|
end
|
386
412
|
yield
|
387
|
-
rescue
|
413
|
+
rescue RestClient::Exception, CustomRequestError => e
|
388
414
|
Kernel.raise e unless first
|
389
415
|
Kernel.raise e unless refresh_authorization!(e.try(:http_code), e.try(:http_body), e.message)
|
390
416
|
|
@@ -413,6 +439,16 @@ module Workato
|
|
413
439
|
T.must(@connection)
|
414
440
|
end
|
415
441
|
|
442
|
+
def lambda_with_error_wrap(error_type, &block)
|
443
|
+
Kernel.lambda do |payload|
|
444
|
+
begin
|
445
|
+
block.call(payload)
|
446
|
+
rescue StandardError => e
|
447
|
+
Kernel.raise error_type.new(e)
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
416
452
|
class Part < StringIO
|
417
453
|
def initialize(path, content_type, original_filename, *args)
|
418
454
|
super(*args)
|
@@ -8,6 +8,20 @@ module Workato
|
|
8
8
|
module Sdk
|
9
9
|
module SorbetTypes
|
10
10
|
WebhookSubscribeOutputHash = T.type_alias { T::Hash[T.any(String, Symbol), T.untyped] }
|
11
|
+
|
12
|
+
WebhookNotificationPayload = T.type_alias { T.untyped }
|
13
|
+
|
14
|
+
TriggerEventHash = T.type_alias { T::Hash[T.untyped, T.untyped] }
|
15
|
+
|
16
|
+
WebhookNotificationOutputHash = T.type_alias { T.any(T::Array[TriggerEventHash], TriggerEventHash) }
|
17
|
+
|
18
|
+
PollOutputHash = T.type_alias do
|
19
|
+
{
|
20
|
+
'events' => T::Array[TriggerEventHash],
|
21
|
+
'can_poll_more' => T.nilable(T::Boolean),
|
22
|
+
'next_poll' => T.untyped
|
23
|
+
}
|
24
|
+
end
|
11
25
|
end
|
12
26
|
|
13
27
|
class Trigger < Operation
|
@@ -38,7 +52,7 @@ module Workato
|
|
38
52
|
extended_input_schema: SorbetTypes::OperationSchema,
|
39
53
|
extended_output_schema: SorbetTypes::OperationSchema
|
40
54
|
).returns(
|
41
|
-
|
55
|
+
SorbetTypes::PollOutputHash
|
42
56
|
)
|
43
57
|
end
|
44
58
|
def poll_page(settings = nil, input = {}, closure = nil, extended_input_schema = [],
|
@@ -52,7 +66,10 @@ module Workato
|
|
52
66
|
) do |connection, payload, eis, eos|
|
53
67
|
instance_exec(connection, payload[:input], payload[:closure], eis, eos, &poll_proc)
|
54
68
|
end
|
55
|
-
output
|
69
|
+
output.with_indifferent_access
|
70
|
+
output[:events] = Array.wrap(output[:events])
|
71
|
+
.reverse!
|
72
|
+
.map! { |event| ::Hash.try_convert(event) || event }
|
56
73
|
output[:next_poll] = output[:next_poll].presence || closure
|
57
74
|
output
|
58
75
|
end
|
@@ -65,11 +82,11 @@ module Workato
|
|
65
82
|
extended_input_schema: SorbetTypes::OperationSchema,
|
66
83
|
extended_output_schema: SorbetTypes::OperationSchema
|
67
84
|
).returns(
|
68
|
-
|
85
|
+
SorbetTypes::PollOutputHash
|
69
86
|
)
|
70
87
|
end
|
71
88
|
def poll(settings = nil, input = {}, closure = nil, extended_input_schema = [], extended_output_schema = [])
|
72
|
-
events = T.let([], T::Array[
|
89
|
+
events = T.let([], T::Array[SorbetTypes::TriggerEventHash])
|
73
90
|
|
74
91
|
loop do
|
75
92
|
output = poll_page(settings, input, closure, extended_input_schema, extended_output_schema)
|
@@ -80,13 +97,13 @@ module Workato
|
|
80
97
|
end
|
81
98
|
|
82
99
|
{
|
83
|
-
events: events
|
100
|
+
events: events,
|
84
101
|
can_poll_more: false,
|
85
102
|
next_poll: closure
|
86
103
|
}.with_indifferent_access
|
87
104
|
end
|
88
105
|
|
89
|
-
sig { params(input: SorbetTypes::
|
106
|
+
sig { params(input: SorbetTypes::TriggerEventHash).returns(T.untyped) }
|
90
107
|
def dedup(input = {})
|
91
108
|
trigger[:dedup].call(input)
|
92
109
|
end
|
@@ -94,7 +111,7 @@ module Workato
|
|
94
111
|
sig do
|
95
112
|
params(
|
96
113
|
input: SorbetTypes::OperationInputHash,
|
97
|
-
payload:
|
114
|
+
payload: SorbetTypes::WebhookNotificationPayload,
|
98
115
|
extended_input_schema: SorbetTypes::OperationSchema,
|
99
116
|
extended_output_schema: SorbetTypes::OperationSchema,
|
100
117
|
headers: T::Hash[T.any(String, Symbol), T.untyped],
|
@@ -102,7 +119,7 @@ module Workato
|
|
102
119
|
settings: T.nilable(SorbetTypes::SettingsHash),
|
103
120
|
webhook_subscribe_output: SorbetTypes::WebhookSubscribeOutputHash
|
104
121
|
).returns(
|
105
|
-
|
122
|
+
SorbetTypes::WebhookNotificationOutputHash
|
106
123
|
)
|
107
124
|
end
|
108
125
|
def webhook_notification(
|
@@ -116,10 +133,10 @@ module Workato
|
|
116
133
|
webhook_subscribe_output = {}
|
117
134
|
)
|
118
135
|
connection.merge_settings!(settings) if settings
|
119
|
-
Dsl::WithDsl.execute(
|
136
|
+
output = Dsl::WithDsl.execute(
|
120
137
|
connection,
|
121
138
|
input.with_indifferent_access,
|
122
|
-
payload
|
139
|
+
payload,
|
123
140
|
Array.wrap(extended_input_schema).map(&:with_indifferent_access),
|
124
141
|
Array.wrap(extended_output_schema).map(&:with_indifferent_access),
|
125
142
|
headers.with_indifferent_access,
|
@@ -127,7 +144,12 @@ module Workato
|
|
127
144
|
connection.settings,
|
128
145
|
webhook_subscribe_output.with_indifferent_access,
|
129
146
|
&trigger[:webhook_notification]
|
130
|
-
)
|
147
|
+
)
|
148
|
+
if output.is_a?(::Array)
|
149
|
+
output.map! { |event| ::Hash.try_convert(event) || event }
|
150
|
+
else
|
151
|
+
::Hash.try_convert(output) || output
|
152
|
+
end
|
131
153
|
end
|
132
154
|
|
133
155
|
sig do
|
@@ -169,7 +191,7 @@ module Workato
|
|
169
191
|
params: T::Hash[T.any(String, Symbol), T.untyped],
|
170
192
|
webhook_subscribe_output: SorbetTypes::WebhookSubscribeOutputHash
|
171
193
|
).returns(
|
172
|
-
T
|
194
|
+
T.any(SorbetTypes::WebhookNotificationOutputHash, SorbetTypes::PollOutputHash)
|
173
195
|
)
|
174
196
|
end
|
175
197
|
def invoke(input = {}, payload = {}, headers = {}, params = {}, webhook_subscribe_output = {})
|
@@ -26,6 +26,10 @@ module Workato
|
|
26
26
|
|
27
27
|
WORKATO_API_EMAIL_ENV = 'WORKATO_API_EMAIL'
|
28
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)
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: false
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'rails-html-sanitizer'
|
5
|
+
|
4
6
|
module Workato
|
5
7
|
module Extension
|
6
8
|
module String
|
@@ -36,7 +38,8 @@ module Workato
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def strip_tags
|
39
|
-
|
41
|
+
@html_full_sanitizer ||= Rails::Html::Sanitizer.full_sanitizer.new
|
42
|
+
@html_full_sanitizer.sanitize(self)
|
40
43
|
end
|
41
44
|
|
42
45
|
def to_time(form = :local, format: nil)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'charlock_holmes'
|
5
|
+
|
6
|
+
module Workato
|
7
|
+
module Utilities
|
8
|
+
module Encoding
|
9
|
+
class << self
|
10
|
+
# this function finds first possible encoding that allows to perform correct encoding operations
|
11
|
+
# if no encoding is found it preserves initial and replaces bad symbols with ?
|
12
|
+
def force_best_encoding!(string)
|
13
|
+
original_encoding = string.encoding
|
14
|
+
|
15
|
+
possible_encodings(string).each do |encoding|
|
16
|
+
return string.force_encoding(::Encoding::ASCII_8BIT) if encoding.nil? # for binary
|
17
|
+
next unless string.force_encoding(encoding).valid_encoding?
|
18
|
+
|
19
|
+
begin
|
20
|
+
# try encode to utf
|
21
|
+
string.encode(::Encoding::UTF_8)
|
22
|
+
return string
|
23
|
+
rescue ::Encoding::UndefinedConversionError
|
24
|
+
next
|
25
|
+
end
|
26
|
+
end
|
27
|
+
if original_encoding == ::Encoding::BINARY
|
28
|
+
string.force_encoding(::Encoding::BINARY)
|
29
|
+
else
|
30
|
+
string
|
31
|
+
.encode!(::Encoding::UTF_8, invalid: :replace, undef: :replace, replace: '?')
|
32
|
+
.encode!(original_encoding, invalid: :replace, undef: :replace, replace: '?')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def possible_encodings(string)
|
39
|
+
encoding_candidates = CharlockHolmes::EncodingDetector.detect_all(string).sort! do |a, b|
|
40
|
+
confidence_a, encoding_a = a.values_at(:confidence, :ruby_encoding)
|
41
|
+
confidence_b, encoding_b = b.values_at(:confidence, :ruby_encoding)
|
42
|
+
# If equal and one binary, prefer non-binary.
|
43
|
+
if confidence_a == confidence_b
|
44
|
+
if encoding_a == 'binary'
|
45
|
+
confidence_b += 100
|
46
|
+
elsif encoding_b == 'binary'
|
47
|
+
confidence_a += 100
|
48
|
+
end
|
49
|
+
end
|
50
|
+
confidence_b <=> confidence_a
|
51
|
+
end
|
52
|
+
encoding_candidates.map { |candidate| candidate[:ruby_encoding] }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workato-connector-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pavel Abolmasov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.2.4
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: charlock_holmes
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.7.7
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.7.7
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: countries
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,20 +150,6 @@ dependencies:
|
|
136
150
|
- - "~>"
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '2.0'
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: loofah
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - '='
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: 2.16.0
|
146
|
-
type: :runtime
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - '='
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: 2.16.0
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: net-http-digest_auth
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -206,6 +206,20 @@ dependencies:
|
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: '2.0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: rails-html-sanitizer
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - '='
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: 1.4.3
|
216
|
+
type: :runtime
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - '='
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 1.4.3
|
209
223
|
- !ruby/object:Gem::Dependency
|
210
224
|
name: rest-client
|
211
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -491,6 +505,7 @@ files:
|
|
491
505
|
- lib/workato/extension/time.rb
|
492
506
|
- lib/workato/testing/vcr_encrypted_cassette_serializer.rb
|
493
507
|
- lib/workato/testing/vcr_multipart_body_matcher.rb
|
508
|
+
- lib/workato/utilities/encoding.rb
|
494
509
|
- lib/workato/web/app.rb
|
495
510
|
- templates/.rspec.erb
|
496
511
|
- templates/Gemfile.erb
|
@@ -525,8 +540,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
525
540
|
- !ruby/object:Gem::Version
|
526
541
|
version: '0'
|
527
542
|
requirements: []
|
528
|
-
|
529
|
-
rubygems_version: 2.6.14.4
|
543
|
+
rubygems_version: 3.2.3
|
530
544
|
signing_key:
|
531
545
|
specification_version: 4
|
532
546
|
summary: Gem for running adapter's code outside Workato infrastructure
|