workato-connector-sdk 0.1.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +57 -30
- data/exe/workato +12 -1
- data/lib/workato/cli/edit_command.rb +1 -3
- data/lib/workato/cli/exec_command.rb +23 -7
- data/lib/workato/cli/generate_command.rb +1 -2
- data/lib/workato/cli/generators/connector_generator.rb +4 -1
- data/lib/workato/cli/main.rb +50 -3
- data/lib/workato/cli/oauth2_command.rb +180 -0
- data/lib/workato/cli/push_command.rb +36 -24
- data/lib/workato/connector/sdk/action.rb +52 -4
- data/lib/workato/connector/sdk/connection.rb +144 -0
- data/lib/workato/connector/sdk/connector.rb +24 -9
- data/lib/workato/connector/sdk/dsl/aws.rb +225 -0
- data/lib/workato/connector/sdk/dsl/error.rb +1 -1
- data/lib/workato/connector/sdk/dsl/http.rb +19 -0
- data/lib/workato/connector/sdk/dsl/time.rb +8 -1
- data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +21 -4
- data/lib/workato/connector/sdk/dsl.rb +2 -0
- data/lib/workato/connector/sdk/errors.rb +16 -1
- data/lib/workato/connector/sdk/object_definitions.rb +10 -10
- data/lib/workato/connector/sdk/operation.rb +38 -40
- data/lib/workato/connector/sdk/request.rb +11 -7
- data/lib/workato/connector/sdk/schema/field/array.rb +25 -0
- data/lib/workato/connector/sdk/schema/field/convertors.rb +189 -0
- data/lib/workato/connector/sdk/schema/field/date.rb +28 -0
- data/lib/workato/connector/sdk/schema/field/date_time.rb +28 -0
- data/lib/workato/connector/sdk/schema/field/integer.rb +27 -0
- data/lib/workato/connector/sdk/schema/field/number.rb +27 -0
- data/lib/workato/connector/sdk/schema/field/object.rb +25 -0
- data/lib/workato/connector/sdk/schema/field/string.rb +26 -0
- data/lib/workato/connector/sdk/schema/type/time.rb +53 -0
- data/lib/workato/connector/sdk/schema/type/unicode_string.rb +22 -0
- data/lib/workato/connector/sdk/schema.rb +230 -0
- data/lib/workato/connector/sdk/settings.rb +6 -3
- data/lib/workato/connector/sdk/trigger.rb +25 -0
- data/lib/workato/connector/sdk/version.rb +1 -1
- data/lib/workato/connector/sdk.rb +1 -0
- data/lib/workato/extension/string.rb +20 -10
- data/lib/workato/web/app.rb +23 -0
- data/templates/Gemfile.erb +1 -0
- data/templates/spec/action_spec.rb.erb +7 -1
- data/templates/spec/trigger_spec.rb.erb +6 -0
- metadata +102 -4
@@ -6,6 +6,10 @@ module Workato
|
|
6
6
|
module Dsl
|
7
7
|
# https://docs.workato.com/developing-connectors/sdk/sdk-reference/http.html#http-methods
|
8
8
|
module HTTP
|
9
|
+
PARALLEL_SUCCESS_INDEX = 0
|
10
|
+
PARALLEL_RESULTS_INDEX = 1
|
11
|
+
PARALLEL_ERRORS_INDEX = 2
|
12
|
+
|
9
13
|
def get(url, params = {})
|
10
14
|
http_request(url, method: 'GET').params(params).response_format_json
|
11
15
|
end
|
@@ -42,6 +46,21 @@ module Workato
|
|
42
46
|
http_request(url, method: 'MOVE').payload(payload).format_json
|
43
47
|
end
|
44
48
|
|
49
|
+
def parallel(requests = [], threads: 1, rpm: nil, requests_per_period: nil, period: 1.minute.to_i) # rubocop:disable Lint/UnusedMethodArgument
|
50
|
+
requests.each.with_object([true, [], []]) do |request, result|
|
51
|
+
response = nil
|
52
|
+
exception = nil
|
53
|
+
begin
|
54
|
+
response = request.execute!
|
55
|
+
rescue RequestError, RuntimeError => e
|
56
|
+
exception = e.to_s
|
57
|
+
end
|
58
|
+
result[PARALLEL_SUCCESS_INDEX] &&= exception.nil?
|
59
|
+
result[PARALLEL_RESULTS_INDEX] << response
|
60
|
+
result[PARALLEL_ERRORS_INDEX] << exception
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
45
64
|
private
|
46
65
|
|
47
66
|
def http_request(url, method:)
|
@@ -18,4 +18,11 @@ module Workato
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
begin
|
22
|
+
::Time.zone = Workato::Connector::Sdk::DEFAULT_TIME_ZONE
|
23
|
+
rescue TZInfo::DataSourceNotFound
|
24
|
+
puts ''
|
25
|
+
puts "tzinfo-data is not present. Please install gem 'tzinfo-data' by 'gem install tzinfo-data'"
|
26
|
+
puts ''
|
27
|
+
exit!
|
28
|
+
end
|
@@ -10,6 +10,8 @@ module Workato
|
|
10
10
|
JWT_ALGORITHMS = %w[RS256 RS384 RS512].freeze
|
11
11
|
JWT_RSA_KEY_MIN_LENGTH = 2048
|
12
12
|
|
13
|
+
VERIFY_RCA_ALGORITHMS = %w[SHA SHA1 SHA224 SHA256 SHA384 SHA512].freeze
|
14
|
+
|
13
15
|
def workato
|
14
16
|
WorkatoCodeLib
|
15
17
|
end
|
@@ -38,6 +40,21 @@ module Workato
|
|
38
40
|
::JWT.encode(payload, rsa_private, algorithm, header_fields)
|
39
41
|
end
|
40
42
|
|
43
|
+
def verify_rsa(payload, certificate, signature, algorithm = 'SHA256')
|
44
|
+
algorithm = algorithm.to_s.upcase
|
45
|
+
unless VERIFY_RCA_ALGORITHMS.include?(algorithm)
|
46
|
+
raise "Unsupported signing method. Supports only #{VERIFY_RCA_ALGORITHMS.join(', ')}. Got: '#{algorithm}'" # rubocop:disable Layout/LineLength
|
47
|
+
end
|
48
|
+
|
49
|
+
cert = OpenSSL::X509::Certificate.new(certificate)
|
50
|
+
digest = OpenSSL::Digest.new(algorithm)
|
51
|
+
cert.public_key.verify(digest, signature, payload)
|
52
|
+
rescue OpenSSL::PKey::PKeyError
|
53
|
+
raise 'An error occurred during signature verification. Check arguments'
|
54
|
+
rescue OpenSSL::X509::CertificateError
|
55
|
+
raise 'Invalid certificate format'
|
56
|
+
end
|
57
|
+
|
41
58
|
def parse_yaml(yaml)
|
42
59
|
::Psych.safe_load(yaml)
|
43
60
|
rescue ::Psych::DisallowedClass => e
|
@@ -63,7 +80,7 @@ module Workato
|
|
63
80
|
raise "The requested length or random bytes sequence should be <= #{RANDOM_SIZE}"
|
64
81
|
end
|
65
82
|
|
66
|
-
|
83
|
+
Extension::Binary.new(::OpenSSL::Random.random_bytes(len))
|
67
84
|
end
|
68
85
|
|
69
86
|
ALLOWED_KEY_SIZES = [128, 192, 256].freeze
|
@@ -78,7 +95,7 @@ module Workato
|
|
78
95
|
cipher.encrypt
|
79
96
|
cipher.key = key
|
80
97
|
cipher.iv = init_vector if init_vector.present?
|
81
|
-
|
98
|
+
Extension::Binary.new(cipher.update(string) + cipher.final)
|
82
99
|
end
|
83
100
|
|
84
101
|
def aes_cbc_decrypt(string, key, init_vector = nil)
|
@@ -91,11 +108,11 @@ module Workato
|
|
91
108
|
cipher.decrypt
|
92
109
|
cipher.key = key
|
93
110
|
cipher.iv = init_vector if init_vector.present?
|
94
|
-
|
111
|
+
Extension::Binary.new(cipher.update(string) + cipher.final)
|
95
112
|
end
|
96
113
|
|
97
114
|
def pbkdf2_hmac_sha1(string, salt, iterations = 1000, key_len = 16)
|
98
|
-
|
115
|
+
Extension::Binary.new(::OpenSSL::PKCS5.pbkdf2_hmac_sha1(string, salt, iterations, key_len))
|
99
116
|
end
|
100
117
|
end
|
101
118
|
end
|
@@ -10,6 +10,7 @@ require_relative './dsl/lookup_table'
|
|
10
10
|
require_relative './dsl/workato_code_lib'
|
11
11
|
require_relative './dsl/workato_schema'
|
12
12
|
require_relative './dsl/time'
|
13
|
+
require_relative './dsl/aws'
|
13
14
|
|
14
15
|
module Workato
|
15
16
|
module Connector
|
@@ -21,6 +22,7 @@ module Workato
|
|
21
22
|
include LookupTable
|
22
23
|
include WorkatoCodeLib
|
23
24
|
include WorkatoSchema
|
25
|
+
include AWS
|
24
26
|
|
25
27
|
def sleep(seconds)
|
26
28
|
::Kernel.sleep(seconds.presence || 0)
|
@@ -5,8 +5,12 @@ module Workato
|
|
5
5
|
module Sdk
|
6
6
|
InvalidDefinitionError = Class.new(StandardError)
|
7
7
|
|
8
|
+
InvalidSchemaError = Class.new(StandardError)
|
9
|
+
|
8
10
|
CustomRequestError = Class.new(StandardError)
|
9
11
|
|
12
|
+
RuntimeError = Class.new(StandardError)
|
13
|
+
|
10
14
|
class RequestError < StandardError
|
11
15
|
attr_reader :method,
|
12
16
|
:code,
|
@@ -20,11 +24,22 @@ module Workato
|
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
23
|
-
class NotImplementedError <
|
27
|
+
class NotImplementedError < StandardError
|
24
28
|
def initialize(msg = 'This part of Connector SDK is not implemented in workato-connector-sdk yet')
|
25
29
|
super
|
26
30
|
end
|
27
31
|
end
|
32
|
+
|
33
|
+
class MissingRequiredInput < StandardError
|
34
|
+
def initialize(label, toggle_label)
|
35
|
+
message = if toggle_label && label != toggle_label
|
36
|
+
"Either '#{label}' or '#{toggle_label}' must be present"
|
37
|
+
else
|
38
|
+
"'#{label}' must be present"
|
39
|
+
end
|
40
|
+
super(message)
|
41
|
+
end
|
42
|
+
end
|
28
43
|
end
|
29
44
|
end
|
30
45
|
end
|
@@ -10,23 +10,23 @@ module Workato
|
|
10
10
|
|
11
11
|
def initialize(object_definitions:, connection:, methods:, settings:)
|
12
12
|
@object_definitions_source = object_definitions
|
13
|
-
@
|
13
|
+
@methods_source = methods
|
14
14
|
@connection = connection
|
15
15
|
@settings = settings
|
16
16
|
define_object_definition_methods(object_definitions)
|
17
17
|
end
|
18
18
|
|
19
19
|
def lazy(settings = nil, config_fields = {})
|
20
|
-
|
21
|
-
fields_proc =
|
22
|
-
|
20
|
+
DupHashWithIndifferentAccess.new do |object_definitions, name|
|
21
|
+
fields_proc = object_definitions_source[name][:fields]
|
22
|
+
object_definitions[name] = Action.new(
|
23
23
|
action: {
|
24
24
|
execute: lambda do |connection, input|
|
25
|
-
instance_exec(connection, input,
|
25
|
+
instance_exec(connection, input, object_definitions, &fields_proc)
|
26
26
|
end
|
27
27
|
},
|
28
|
-
methods:
|
29
|
-
connection:
|
28
|
+
methods: methods_source,
|
29
|
+
connection: connection,
|
30
30
|
settings: @settings
|
31
31
|
).execute(settings, config_fields)
|
32
32
|
end
|
@@ -34,10 +34,10 @@ module Workato
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
attr_reader :
|
37
|
+
attr_reader :methods_source,
|
38
38
|
:connection,
|
39
|
-
:
|
40
|
-
:
|
39
|
+
:settings,
|
40
|
+
:object_definitions_source
|
41
41
|
|
42
42
|
def define_object_definition_methods(object_definitions)
|
43
43
|
object_definitions.each do |(object, _definition)|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './dsl'
|
4
4
|
require_relative './block_invocation_refinements'
|
5
|
+
require_relative './schema'
|
5
6
|
|
6
7
|
module Workato
|
7
8
|
module Connector
|
@@ -16,21 +17,23 @@ module Workato
|
|
16
17
|
|
17
18
|
cattr_accessor :on_settings_updated
|
18
19
|
|
19
|
-
def initialize(
|
20
|
-
@
|
20
|
+
def initialize(connection:, operation: {}, methods: {}, settings: {}, object_definitions: nil)
|
21
|
+
@connection = connection
|
22
|
+
@settings = settings
|
21
23
|
@operation = operation.with_indifferent_access
|
22
|
-
@connection = connection.with_indifferent_access
|
23
24
|
@_methods = methods.with_indifferent_access
|
24
25
|
@object_definitions = object_definitions
|
25
26
|
end
|
26
27
|
|
27
|
-
def execute(settings = nil, input = {}, extended_input_schema = [], extended_output_schema = [],
|
28
|
+
def execute(settings = nil, input = {}, extended_input_schema = [], extended_output_schema = [], continue = {},
|
29
|
+
&block)
|
28
30
|
@settings = settings.with_indifferent_access if settings # is being used in request for refresh tokens
|
29
31
|
request_or_result = instance_exec(
|
30
32
|
@settings.with_indifferent_access, # a copy of settings hash is being used in executable blocks
|
31
33
|
input.with_indifferent_access,
|
32
34
|
Array.wrap(extended_input_schema).map(&:with_indifferent_access),
|
33
35
|
Array.wrap(extended_output_schema).map(&:with_indifferent_access),
|
36
|
+
continue.with_indifferent_access,
|
34
37
|
&block
|
35
38
|
)
|
36
39
|
resolve_request(request_or_result)
|
@@ -69,22 +72,39 @@ module Workato
|
|
69
72
|
def refresh_authorization!(http_code, http_body, exception, settings = {})
|
70
73
|
return unless refresh_auth?(http_code, http_body, exception)
|
71
74
|
|
72
|
-
new_settings = if /oauth2/i =~ connection
|
75
|
+
new_settings = if /oauth2/i =~ connection.authorization.type
|
73
76
|
refresh_oauth2_token(settings)
|
74
|
-
elsif connection
|
77
|
+
elsif connection.authorization.acquire?
|
75
78
|
acquire_token(settings)
|
76
79
|
end
|
77
80
|
return unless new_settings
|
78
81
|
|
79
82
|
settings.merge!(new_settings)
|
80
83
|
|
81
|
-
on_settings_updated&.call(
|
84
|
+
on_settings_updated&.call("Refresh token triggered on response \"#{exception}\"", settings)
|
82
85
|
|
83
86
|
settings
|
84
87
|
end
|
85
88
|
|
86
89
|
private
|
87
90
|
|
91
|
+
def apply_input_schema(input, schema)
|
92
|
+
input = schema.trim(input)
|
93
|
+
schema.apply(input, enforce_required: true) do |value, field|
|
94
|
+
field.render_input(value, @_methods[field[:render_input]])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def apply_output_schema(output, schema)
|
99
|
+
schema.apply(output, enforce_required: false) do |value, field|
|
100
|
+
field.parse_output(value, @_methods[field[:parse_output]])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def config_fields_schema
|
105
|
+
operation[:config_fields] || []
|
106
|
+
end
|
107
|
+
|
88
108
|
def summarize(data, paths)
|
89
109
|
return data unless paths.present?
|
90
110
|
|
@@ -92,7 +112,7 @@ module Workato
|
|
92
112
|
end
|
93
113
|
|
94
114
|
def schema_fields(object_definitions_hash, settings, config_fields, &schema_proc)
|
95
|
-
return
|
115
|
+
return [] unless schema_proc
|
96
116
|
|
97
117
|
execute(settings, config_fields) do |connection, input|
|
98
118
|
instance_exec(
|
@@ -132,7 +152,7 @@ module Workato
|
|
132
152
|
end
|
133
153
|
|
134
154
|
def refresh_auth?(http_code, http_body, exception)
|
135
|
-
refresh_on =
|
155
|
+
refresh_on = connection.authorization.refresh_on
|
136
156
|
refresh_on.blank? || refresh_on.any? do |pattern|
|
137
157
|
pattern.is_a?(::Integer) && pattern == http_code ||
|
138
158
|
pattern === exception&.to_s ||
|
@@ -141,48 +161,26 @@ module Workato
|
|
141
161
|
end
|
142
162
|
|
143
163
|
def acquire_token(settings)
|
144
|
-
|
145
|
-
raise InvalidDefinitionError, "'acquire' block is required for authorization" unless acquire
|
146
|
-
|
147
|
-
Action.new(
|
148
|
-
action: {
|
149
|
-
execute: ->(connection) { instance_exec(connection, &acquire) }
|
150
|
-
},
|
151
|
-
connection: connection.merge(
|
152
|
-
authorization: connection[:authorization].merge(
|
153
|
-
apply: nil
|
154
|
-
)
|
155
|
-
),
|
156
|
-
methods: @_methods
|
157
|
-
).execute(settings)
|
164
|
+
connection.authorization.acquire(settings)
|
158
165
|
end
|
159
166
|
|
160
167
|
def refresh_oauth2_token_using_refresh(settings)
|
161
|
-
|
162
|
-
new_tokens, new_settings = Action.new(
|
163
|
-
action: {
|
164
|
-
execute: lambda do |connection|
|
165
|
-
instance_exec(connection, connection[:refresh_token], &refresh)
|
166
|
-
end
|
167
|
-
},
|
168
|
-
methods: @_methods
|
169
|
-
).execute(settings)
|
170
|
-
|
168
|
+
new_tokens, new_settings = connection.authorization.refresh(settings, settings[:refresh_token])
|
171
169
|
new_tokens.with_indifferent_access.merge(new_settings || {})
|
172
170
|
end
|
173
171
|
|
174
172
|
def refresh_oauth2_token_using_token_url(settings)
|
175
173
|
if settings[:refresh_token].blank?
|
176
|
-
raise NotImplementedError, '
|
177
|
-
'Use
|
174
|
+
raise NotImplementedError, 'refresh_token is empty. ' \
|
175
|
+
'Use workato oauth2 command to acquire access_token and refresh_token'
|
178
176
|
end
|
179
177
|
|
180
178
|
response = RestClient::Request.execute(
|
181
|
-
url: connection
|
179
|
+
url: connection.authorization.token_url(settings),
|
182
180
|
method: :post,
|
183
181
|
payload: {
|
184
|
-
client_id: connection
|
185
|
-
client_secret: connection
|
182
|
+
client_id: connection.authorization.client_id(settings),
|
183
|
+
client_secret: connection.authorization.client_secret(settings),
|
186
184
|
grant_type: :refresh_token,
|
187
185
|
refresh_token: settings[:refresh_token]
|
188
186
|
},
|
@@ -198,9 +196,9 @@ module Workato
|
|
198
196
|
end
|
199
197
|
|
200
198
|
def refresh_oauth2_token(settings)
|
201
|
-
if connection
|
199
|
+
if connection.authorization.refresh?
|
202
200
|
refresh_oauth2_token_using_refresh(settings)
|
203
|
-
elsif connection
|
201
|
+
elsif connection.authorization.token_url?
|
204
202
|
refresh_oauth2_token_using_token_url(settings)
|
205
203
|
else
|
206
204
|
raise InvalidDefinitionError, "'refresh' block or 'token_url' is required for refreshing the token"
|
@@ -15,13 +15,13 @@ module Workato
|
|
15
15
|
class Request < SimpleDelegator
|
16
16
|
using BlockInvocationRefinements
|
17
17
|
|
18
|
-
def initialize(uri, method: 'GET',
|
18
|
+
def initialize(uri, method: 'GET', settings: {}, connection: nil, action: nil)
|
19
19
|
super(nil)
|
20
20
|
@uri = uri
|
21
|
-
@authorization = (connection[:authorization] || {}).with_indifferent_access
|
22
|
-
@settings = settings
|
23
|
-
@base_uri = connection[:base_uri]&.call(settings.with_indifferent_access)
|
24
21
|
@method = method
|
22
|
+
@settings = settings
|
23
|
+
@authorization = connection&.authorization
|
24
|
+
@base_uri = connection&.base_uri(settings)
|
25
25
|
@action = action
|
26
26
|
@headers = {}
|
27
27
|
@case_sensitive_headers = {}
|
@@ -321,7 +321,9 @@ module Workato
|
|
321
321
|
end
|
322
322
|
|
323
323
|
def detect_error!(response)
|
324
|
-
|
324
|
+
return unless @authorization
|
325
|
+
|
326
|
+
error_patterns = @authorization.detect_on
|
325
327
|
return unless error_patterns.any? { |pattern| pattern === response rescue false }
|
326
328
|
|
327
329
|
Kernel.raise(CustomRequestError, response.to_s)
|
@@ -357,13 +359,15 @@ module Workato
|
|
357
359
|
end
|
358
360
|
|
359
361
|
def authorized
|
360
|
-
|
362
|
+
return yield unless @authorization
|
363
|
+
|
364
|
+
apply = @authorization.source[:apply] || @authorization.source[:credentials]
|
361
365
|
return yield unless apply
|
362
366
|
|
363
367
|
first = true
|
364
368
|
begin
|
365
369
|
settings = @settings.with_indifferent_access
|
366
|
-
if /oauth2/i =~ @authorization
|
370
|
+
if /oauth2/i =~ @authorization.type
|
367
371
|
instance_exec(settings, settings[:access_token], @auth_type, &apply)
|
368
372
|
else
|
369
373
|
instance_exec(settings, @auth_type, &apply)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './convertors'
|
4
|
+
|
5
|
+
module Workato
|
6
|
+
module Connector
|
7
|
+
module Sdk
|
8
|
+
class Schema
|
9
|
+
module Field
|
10
|
+
class Array < SimpleDelegator
|
11
|
+
include Convertors
|
12
|
+
|
13
|
+
DEFAULT_ATTRIBUTES = {
|
14
|
+
type: 'array'
|
15
|
+
}.with_indifferent_access.freeze
|
16
|
+
|
17
|
+
def initialize(field)
|
18
|
+
super(DEFAULT_ATTRIBUTES.merge(field))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Workato
|
4
|
+
module Connector
|
5
|
+
module Sdk
|
6
|
+
class Schema
|
7
|
+
module Field
|
8
|
+
module Convertors
|
9
|
+
def render_input(value, custom_convertor = nil)
|
10
|
+
apply_convertor(value, self[:render_input], custom_convertor)
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_output(value, custom_convertor = nil)
|
14
|
+
apply_convertor(value, self[:parse_output], custom_convertor)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def apply_convertor(value, builtin_convertor, custom_convertor)
|
20
|
+
return value unless builtin_convertor || custom_convertor
|
21
|
+
return send(builtin_convertor, value) if builtin_convertor && respond_to?(builtin_convertor, true)
|
22
|
+
return custom_convertor.call(value) if custom_convertor.is_a?(Proc)
|
23
|
+
|
24
|
+
raise ArgumentError, "Cannot find converter '#{builtin_convertor}'."
|
25
|
+
end
|
26
|
+
|
27
|
+
def integer_conversion(value)
|
28
|
+
value.try(:is_number?) && value.to_i || value
|
29
|
+
end
|
30
|
+
|
31
|
+
def boolean_conversion(value)
|
32
|
+
value.try(:is_true?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def float_conversion(value)
|
36
|
+
value.try(:is_number?) && value.to_f || value
|
37
|
+
end
|
38
|
+
|
39
|
+
def item_array_wrap(items)
|
40
|
+
::Array.wrap(items).presence&.flatten(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
def date_conversion(value)
|
44
|
+
parse_date(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def date_iso8601_conversion(value)
|
48
|
+
parse_date(value)&.iso8601
|
49
|
+
end
|
50
|
+
|
51
|
+
def date_time_conversion(value)
|
52
|
+
parse_date_time(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def date_time_iso8601_conversion(value)
|
56
|
+
parse_date_time(value)&.iso8601
|
57
|
+
end
|
58
|
+
|
59
|
+
def convert_to_datetime(value)
|
60
|
+
value.try(:to_datetime) || value
|
61
|
+
end
|
62
|
+
|
63
|
+
def convert_to_datetime_wo_tz(value)
|
64
|
+
value.try(:to_datetime).strftime('%Y-%m-%d %H:%M:%S') || value
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_date_output(value)
|
68
|
+
value.try(:to_date) || value
|
69
|
+
end
|
70
|
+
|
71
|
+
def render_date_input(value)
|
72
|
+
try_in_time_zone(value).try(:to_date)
|
73
|
+
end
|
74
|
+
|
75
|
+
def convert_date_time(value)
|
76
|
+
try_in_time_zone(value)
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_date_time_epoch_millis(value)
|
80
|
+
if value.is_a?(::Time)
|
81
|
+
value
|
82
|
+
else
|
83
|
+
value.is_a?(Numeric) && ::Time.at(value.to_f / 1000)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def render_date_time_epoch_millis(value)
|
88
|
+
value.try(:to_f).try(:*, 1000).try(:to_i)
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse_iso8601_timestamp(value)
|
92
|
+
if value.is_a?(::Time)
|
93
|
+
value
|
94
|
+
else
|
95
|
+
value.try(:to_time)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def render_iso8601_timestamp(value)
|
100
|
+
value.try(:to_time).try(:iso8601)
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse_iso8601_date(value)
|
104
|
+
if value.is_a?(::Date)
|
105
|
+
value
|
106
|
+
else
|
107
|
+
value.try(:to_date)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def render_iso8601_date(value)
|
112
|
+
value.try(:to_date).try(:iso8601)
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse_epoch_time(value)
|
116
|
+
if value.is_a?(::Time)
|
117
|
+
value
|
118
|
+
else
|
119
|
+
(value.is_a?(Numeric).presence || value.try(:is_number?).presence) && ::Time.zone.at(value.to_i)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def render_epoch_time(value)
|
124
|
+
value.try(:to_time).try(:to_i)
|
125
|
+
end
|
126
|
+
|
127
|
+
def parse_float_epoch_time(value)
|
128
|
+
if value.is_a?(::Time)
|
129
|
+
value
|
130
|
+
else
|
131
|
+
(value.is_a?(Numeric) || value.try(:is_number?)) && ::Time.zone.at(value.to_f)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def render_float_epoch_time(value)
|
136
|
+
value.try(:to_time).try(:to_f)
|
137
|
+
end
|
138
|
+
|
139
|
+
def implicit_utc_time(value)
|
140
|
+
value&.in_time_zone('UTC')
|
141
|
+
end
|
142
|
+
|
143
|
+
def implicit_utc_iso8601_time(value)
|
144
|
+
value&.in_time_zone('UTC')&.iso8601
|
145
|
+
end
|
146
|
+
|
147
|
+
# Helpers
|
148
|
+
#
|
149
|
+
def try_in_time_zone(value)
|
150
|
+
value.try(:in_time_zone, local_time_zone || ::Time.zone) || value
|
151
|
+
end
|
152
|
+
|
153
|
+
def local_time_zone
|
154
|
+
ENV['WORKATO_TIME_ZONE'] || Workato::Connector::Sdk::DEFAULT_TIME_ZONE
|
155
|
+
end
|
156
|
+
|
157
|
+
def parse_date(value)
|
158
|
+
if value.blank? || value.is_a?(::Date)
|
159
|
+
value.presence
|
160
|
+
elsif value.is_a?(::Time)
|
161
|
+
value.to_date
|
162
|
+
else
|
163
|
+
parse_time_string(value).to_date
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_date_time(value)
|
168
|
+
if value.blank? || value.is_a?(::Time)
|
169
|
+
value.presence
|
170
|
+
elsif value.is_a?(::Date)
|
171
|
+
value.in_time_zone(local_time_zone)
|
172
|
+
else
|
173
|
+
parse_time_string(value)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_time_string(value)
|
178
|
+
value_time = ::Time.parse(value)
|
179
|
+
user_time = ActiveSupport::TimeZone[local_time_zone].parse(value)
|
180
|
+
|
181
|
+
# equal means value had its own offset/TZ or defaulted to system TZ with same offset as user's.
|
182
|
+
value_time == user_time ? value_time : user_time
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './convertors'
|
4
|
+
|
5
|
+
module Workato
|
6
|
+
module Connector
|
7
|
+
module Sdk
|
8
|
+
class Schema
|
9
|
+
module Field
|
10
|
+
class Date < SimpleDelegator
|
11
|
+
include Convertors
|
12
|
+
|
13
|
+
DEFAULT_ATTRIBUTES = {
|
14
|
+
type: 'date_time',
|
15
|
+
control_type: 'date',
|
16
|
+
render_input: 'date_conversion',
|
17
|
+
parse_output: 'date_conversion'
|
18
|
+
}.with_indifferent_access.freeze
|
19
|
+
|
20
|
+
def initialize(field)
|
21
|
+
super(DEFAULT_ATTRIBUTES.merge(field))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|