workato-connector-sdk 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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 +21 -5
- 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/time.rb +8 -1
- data/lib/workato/connector/sdk/dsl/workato_code_lib.rb +4 -4
- data/lib/workato/connector/sdk/errors.rb +13 -0
- data/lib/workato/connector/sdk/object_definitions.rb +10 -10
- data/lib/workato/connector/sdk/operation.rb +37 -39
- 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 +16 -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 +95 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b958407bc907daf631e2bd91ecb5c002f911906e3bf8a88c00aedd612fe80bbe
|
4
|
+
data.tar.gz: 1445969d82d648667e1de62809722625d06e40a30bb4bec565f56f1b7ce113da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ad8b4ef4ed273ad65573acb5a4e45dd1e0772b24a4de82cc4c40a3fe0a926c8f93f067eab0df2a6f23b0f94317f2d4bcaad588d203f9323417d7a973084a1fe
|
7
|
+
data.tar.gz: 879463eea7f3e0b9ef01ce1a5019eafbc8af18610306541ff97f6b62520869e0db8d1de1c0a61b9bb3c233c0932dc780e1ec04fd61704350ece80e9fbf9c764d
|
data/README.md
CHANGED
@@ -15,6 +15,7 @@ This guide below showcases how you can do the following things:
|
|
15
15
|
1. Install [RVM ("Ruby Version Manager")](http://rvm.io/) or a Ruby manager of your choice. You can find more at [here](https://www.ruby-lang.org/en/documentation/installation/)
|
16
16
|
2. Choose between Ruby versions `2.4.10` `2.5.X` or `2.7.X`. Our preferred version is `2.7.X`.
|
17
17
|
3. Verify you're running a valid ruby version. Do this by running either `ruby -v` or the commands within your version manager. i.e., `rvm current` if you have installed RVM.
|
18
|
+
4. For Windows you need tzinfo-data gem installed as well. `gem install tzinfo-data`
|
18
19
|
|
19
20
|
```bash
|
20
21
|
ruby -v
|
@@ -38,7 +39,8 @@ Commands:
|
|
38
39
|
workato generate <SUBCOMMAND> # Generates code from template
|
39
40
|
workato help [COMMAND] # Describe available commands or one specific command
|
40
41
|
workato new <CONNECTOR_PATH> # Inits new connector folder
|
41
|
-
workato
|
42
|
+
workato oauth2 # Implements OAuth Authorization Code flow
|
43
|
+
workato push # Upload and release connector's code
|
42
44
|
|
43
45
|
Options:
|
44
46
|
[--verbose], [--no-verbose]
|
@@ -124,11 +126,11 @@ You don't need to create this file. It'll be created later on. This file just ho
|
|
124
126
|
|
125
127
|
### 2.5 README.MD
|
126
128
|
This file shows up on your Github project (or other Git software you use). Use it to document what your connector does! **Not created via `workato new <PATH>` commands.**
|
127
|
-
When you use the `workato push
|
129
|
+
When you use the `workato push` command to sync your connector with your Workato workspace, this is the default file for your connector's description.
|
128
130
|
|
129
131
|
### 2.6 logo.png
|
130
132
|
The logo of your connector. **Not created via `workato new <PATH>` commands.**
|
131
|
-
When you use the `workato push
|
133
|
+
When you use the `workato push` command to sync your connector with your Workato workspace, this is the default file for your connector's logo.
|
132
134
|
|
133
135
|
### 2.7 connector.rb
|
134
136
|
Well, this is your actual connector code. This file should be a replica of your connector code in Workato.
|
@@ -223,7 +225,8 @@ Commands:
|
|
223
225
|
workato generate <SUBCOMMAND> # Generates code from template
|
224
226
|
workato help [COMMAND] # Describe available commands or one specific command
|
225
227
|
workato new <CONNECTOR_PATH> # Inits new connector folder
|
226
|
-
workato
|
228
|
+
workato oauth2 # Implements OAuth Authorization Code flow
|
229
|
+
workato push # Upload and release connector's code
|
227
230
|
|
228
231
|
Options:
|
229
232
|
[--verbose], [--no-verbose]
|
@@ -258,7 +261,6 @@ Edit encrypted file, e.g. settings.yaml.enc
|
|
258
261
|
### 3.3 workato exec
|
259
262
|
```
|
260
263
|
workato help exec
|
261
|
-
|
262
264
|
Usage:
|
263
265
|
workato exec <PATH>
|
264
266
|
|
@@ -269,6 +271,7 @@ Options:
|
|
269
271
|
-k, [--key=KEY] # Path to file with encrypt/decrypt key. NOTE: key from WORKATO_CONNECTOR_MASTER_KEY has higher priority
|
270
272
|
-i, [--input=INPUT] # Path to file with input JSON
|
271
273
|
[--closure=CLOSURE] # Path to file with next poll closure JSON
|
274
|
+
[--continue=CONTINUE] # Path to file with next multistep action continue closure JSON
|
272
275
|
-a, [--args=ARGS] # Path to file with method arguments JSON
|
273
276
|
[--extended-input-schema=EXTENDED_INPUT_SCHEMA] # Path to file with extended input schema definition JSON
|
274
277
|
[--extended-output-schema=EXTENDED_OUTPUT_SCHEMA] # Path to file with extended output schema definition JSON
|
@@ -278,8 +281,11 @@ Options:
|
|
278
281
|
[--webhook-headers=WEBHOOK_HEADERS] # Path to file with webhook headers JSON
|
279
282
|
[--webhook-url=WEBHOOK_URL] # Webhook URL for automatic webhook subscription
|
280
283
|
-o, [--output=OUTPUT] # Write output to JSON file
|
281
|
-
[--
|
282
|
-
[--
|
284
|
+
[--oauth2-code=OAUTH2_CODE] # OAuth2 code exchange to tokens pair
|
285
|
+
[--redirect-url=REDIRECT_URL] # OAuth2 callback url
|
286
|
+
[--refresh-token=REFRESH_TOKEN] # OAuth2 refresh token
|
287
|
+
[--debug], [--no-debug]
|
288
|
+
[--verbose], [--no-verbose]
|
283
289
|
|
284
290
|
Description:
|
285
291
|
The 'workato exec' executes connector's lambda block at <PATH>. Lambda's parameters can be provided if needed, see options part.
|
@@ -357,12 +363,36 @@ Please select default HTTP mocking behavior suitable for your project?
|
|
357
363
|
|
358
364
|
- `simple` means your HTTP requests will be stored in plain text.
|
359
365
|
|
360
|
-
### 3.6 workato
|
366
|
+
### 3.6 workato oauth2
|
367
|
+
```
|
368
|
+
workato help oauth2
|
369
|
+
|
370
|
+
Usage:
|
371
|
+
workato oauth2
|
372
|
+
|
373
|
+
Options:
|
374
|
+
-c, [--connector=CONNECTOR] # Path to connector source code
|
375
|
+
-s, [--settings=SETTINGS] # Path to plain or encrypted file with connection configs, passwords, tokens, secrets etc
|
376
|
+
-n, [--connection=CONNECTION] # Connection name if settings file contains multiple settings
|
377
|
+
-k, [--key=KEY] # Path to file with encrypt/decrypt key. NOTE: key from WORKATO_CONNECTOR_MASTER_KEY has higher priority
|
378
|
+
[--port=PORT] # Listen requests on specific port
|
379
|
+
# Default: 45555
|
380
|
+
[--ip=IP] # Listen requests on specific interface
|
381
|
+
# Default: 127.0.0.1
|
382
|
+
[--https], [--no-https] # Start HTTPS server using self-signed certificate
|
383
|
+
[--verbose], [--no-verbose]
|
384
|
+
|
385
|
+
Implements OAuth Authorization Code flow
|
386
|
+
```
|
387
|
+
|
388
|
+
Use this to implement the OAuth2 Authorization code grant flow for applicable connectors. Applicable connectors are ones where the connection hash has `type: 'oauth2`. For more information, check out our guide on our [main docs site](https://docs.workato.com/developing-connectors/sdk/guides/authentication/oauth/auth-code.html#how-to-guide-oauth-2-0-authorization-code-variant).
|
389
|
+
|
390
|
+
### 3.7 workato push
|
361
391
|
```
|
362
392
|
workato help push
|
363
393
|
|
364
394
|
Usage:
|
365
|
-
workato push
|
395
|
+
workato push
|
366
396
|
|
367
397
|
Options:
|
368
398
|
-t, [--title=TITLE] # Connector title on the Workato Platform
|
@@ -375,7 +405,8 @@ Options:
|
|
375
405
|
[--environment=ENVIRONMENT] # Server to push connector code to
|
376
406
|
# Default: live
|
377
407
|
# Possible values: preview, preview-eu, live, live-eu
|
378
|
-
[--
|
408
|
+
[--folder=FOLDER] # Folder ID if you what to push to folder other than Home
|
409
|
+
[--verbose], [--no-verbose]
|
379
410
|
|
380
411
|
Upload and release connector's code
|
381
412
|
```
|
@@ -488,7 +519,7 @@ workato exec test #Output of the test: lambda function should be shown
|
|
488
519
|
> *Note*: You may also see a intermediary command from the Gem asking if you'd like to refresh your access tokens. This is done when HTTP requests are made which have a response that triggers the `refresh_on` block. Selecting yes would cause the Gem to update your settings file with the latest auth credentials.
|
489
520
|
|
490
521
|
### 4.3 Example: Testing your connection on CLI - OAuth 2 - auth code grant flows
|
491
|
-
For auth code grant flows, the Workato Gem
|
522
|
+
For auth code grant flows, the Workato Gem allows you to simulate the OAuth2 flow using the `workato oauth2` command.
|
492
523
|
|
493
524
|
```ruby
|
494
525
|
{
|
@@ -572,27 +603,24 @@ For auth code grant flows, the Workato Gem doesn't simulate the browser popup so
|
|
572
603
|
and a `settings.yaml.enc` or `settings.yaml` file with the following details
|
573
604
|
|
574
605
|
```yaml
|
575
|
-
|
576
|
-
|
577
|
-
refresh_token: "valid_refresh_token"
|
578
|
-
user_key: "valid_user_key"
|
579
|
-
client_id: "valid_client_id"
|
580
|
-
client_secret: "valid_client_secret"
|
581
|
-
Invalid Access Token:
|
582
|
-
access_token: "invalid_access_token"
|
583
|
-
refresh_token: "valid_refresh_token"
|
584
|
-
user_key: "valid_user_key"
|
585
|
-
client_id: "valid_client_id"
|
586
|
-
client_secret: "valid_client_secret"
|
606
|
+
client_id: valid_client_id
|
607
|
+
client_secret: valid_client_secret
|
587
608
|
```
|
588
609
|
|
589
|
-
You can now run the following commands to
|
610
|
+
You can now run the following commands to go through the OAuth2 Authorization code flow which includes a browser popup.
|
590
611
|
```bash
|
591
|
-
workato
|
592
|
-
workato exec test --connection='Invalid Access Token' --verbose #You should see a see a set of request where the test is tried, access token receives 401 and the sequence to refresh the gem using the refresh key is used! This allows you to test how your connector refreshes a connection.
|
612
|
+
workato oauth2
|
593
613
|
```
|
594
614
|
|
595
|
-
|
615
|
+
https://user-images.githubusercontent.com/25265275/137942408-812fa6ad-353f-4ea2-bf37-f804e2ff7b04.mov
|
616
|
+
|
617
|
+
|
618
|
+
> *Note*: `--verbose` can be used to detail everything, including the HTTP requests.
|
619
|
+
|
620
|
+
|
621
|
+
Now after you've successfully gone through the flow, you may be use the same `workato exec test` command to verify you're applying your token properly in your requests! Depending on when you received your token, you may also see a intermediary command from the Gem asking if you'd like to refresh your access tokens (if it has expired). This is done when HTTP requests are made which have a response that triggers the `refresh_on` block. Selecting "Yes" would cause the Gem to update your settings file with the latest auth credentials.
|
622
|
+
|
623
|
+
Take note, you may also use `workato exec` to execute lambdas in your `authorization` hash like `acquire` and `refresh`. **That said, we highlight recommend you use `workato exec test` and `workato oauth2` which handle the updating of your `settings.yaml` file automatically.**
|
596
624
|
|
597
625
|
### Example: Testing a sample action on CLI
|
598
626
|
Continuing from the previous example, let's take a look at a simple action and invoke the individual lambda functions.
|
@@ -631,7 +659,7 @@ Continuing from the previous example, let's take a look at a simple action and i
|
|
631
659
|
end,
|
632
660
|
|
633
661
|
|
634
|
-
execute: lambda do |connection, input, input_schema, output_schema|
|
662
|
+
execute: lambda do |connection, input, input_schema, output_schema, closure|
|
635
663
|
get("/api/v2/customers",input)
|
636
664
|
end,
|
637
665
|
|
@@ -1086,8 +1114,7 @@ jobs:
|
|
1086
1114
|
# env:
|
1087
1115
|
# WORKATO_API_EMAIL: ${{ secrets.WORKATO_DEV_ENVIRONMENT_API_EMAIL}}
|
1088
1116
|
# WORKATO_API_TOKEN: ${{ secrets.WORKATO_DEV_ENVIRONMENT_API_TOKEN}}
|
1089
|
-
#
|
1090
|
-
# run: workato push $WORKATO_API_FOLDER
|
1117
|
+
# run: workato push
|
1091
1118
|
```
|
1092
1119
|
|
1093
1120
|
You may also add more Github actions for rubocop to automate this.
|
data/exe/workato
CHANGED
@@ -2,4 +2,15 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'workato/cli/main'
|
5
|
-
|
5
|
+
|
6
|
+
begin
|
7
|
+
Workato::CLI::Main.start
|
8
|
+
rescue Workato::CLI::ExecCommand::DebugExceptionError => e
|
9
|
+
raise e.cause
|
10
|
+
rescue SystemExit, SignalException
|
11
|
+
raise
|
12
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
13
|
+
puts ''
|
14
|
+
puts e.message
|
15
|
+
exit(false)
|
16
|
+
end
|
@@ -28,7 +28,7 @@ module Workato
|
|
28
28
|
|
29
29
|
puts 'File encrypted and saved.'
|
30
30
|
rescue ActiveSupport::MessageEncryptor::InvalidMessage
|
31
|
-
|
31
|
+
raise "Couldn't decrypt #{encrypted_file_path}. Perhaps you passed the wrong key?"
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -63,8 +63,6 @@ module Workato
|
|
63
63
|
yield
|
64
64
|
rescue Interrupt
|
65
65
|
puts 'Aborted changing file: nothing saved.'
|
66
|
-
rescue ActiveSupport::EncryptedFile::MissingKeyError => e
|
67
|
-
puts e.message
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'thor'
|
4
|
-
require 'ruby-progressbar'
|
5
4
|
|
6
5
|
module Workato
|
7
6
|
module CLI
|
8
7
|
class ExecCommand
|
9
8
|
include Thor::Shell
|
10
9
|
|
10
|
+
DebugExceptionError = Class.new(StandardError)
|
11
|
+
|
11
12
|
def initialize(path:, options:)
|
12
13
|
@path = path
|
13
14
|
@options = options
|
@@ -55,10 +56,14 @@ module Workato
|
|
55
56
|
extended_output_schema: from_json(options[:extended_output_schema]).presence || [],
|
56
57
|
config_fields: from_json(options[:config_fields]),
|
57
58
|
closure: from_json(options[:closure], parse_json_times: true).presence,
|
59
|
+
continue: from_json(options[:continue], parse_json_times: true),
|
58
60
|
payload: from_json(options[:webhook_payload]),
|
59
61
|
params: from_json(options[:webhook_params]),
|
60
62
|
headers: from_json(options[:webhook_headers]),
|
61
63
|
webhook_url: options[:webhook_url],
|
64
|
+
oauth2_code: options[:oauth2_code],
|
65
|
+
redirect_url: options[:redirect_url],
|
66
|
+
refresh_token: options[:refresh_token],
|
62
67
|
recipe_id: SecureRandom.uuid
|
63
68
|
}
|
64
69
|
end
|
@@ -119,16 +124,25 @@ module Workato
|
|
119
124
|
methods = path.split('.')
|
120
125
|
method = methods.pop
|
121
126
|
object = methods.inject(params[:connector]) { |obj, m| obj.public_send(m) }
|
127
|
+
output = invoke_method(object, method)
|
128
|
+
if output.respond_to?(:invoke)
|
129
|
+
invoke_method(output, :invoke)
|
130
|
+
else
|
131
|
+
output
|
132
|
+
end
|
133
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
134
|
+
raise DebugExceptionError, e if options[:debug]
|
135
|
+
|
136
|
+
raise
|
137
|
+
end
|
138
|
+
|
139
|
+
def invoke_method(object, method)
|
122
140
|
parameters = object.method(method).parameters.reject { |p| p[0] == :block }.map(&:second)
|
123
141
|
args = params.values_at(*parameters)
|
124
142
|
if parameters.last == :args
|
125
143
|
args = args.take(args.length - 1) + Array.wrap(args.last).flatten(1)
|
126
144
|
end
|
127
145
|
object.public_send(method, *args)
|
128
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
129
|
-
raise e if options[:debug]
|
130
|
-
|
131
|
-
e.message
|
132
146
|
end
|
133
147
|
|
134
148
|
def show_output(output)
|
@@ -149,6 +163,8 @@ module Workato
|
|
149
163
|
def with_progress
|
150
164
|
return yield unless verbose?
|
151
165
|
|
166
|
+
require 'ruby-progressbar'
|
167
|
+
|
152
168
|
say('')
|
153
169
|
|
154
170
|
old_stdout = $stdout
|
@@ -30,7 +30,7 @@ module Workato
|
|
30
30
|
def create_spec_files
|
31
31
|
template('.rspec.erb', '.rspec')
|
32
32
|
|
33
|
-
|
33
|
+
say(<<~HELP)
|
34
34
|
Please select default HTTP mocking behavior suitable for your project?
|
35
35
|
|
36
36
|
1 - secure. Cause an error to be raised for any unknown requests, all request recordings are encrypted.
|
@@ -39,8 +39,11 @@ module Workato
|
|
39
39
|
Example: VCR_RECORD_MODE=once bundle exec rspec spec/actions/test_action_spec.rb
|
40
40
|
|
41
41
|
2 - simple. Record new interaction if it is a new request, requests are stored as plain text and expose secret tokens.
|
42
|
+
|
42
43
|
HELP
|
43
44
|
|
45
|
+
@vcr_record_mode = ask('Your choice:')
|
46
|
+
|
44
47
|
MasterKeyGenerator.new.call if @vcr_record_mode == '1'
|
45
48
|
|
46
49
|
template('spec/spec_helper.rb.erb', 'spec/spec_helper.rb')
|
data/lib/workato/cli/main.rb
CHANGED
@@ -6,6 +6,7 @@ require_relative './exec_command'
|
|
6
6
|
require_relative './edit_command'
|
7
7
|
require_relative './generate_command'
|
8
8
|
require_relative './push_command'
|
9
|
+
require_relative './oauth2_command'
|
9
10
|
require_relative './generators/connector_generator'
|
10
11
|
require_relative './generators/master_key_generator'
|
11
12
|
|
@@ -42,6 +43,7 @@ module Workato
|
|
42
43
|
"NOTE: key from #{Workato::Connector::Sdk::DEFAULT_MASTER_KEY_ENV} has higher priority"
|
43
44
|
method_option :input, type: :string, aliases: '-i', desc: 'Path to file with input JSON'
|
44
45
|
method_option :closure, type: :string, desc: 'Path to file with next poll closure JSON'
|
46
|
+
method_option :continue, type: :string, desc: 'Path to file with next multistep action continue closure JSON'
|
45
47
|
method_option :args, type: :string, aliases: '-a', desc: 'Path to file with method arguments JSON'
|
46
48
|
method_option :extended_input_schema, type: :string,
|
47
49
|
desc: 'Path to file with extended input schema definition JSON'
|
@@ -53,6 +55,9 @@ module Workato
|
|
53
55
|
method_option :webhook_headers, type: :string, desc: 'Path to file with webhook headers JSON'
|
54
56
|
method_option :webhook_url, type: :string, desc: 'Webhook URL for automatic webhook subscription'
|
55
57
|
method_option :output, type: :string, aliases: '-o', desc: 'Write output to JSON file'
|
58
|
+
method_option :oauth2_code, type: :string, desc: 'OAuth2 code exchange to tokens pair'
|
59
|
+
method_option :redirect_url, type: :string, desc: 'OAuth2 callback url'
|
60
|
+
method_option :refresh_token, type: :string, desc: 'OAuth2 refresh token'
|
56
61
|
|
57
62
|
method_option :debug, type: :boolean
|
58
63
|
|
@@ -95,7 +100,7 @@ module Workato
|
|
95
100
|
desc 'generate <SUBCOMMAND>', 'Generates code from template'
|
96
101
|
subcommand('generate', GenerateCommand)
|
97
102
|
|
98
|
-
desc 'push
|
103
|
+
desc 'push', "Upload and release connector's code"
|
99
104
|
method_option :title,
|
100
105
|
type: :string,
|
101
106
|
aliases: '-t',
|
@@ -130,10 +135,52 @@ module Workato
|
|
130
135
|
enum: Workato::CLI::PushCommand::ENVIRONMENTS.keys,
|
131
136
|
default: 'live',
|
132
137
|
desc: 'Server to push connector code to'
|
138
|
+
method_option :folder,
|
139
|
+
type: :string,
|
140
|
+
desc: 'Folder ID if you what to push to folder other than Home'
|
133
141
|
|
134
|
-
def push
|
142
|
+
def push
|
135
143
|
PushCommand.new(
|
136
|
-
|
144
|
+
options: options
|
145
|
+
).call
|
146
|
+
end
|
147
|
+
|
148
|
+
desc 'oauth2', 'Implements OAuth Authorization Code flow'
|
149
|
+
|
150
|
+
method_option :connector,
|
151
|
+
type: :string,
|
152
|
+
aliases: '-c',
|
153
|
+
desc: 'Path to connector source code',
|
154
|
+
lazy_default: Workato::Connector::Sdk::DEFAULT_CONNECTOR_PATH
|
155
|
+
method_option :settings,
|
156
|
+
type: :string,
|
157
|
+
aliases: '-s',
|
158
|
+
desc: 'Path to plain or encrypted file with connection configs, passwords, tokens, secrets etc',
|
159
|
+
lazy_default: Workato::Connector::Sdk::DEFAULT_ENCRYPTED_SETTINGS_PATH
|
160
|
+
method_option :connection,
|
161
|
+
type: :string,
|
162
|
+
aliases: '-n',
|
163
|
+
desc: 'Connection name if settings file contains multiple settings'
|
164
|
+
method_option :key,
|
165
|
+
type: :string,
|
166
|
+
aliases: '-k',
|
167
|
+
lazy_default: Workato::Connector::Sdk::DEFAULT_MASTER_KEY_PATH,
|
168
|
+
desc: 'Path to file with encrypt/decrypt key. '\
|
169
|
+
"NOTE: key from #{Workato::Connector::Sdk::DEFAULT_MASTER_KEY_ENV} has higher priority"
|
170
|
+
method_option :port,
|
171
|
+
type: :string,
|
172
|
+
desc: 'Listen requests on specific port',
|
173
|
+
default: Workato::CLI::OAuth2Command::DEFAULT_PORT
|
174
|
+
method_option :ip,
|
175
|
+
type: :string,
|
176
|
+
desc: 'Listen requests on specific interface',
|
177
|
+
default: Workato::CLI::OAuth2Command::DEFAULT_ADDRESS
|
178
|
+
method_option :https,
|
179
|
+
type: :boolean,
|
180
|
+
desc: 'Start HTTPS server using self-signed certificate'
|
181
|
+
|
182
|
+
def oauth2
|
183
|
+
OAuth2Command.new(
|
137
184
|
options: options
|
138
185
|
).call
|
139
186
|
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'workato/web/app'
|
4
|
+
|
5
|
+
module Workato
|
6
|
+
module CLI
|
7
|
+
class OAuth2Command
|
8
|
+
include Thor::Shell
|
9
|
+
|
10
|
+
AWAIT_CODE_TIMEOUT_INTERVAL = 180 # seconds
|
11
|
+
AWAIT_CODE_SLEEP_INTERVAL = 5 # seconds
|
12
|
+
|
13
|
+
DEFAULT_ADDRESS = '127.0.0.1'
|
14
|
+
DEFAULT_PORT = '45555'
|
15
|
+
|
16
|
+
def initialize(options: {})
|
17
|
+
@options = options
|
18
|
+
@port = options[:port] || DEFAULT_PORT
|
19
|
+
@https = options[:https].is_true?
|
20
|
+
@base_url = "#{https ? 'https' : 'http'}://localhost:#{port}"
|
21
|
+
@redirect_url = "#{base_url}#{Workato::Web::App::CALLBACK_PATH}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def call
|
25
|
+
require_gems
|
26
|
+
start_webrick
|
27
|
+
|
28
|
+
say 'Local server is running. Allow following redirect_url in your OAuth2 provider:'
|
29
|
+
say "\n"
|
30
|
+
say redirect_url
|
31
|
+
say ''
|
32
|
+
|
33
|
+
say_status :success, "Open #{authorize_url} in browser"
|
34
|
+
Launchy.open(authorize_url) do |exception|
|
35
|
+
raise "Attempted to open #{authorize_url} and failed because #{exception}"
|
36
|
+
end
|
37
|
+
|
38
|
+
code = await_code
|
39
|
+
say_status :success, "Receive OAuth2 code=#{code}"
|
40
|
+
|
41
|
+
tokens = acquire_token(code)
|
42
|
+
say_status :success, 'Receive OAuth2 tokens'
|
43
|
+
jj tokens if verbose?
|
44
|
+
|
45
|
+
settings_store.update(tokens)
|
46
|
+
say_status :success, 'Update settings file'
|
47
|
+
rescue Timeout::Error
|
48
|
+
raise "Have not received callback from OAuth2 provider in #{AWAIT_CODE_TIMEOUT_INTERVAL} seconds. Aborting!"
|
49
|
+
rescue Errno::EADDRINUSE
|
50
|
+
raise "Port #{port} already in use. Try to use different port with --port=#{rand(10_000..65_664)}"
|
51
|
+
ensure
|
52
|
+
stop_webrick
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :https,
|
58
|
+
:base_url,
|
59
|
+
:redirect_url,
|
60
|
+
:port,
|
61
|
+
:options
|
62
|
+
|
63
|
+
def verbose?
|
64
|
+
!!options[:verbose]
|
65
|
+
end
|
66
|
+
|
67
|
+
def require_gems
|
68
|
+
require 'oauth2'
|
69
|
+
require 'launchy'
|
70
|
+
require 'rack'
|
71
|
+
end
|
72
|
+
|
73
|
+
def start_webrick
|
74
|
+
@thread = Thread.start do
|
75
|
+
Rack::Handler::WEBrick.run(
|
76
|
+
Workato::Web::App.new,
|
77
|
+
{
|
78
|
+
Port: port,
|
79
|
+
BindAddress: options[:ip] || DEFAULT_ADDRESS,
|
80
|
+
SSLEnable: https,
|
81
|
+
SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
|
82
|
+
SSLCertName: [%w[CN localhost]]
|
83
|
+
}.tap do |server_options|
|
84
|
+
unless verbose?
|
85
|
+
server_options[:AccessLog] = []
|
86
|
+
server_options[:Logger] = WEBrick::Log.new($stderr, 0)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
)
|
90
|
+
end
|
91
|
+
@thread.abort_on_exception = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def stop_webrick
|
95
|
+
Rack::Handler::WEBrick.shutdown
|
96
|
+
@thread.exit
|
97
|
+
end
|
98
|
+
|
99
|
+
def client
|
100
|
+
@client ||= OAuth2::Client.new(
|
101
|
+
connector.connection.authorization.client_id(settings),
|
102
|
+
connector.connection.authorization.client_secret(settings),
|
103
|
+
site: connector.connection.base_uri(settings),
|
104
|
+
token_url: connector.connection.authorization.token_url(settings),
|
105
|
+
redirect_uri: redirect_url
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
def authorize_url
|
110
|
+
return @authorize_url if defined?(@authorize_url)
|
111
|
+
|
112
|
+
@authorize_url =
|
113
|
+
if (authorization_url = connector.connection.authorization.authorization_url(settings))
|
114
|
+
params = {
|
115
|
+
client_id: connector.connection.authorization.client_id(settings),
|
116
|
+
redirect_uri: redirect_url
|
117
|
+
}
|
118
|
+
uri = URI(authorization_url)
|
119
|
+
uri.query = params.with_indifferent_access.merge(Rack::Utils.parse_nested_query(uri.query || '')).to_param
|
120
|
+
uri.to_s
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def settings_store
|
125
|
+
@settings_store ||= Workato::Connector::Sdk::Settings.new(
|
126
|
+
path: options[:settings],
|
127
|
+
name: options[:connection],
|
128
|
+
key_path: options[:key]
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
def settings
|
133
|
+
@settings ||= settings_store.read
|
134
|
+
end
|
135
|
+
|
136
|
+
def connector
|
137
|
+
@connector ||= Workato::Connector::Sdk::Connector.from_file(
|
138
|
+
options[:connector] || Workato::Connector::Sdk::DEFAULT_CONNECTOR_PATH
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
def await_code
|
143
|
+
code_uri = URI("#{base_url}#{Workato::Web::App::CODE_PATH}")
|
144
|
+
|
145
|
+
Timeout.timeout(AWAIT_CODE_TIMEOUT_INTERVAL) do
|
146
|
+
loop do
|
147
|
+
response = get(code_uri) rescue nil
|
148
|
+
break response if response.present?
|
149
|
+
|
150
|
+
sleep(AWAIT_CODE_SLEEP_INTERVAL)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def acquire_token(code)
|
156
|
+
if connector.source.dig(:connection, :authorization, :acquire)
|
157
|
+
tokens, _, extra_settings = connector.connection.authorization.acquire(settings, await_code, redirect_url)
|
158
|
+
tokens ||= {}
|
159
|
+
extra_settings ||= {}
|
160
|
+
extra_settings.merge(tokens)
|
161
|
+
else
|
162
|
+
client.auth_code.get_token(code).to_hash
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def get(uri)
|
167
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
168
|
+
if https
|
169
|
+
http.use_ssl = true
|
170
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
171
|
+
end
|
172
|
+
|
173
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
174
|
+
|
175
|
+
response = http.request(request)
|
176
|
+
response.body
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|