wisco 0.3.5 → 0.3.6
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 +4 -4
- data/lib/wisco/commands/init.rb +74 -31
- data/lib/wisco/commands/profile.rb +236 -0
- data/lib/wisco/config.rb +53 -3
- data/lib/wisco/profile.rb +137 -0
- data/lib/wisco/version.rb +1 -1
- data/lib/wisco.rb +18 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc712af30ea2647f51bf72fac2bcf59f57105efcd8382d831afcadceee912f5c
|
|
4
|
+
data.tar.gz: b49f08a071f353521d09469452e8508e31b78f4108545b6e723083e816ecc67d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4029281c6717dc8eeef9b9b3d86018998c5846dea82d5703d10229b84bf990c8beb6d8c73928315804878b8c9ab6a5bc541b9e46b34ddef7d601c2590e354331
|
|
7
|
+
data.tar.gz: 7a4161111ce339ffe212cacdd08365f9464b79d02a729b492e04925ed2d2a9f10687fdcd79d0eef0fcadc321cf37c9a5fb05eca57f63f3d7da747b3438204e14
|
data/lib/wisco/commands/init.rb
CHANGED
|
@@ -2,22 +2,14 @@ require 'erb'
|
|
|
2
2
|
require 'fileutils'
|
|
3
3
|
require_relative '../config'
|
|
4
4
|
require_relative '../connector'
|
|
5
|
+
require_relative '../profile'
|
|
5
6
|
|
|
6
7
|
module Wisco
|
|
7
8
|
module Commands
|
|
8
9
|
module Init
|
|
9
|
-
HOSTNAMES = [
|
|
10
|
-
{ region: 'US Data Center', hostname: 'www.workato.com' },
|
|
11
|
-
{ region: 'EU Data Center', hostname: 'app.eu.workato.com' },
|
|
12
|
-
{ region: 'JP Data Center', hostname: 'app.jp.workato.com' },
|
|
13
|
-
{ region: 'SG Data Center', hostname: 'app.sg.workato.com' },
|
|
14
|
-
{ region: 'AU Data Center', hostname: 'app.au.workato.com' },
|
|
15
|
-
{ region: 'IL Data Center', hostname: 'app.il.workato.com' },
|
|
16
|
-
].freeze
|
|
17
|
-
|
|
18
10
|
module_function
|
|
19
11
|
|
|
20
|
-
def run(target_dir)
|
|
12
|
+
def run(target_dir, profile: nil)
|
|
21
13
|
target_dir = File.expand_path(target_dir)
|
|
22
14
|
|
|
23
15
|
unless Dir.exist?(target_dir)
|
|
@@ -46,7 +38,7 @@ module Wisco
|
|
|
46
38
|
config['connector']['path'] = target_dir
|
|
47
39
|
config['connector']['file'] = connector_file
|
|
48
40
|
|
|
49
|
-
prompt_hostname(config)
|
|
41
|
+
prompt_hostname(config, profile: profile)
|
|
50
42
|
|
|
51
43
|
Wisco::Config.save_config(config_path, config)
|
|
52
44
|
puts "Config written to #{config_path}"
|
|
@@ -56,32 +48,78 @@ module Wisco
|
|
|
56
48
|
deploy_github_workflow(target_dir, connector_file, config)
|
|
57
49
|
end
|
|
58
50
|
|
|
59
|
-
def prompt_hostname(config)
|
|
51
|
+
def prompt_hostname(config, profile: nil)
|
|
60
52
|
api_cfg = config['workato_developer_api'] ||= {}
|
|
61
|
-
current = api_cfg['hostname']
|
|
62
53
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
# --profile flag: skip all prompts and attach the named profile
|
|
55
|
+
if profile
|
|
56
|
+
unless Wisco::Profiles.exists?(profile)
|
|
57
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{profile}\" not found in #{Wisco::Profiles.profiles_path}.")
|
|
58
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco profile list' to see available profiles.")
|
|
59
|
+
exit 1
|
|
60
|
+
end
|
|
61
|
+
api_cfg.delete('hostname')
|
|
62
|
+
api_cfg.delete('api_token')
|
|
63
|
+
api_cfg['profile'] = profile
|
|
64
|
+
puts "Using profile: #{profile}"
|
|
65
|
+
return
|
|
67
66
|
end
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
# If profiles exist, offer them alongside "enter manually"
|
|
69
|
+
existing_profiles = Wisco::Profiles.all
|
|
70
|
+
unless existing_profiles.empty?
|
|
71
|
+
current_profile = api_cfg['profile'].to_s.strip
|
|
72
|
+
current_host = api_cfg['hostname'].to_s.strip
|
|
73
|
+
|
|
74
|
+
if !current_profile.empty?
|
|
75
|
+
puts "Current Workato profile: #{current_profile}"
|
|
76
|
+
print 'Keep this? (y/n): '
|
|
77
|
+
return if $stdin.gets.strip.downcase == 'y'
|
|
78
|
+
elsif !current_host.empty?
|
|
79
|
+
puts "Current Workato hostname: #{current_host} (inline credentials)"
|
|
80
|
+
print 'Keep this? (y/n): '
|
|
81
|
+
return if $stdin.gets.strip.downcase == 'y'
|
|
82
|
+
end
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
puts 'Workato API credentials'
|
|
85
|
+
name_width = existing_profiles.keys.map(&:length).max
|
|
86
|
+
existing_profiles.each_with_index do |(prof_name, p), i|
|
|
87
|
+
puts " #{i + 1}. #{prof_name.ljust(name_width)} (#{p['hostname']})"
|
|
88
|
+
end
|
|
89
|
+
manual_index = existing_profiles.size + 1
|
|
90
|
+
puts " #{manual_index}. Enter credentials manually"
|
|
91
|
+
loop do
|
|
92
|
+
print "Select an option (1-#{manual_index}): "
|
|
93
|
+
input = $stdin.gets.strip
|
|
94
|
+
index = input.to_i
|
|
95
|
+
if index >= 1 && index < manual_index
|
|
96
|
+
chosen = existing_profiles.keys[index - 1]
|
|
97
|
+
api_cfg.delete('hostname')
|
|
98
|
+
api_cfg.delete('api_token')
|
|
99
|
+
api_cfg['profile'] = chosen
|
|
100
|
+
puts "Using profile: #{chosen}"
|
|
101
|
+
return
|
|
102
|
+
elsif index == manual_index
|
|
103
|
+
break # fall through to manual hostname prompt below
|
|
104
|
+
else
|
|
105
|
+
warn "Invalid selection. Please enter a number between 1 and #{manual_index}."
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
# No profiles — check for existing inline hostname
|
|
110
|
+
current = api_cfg['hostname']
|
|
111
|
+
if current && !current.strip.empty?
|
|
112
|
+
puts "Current Workato hostname: #{current}"
|
|
113
|
+
print 'Keep this? (y/n): '
|
|
114
|
+
return if $stdin.gets.strip.downcase == 'y'
|
|
82
115
|
end
|
|
83
|
-
warn "Invalid selection. Please enter a number between 1 and #{HOSTNAMES.size}."
|
|
84
116
|
end
|
|
117
|
+
|
|
118
|
+
# Manual hostname selection
|
|
119
|
+
hostname = Wisco::Profiles.prompt_hostname_selection
|
|
120
|
+
api_cfg.delete('profile')
|
|
121
|
+
api_cfg['hostname'] = hostname
|
|
122
|
+
puts "Hostname set to: #{hostname}"
|
|
85
123
|
end
|
|
86
124
|
|
|
87
125
|
def update_gitignore(target_dir)
|
|
@@ -128,7 +166,12 @@ module Wisco
|
|
|
128
166
|
end
|
|
129
167
|
end
|
|
130
168
|
|
|
131
|
-
|
|
169
|
+
api_cfg = config['workato_developer_api'] || {}
|
|
170
|
+
hostname = if api_cfg['profile'].to_s.strip != ''
|
|
171
|
+
Wisco::Profiles.get(api_cfg['profile'])&.dig('hostname')
|
|
172
|
+
else
|
|
173
|
+
api_cfg['hostname']
|
|
174
|
+
end
|
|
132
175
|
workato_base_url = "https://#{hostname}"
|
|
133
176
|
connector_name = connector_file
|
|
134
177
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
require_relative '../profile'
|
|
2
|
+
require_relative '../config'
|
|
3
|
+
require_relative '../terminal_output'
|
|
4
|
+
|
|
5
|
+
module Wisco
|
|
6
|
+
module Commands
|
|
7
|
+
class Profile < Thor
|
|
8
|
+
def self.exit_on_failure?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc 'add [NAME]', 'Create a new connection profile'
|
|
13
|
+
long_desc <<~DESC
|
|
14
|
+
Prompts for hostname and API token and saves a named profile in ~/.wisco/profiles.yaml.
|
|
15
|
+
If NAME is omitted, a name is suggested based on the selected datacenter.
|
|
16
|
+
DESC
|
|
17
|
+
def add(name = nil)
|
|
18
|
+
hostname = Wisco::Profiles.prompt_hostname_selection
|
|
19
|
+
puts "Hostname: #{hostname}"
|
|
20
|
+
|
|
21
|
+
api_token = Wisco::Profiles.prompt_token('API token (input hidden): ')
|
|
22
|
+
|
|
23
|
+
if name.nil?
|
|
24
|
+
suggestion = Wisco::Profiles.suggest_name(hostname)
|
|
25
|
+
name = Wisco::Profiles.prompt_name(suggestion: suggestion)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if Wisco::Profiles.exists?(name)
|
|
29
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{name}\" already exists. Use 'wisco profile edit #{name}' to update it.")
|
|
30
|
+
exit 1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Wisco::Profiles.set(name, hostname, api_token)
|
|
34
|
+
puts "Profile \"#{name}\" saved to #{Wisco::Profiles.profiles_path}."
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc 'list', 'List all connection profiles'
|
|
38
|
+
def list
|
|
39
|
+
profiles = Wisco::Profiles.all
|
|
40
|
+
if profiles.empty?
|
|
41
|
+
puts "No profiles found. Run 'wisco profile add' to create one."
|
|
42
|
+
return
|
|
43
|
+
end
|
|
44
|
+
puts "Profiles (from #{Wisco::Profiles.profiles_path}):\n\n"
|
|
45
|
+
name_width = profiles.keys.map(&:length).max
|
|
46
|
+
profiles.each do |prof_name, p|
|
|
47
|
+
puts " #{prof_name.ljust(name_width)} #{p['hostname']}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc 'show NAME', 'Show details of a profile'
|
|
52
|
+
def show(name)
|
|
53
|
+
profile = Wisco::Profiles.get(name)
|
|
54
|
+
if profile.nil?
|
|
55
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{name}\" not found.")
|
|
56
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco profile list' to see available profiles.")
|
|
57
|
+
exit 1
|
|
58
|
+
end
|
|
59
|
+
puts "Profile: #{name}"
|
|
60
|
+
puts " hostname: #{profile['hostname']}"
|
|
61
|
+
puts " api_token: #{Wisco::Profiles.mask_token(profile['api_token'])}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
desc 'edit NAME', 'Update the hostname and/or API token for a profile'
|
|
65
|
+
long_desc 'Re-prompts for hostname and API token. Press Enter to keep the current value.'
|
|
66
|
+
def edit(name)
|
|
67
|
+
profile = Wisco::Profiles.get(name)
|
|
68
|
+
if profile.nil?
|
|
69
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{name}\" not found.")
|
|
70
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco profile list' to see available profiles.")
|
|
71
|
+
exit 1
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
puts "Editing profile \"#{name}\":"
|
|
75
|
+
puts " Current hostname: #{profile['hostname']}"
|
|
76
|
+
puts " Current api_token: #{Wisco::Profiles.mask_token(profile['api_token'])}"
|
|
77
|
+
puts
|
|
78
|
+
|
|
79
|
+
selected = Wisco::Profiles.prompt_hostname_selection(allow_skip: true)
|
|
80
|
+
hostname = selected || profile['hostname']
|
|
81
|
+
puts "Hostname: #{hostname}"
|
|
82
|
+
|
|
83
|
+
raw = Wisco::Profiles.prompt_token('New API token (input hidden, Enter to keep current): ')
|
|
84
|
+
api_token = raw.empty? ? profile['api_token'] : raw
|
|
85
|
+
|
|
86
|
+
Wisco::Profiles.set(name, hostname, api_token)
|
|
87
|
+
puts "Profile \"#{name}\" updated."
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
desc 'remove NAME', 'Remove a connection profile'
|
|
91
|
+
def remove(name)
|
|
92
|
+
unless Wisco::Profiles.exists?(name)
|
|
93
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{name}\" not found.")
|
|
94
|
+
exit 1
|
|
95
|
+
end
|
|
96
|
+
print "Remove profile \"#{name}\"? (y/n): "
|
|
97
|
+
unless $stdin.gets.strip.downcase == 'y'
|
|
98
|
+
puts 'Aborted.'
|
|
99
|
+
return
|
|
100
|
+
end
|
|
101
|
+
Wisco::Profiles.delete(name)
|
|
102
|
+
puts "Profile \"#{name}\" removed."
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
desc 'use NAME', 'Attach a profile to the current connector project'
|
|
106
|
+
def use(name)
|
|
107
|
+
unless Wisco::Profiles.exists?(name)
|
|
108
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{name}\" not found in #{Wisco::Profiles.profiles_path}.")
|
|
109
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco profile list' to see available profiles.")
|
|
110
|
+
exit 1
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
config_path = Wisco.config_path(Dir.pwd)
|
|
114
|
+
unless File.exist?(config_path)
|
|
115
|
+
Wisco::TerminalOutput.emit_error("Error: No #{Wisco::WISCO_DIR}/#{Wisco::CONFIG_FILENAME} found in #{Dir.pwd}.")
|
|
116
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco init' first.")
|
|
117
|
+
exit 1
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
config = Wisco::Config.load_config(config_path)
|
|
121
|
+
api_cfg = config['workato_developer_api'] ||= {}
|
|
122
|
+
has_inline = !api_cfg['hostname'].to_s.strip.empty? || !api_cfg['api_token'].to_s.strip.empty?
|
|
123
|
+
|
|
124
|
+
if has_inline
|
|
125
|
+
print "This project has inline credentials configured.\nReplace with a reference to profile \"#{name}\"? (y/n): "
|
|
126
|
+
unless $stdin.gets.strip.downcase == 'y'
|
|
127
|
+
puts 'Aborted.'
|
|
128
|
+
return
|
|
129
|
+
end
|
|
130
|
+
api_cfg.delete('hostname')
|
|
131
|
+
api_cfg.delete('api_token')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
api_cfg['profile'] = name
|
|
135
|
+
Wisco::Config.save_config(config_path, config)
|
|
136
|
+
puts "Project config updated to use profile \"#{name}\"."
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
desc 'extract [NAME]', "Save this project's inline credentials as a named profile"
|
|
140
|
+
long_desc <<~DESC
|
|
141
|
+
Reads the inline hostname and api_token from this project's .wisco/config.json,
|
|
142
|
+
saves them as a named profile in ~/.wisco/profiles.yaml, then updates config.json
|
|
143
|
+
to reference the profile instead of storing inline credentials.
|
|
144
|
+
|
|
145
|
+
If NAME is omitted, a name is suggested based on the project's configured hostname.
|
|
146
|
+
DESC
|
|
147
|
+
def extract(name = nil)
|
|
148
|
+
config_path = Wisco.config_path(Dir.pwd)
|
|
149
|
+
unless File.exist?(config_path)
|
|
150
|
+
Wisco::TerminalOutput.emit_error("Error: No #{Wisco::WISCO_DIR}/#{Wisco::CONFIG_FILENAME} found in #{Dir.pwd}.")
|
|
151
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco init' first.")
|
|
152
|
+
exit 1
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
config = Wisco::Config.load_config(config_path)
|
|
156
|
+
api_cfg = config['workato_developer_api'] || {}
|
|
157
|
+
hostname = api_cfg['hostname'].to_s.strip
|
|
158
|
+
api_token = api_cfg['api_token'].to_s.strip
|
|
159
|
+
|
|
160
|
+
if hostname.empty? || api_token.empty?
|
|
161
|
+
Wisco::TerminalOutput.emit_error('Error: This project does not have inline credentials to extract.')
|
|
162
|
+
Wisco::TerminalOutput.emit_error(" Use 'wisco profile use <name>' to attach an existing profile.")
|
|
163
|
+
exit 1
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
duplicate = Wisco::Profiles.find_duplicate(hostname, api_token)
|
|
167
|
+
if duplicate
|
|
168
|
+
puts "A profile with these credentials already exists: #{duplicate} (#{hostname})"
|
|
169
|
+
print 'Reference existing profile instead of creating a new one? (y/n): '
|
|
170
|
+
if $stdin.gets.strip.downcase == 'y'
|
|
171
|
+
api_cfg.delete('hostname')
|
|
172
|
+
api_cfg.delete('api_token')
|
|
173
|
+
api_cfg['profile'] = duplicate
|
|
174
|
+
Wisco::Config.save_config(config_path, config)
|
|
175
|
+
puts "Project config updated to reference profile \"#{duplicate}\"."
|
|
176
|
+
return
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
if name.nil?
|
|
181
|
+
suggestion = Wisco::Profiles.suggest_name(hostname)
|
|
182
|
+
name = Wisco::Profiles.prompt_name(suggestion: suggestion)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if Wisco::Profiles.exists?(name)
|
|
186
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{name}\" already exists. Choose a different name or use 'wisco profile edit #{name}'.")
|
|
187
|
+
exit 1
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
Wisco::Profiles.set(name, hostname, api_token)
|
|
191
|
+
puts "Profile \"#{name}\" created in #{Wisco::Profiles.profiles_path}."
|
|
192
|
+
|
|
193
|
+
api_cfg.delete('hostname')
|
|
194
|
+
api_cfg.delete('api_token')
|
|
195
|
+
api_cfg['profile'] = name
|
|
196
|
+
Wisco::Config.save_config(config_path, config)
|
|
197
|
+
puts "Project config updated to reference profile \"#{name}\"."
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
desc 'current', 'Show which profile or credentials this project is using'
|
|
201
|
+
def current
|
|
202
|
+
config_path = Wisco.config_path(Dir.pwd)
|
|
203
|
+
unless File.exist?(config_path)
|
|
204
|
+
Wisco::TerminalOutput.emit_error("Error: No #{Wisco::WISCO_DIR}/#{Wisco::CONFIG_FILENAME} found in #{Dir.pwd}.")
|
|
205
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco init' first.")
|
|
206
|
+
exit 1
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
config = Wisco::Config.load_config(config_path)
|
|
210
|
+
api_cfg = config['workato_developer_api'] || {}
|
|
211
|
+
prof_name = api_cfg['profile'].to_s.strip
|
|
212
|
+
hostname = api_cfg['hostname'].to_s.strip
|
|
213
|
+
api_token = api_cfg['api_token'].to_s.strip
|
|
214
|
+
|
|
215
|
+
if !prof_name.empty?
|
|
216
|
+
profile = Wisco::Profiles.get(prof_name)
|
|
217
|
+
if profile.nil?
|
|
218
|
+
Wisco::TerminalOutput.emit_error("This project references profile \"#{prof_name}\" which does not exist.")
|
|
219
|
+
Wisco::TerminalOutput.emit_error("Run 'wisco profile list' to see available profiles.")
|
|
220
|
+
exit 1
|
|
221
|
+
end
|
|
222
|
+
puts "This project uses profile: #{prof_name}"
|
|
223
|
+
puts " hostname: #{profile['hostname']}"
|
|
224
|
+
puts " api_token: #{Wisco::Profiles.mask_token(profile['api_token'])}"
|
|
225
|
+
elsif !hostname.empty?
|
|
226
|
+
puts 'This project uses inline credentials (no profile).'
|
|
227
|
+
puts " hostname: #{hostname}"
|
|
228
|
+
puts " api_token: #{Wisco::Profiles.mask_token(api_token)}"
|
|
229
|
+
else
|
|
230
|
+
puts 'This project has no Workato API credentials configured.'
|
|
231
|
+
puts "Run 'wisco init' or 'wisco profile use <name>' to configure."
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
data/lib/wisco/config.rb
CHANGED
|
@@ -2,10 +2,32 @@ module Wisco
|
|
|
2
2
|
module Config
|
|
3
3
|
module_function
|
|
4
4
|
|
|
5
|
-
# Ensures workato_developer_api hostname and api_token are
|
|
6
|
-
#
|
|
5
|
+
# Ensures workato_developer_api hostname and api_token are available in config.
|
|
6
|
+
# Resolves profile references, handles conflicts, and prompts for missing inline values.
|
|
7
|
+
# When a profile is used, credentials are injected into the in-memory config so all
|
|
8
|
+
# callers can continue to use config.dig('workato_developer_api', 'hostname') etc.
|
|
7
9
|
def ensure_api_config(config, config_path)
|
|
8
|
-
api_cfg
|
|
10
|
+
api_cfg = config['workato_developer_api'] ||= {}
|
|
11
|
+
has_profile = !api_cfg['profile'].to_s.strip.empty?
|
|
12
|
+
has_inline = !api_cfg['hostname'].to_s.strip.empty? && !api_cfg['api_token'].to_s.strip.empty?
|
|
13
|
+
|
|
14
|
+
if has_profile && has_inline
|
|
15
|
+
resolve_profile_conflict(api_cfg, config, config_path)
|
|
16
|
+
has_profile = !api_cfg['profile'].to_s.strip.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if has_profile
|
|
20
|
+
profile_name = api_cfg['profile']
|
|
21
|
+
profile = Wisco::Profiles.get(profile_name)
|
|
22
|
+
if profile.nil?
|
|
23
|
+
Wisco::TerminalOutput.emit_error("Error: Profile \"#{profile_name}\" not found in #{Wisco::Profiles.profiles_path}.")
|
|
24
|
+
Wisco::TerminalOutput.emit_error(" Run 'wisco profile list' to see available profiles.")
|
|
25
|
+
exit 1
|
|
26
|
+
end
|
|
27
|
+
api_cfg['hostname'] = profile['hostname']
|
|
28
|
+
api_cfg['api_token'] = profile['api_token']
|
|
29
|
+
return config
|
|
30
|
+
end
|
|
9
31
|
|
|
10
32
|
if api_cfg['hostname'].nil? || api_cfg['hostname'].strip.empty?
|
|
11
33
|
print 'Workato API hostname not configured. Enter hostname (e.g. app.au.workato.com): '
|
|
@@ -21,6 +43,34 @@ module Wisco
|
|
|
21
43
|
config
|
|
22
44
|
end
|
|
23
45
|
|
|
46
|
+
def resolve_profile_conflict(api_cfg, config, config_path)
|
|
47
|
+
Wisco::TerminalOutput.emit_warning('Warning: This project has both a profile reference and inline credentials.')
|
|
48
|
+
puts " Profile: #{api_cfg['profile']}"
|
|
49
|
+
puts " Hostname: #{api_cfg['hostname']}"
|
|
50
|
+
puts
|
|
51
|
+
puts 'Which should be kept?'
|
|
52
|
+
puts " 1. Profile reference (\"#{api_cfg['profile']}\")"
|
|
53
|
+
puts ' 2. Inline credentials'
|
|
54
|
+
loop do
|
|
55
|
+
print 'Enter 1 or 2: '
|
|
56
|
+
case $stdin.gets.strip
|
|
57
|
+
when '1'
|
|
58
|
+
api_cfg.delete('hostname')
|
|
59
|
+
api_cfg.delete('api_token')
|
|
60
|
+
save_config(config_path, config)
|
|
61
|
+
puts "Inline credentials removed. Using profile \"#{api_cfg['profile']}\"."
|
|
62
|
+
return
|
|
63
|
+
when '2'
|
|
64
|
+
api_cfg.delete('profile')
|
|
65
|
+
save_config(config_path, config)
|
|
66
|
+
puts 'Profile reference removed. Using inline credentials.'
|
|
67
|
+
return
|
|
68
|
+
else
|
|
69
|
+
warn 'Please enter 1 or 2.'
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
24
74
|
def load_config(path)
|
|
25
75
|
return {} unless File.exist?(path)
|
|
26
76
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'io/console'
|
|
4
|
+
|
|
5
|
+
module Wisco
|
|
6
|
+
module Profiles
|
|
7
|
+
PROFILES_DIR = File.join(Dir.home, '.wisco').freeze
|
|
8
|
+
PROFILES_FILENAME = 'profiles.yaml'.freeze
|
|
9
|
+
|
|
10
|
+
HOSTNAMES = [
|
|
11
|
+
{ region: 'US Data Center', hostname: 'www.workato.com' },
|
|
12
|
+
{ region: 'EU Data Center', hostname: 'app.eu.workato.com' },
|
|
13
|
+
{ region: 'JP Data Center', hostname: 'app.jp.workato.com' },
|
|
14
|
+
{ region: 'SG Data Center', hostname: 'app.sg.workato.com' },
|
|
15
|
+
{ region: 'AU Data Center', hostname: 'app.au.workato.com' },
|
|
16
|
+
{ region: 'IL Data Center', hostname: 'app.il.workato.com' },
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
DATACENTER_NAMES = {
|
|
20
|
+
'www.workato.com' => 'us',
|
|
21
|
+
'app.eu.workato.com' => 'eu',
|
|
22
|
+
'app.jp.workato.com' => 'jp',
|
|
23
|
+
'app.sg.workato.com' => 'sg',
|
|
24
|
+
'app.au.workato.com' => 'au',
|
|
25
|
+
'app.il.workato.com' => 'il',
|
|
26
|
+
}.freeze
|
|
27
|
+
|
|
28
|
+
module_function
|
|
29
|
+
|
|
30
|
+
def profiles_path
|
|
31
|
+
File.join(PROFILES_DIR, PROFILES_FILENAME)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def load_profiles
|
|
35
|
+
return {} unless File.exist?(profiles_path)
|
|
36
|
+
|
|
37
|
+
YAML.safe_load(File.read(profiles_path)) || {}
|
|
38
|
+
rescue Psych::SyntaxError => e
|
|
39
|
+
Wisco::TerminalOutput.emit_error("Error: Could not parse #{profiles_path}: #{e.message}")
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def save_profiles(data)
|
|
44
|
+
FileUtils.mkdir_p(PROFILES_DIR)
|
|
45
|
+
File.write(profiles_path, YAML.dump(data))
|
|
46
|
+
rescue SystemCallError => e
|
|
47
|
+
Wisco::TerminalOutput.emit_error("Error: Could not write #{profiles_path}: #{e.message}")
|
|
48
|
+
exit 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def all
|
|
52
|
+
load_profiles.dig('workato_developer_api') || {}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def get(name)
|
|
56
|
+
all[name]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def set(name, hostname, api_token)
|
|
60
|
+
data = load_profiles
|
|
61
|
+
data['workato_developer_api'] ||= {}
|
|
62
|
+
data['workato_developer_api'][name] = { 'hostname' => hostname, 'api_token' => api_token }
|
|
63
|
+
save_profiles(data)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def delete(name)
|
|
67
|
+
data = load_profiles
|
|
68
|
+
data['workato_developer_api']&.delete(name)
|
|
69
|
+
save_profiles(data)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def exists?(name)
|
|
73
|
+
!get(name).nil?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def find_duplicate(hostname, api_token)
|
|
77
|
+
all.find { |_n, p| p['hostname'] == hostname && p['api_token'] == api_token }&.first
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def suggest_name(hostname)
|
|
81
|
+
DATACENTER_NAMES[hostname]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def mask_token(token)
|
|
85
|
+
return '****' if token.nil? || token.length <= 4
|
|
86
|
+
|
|
87
|
+
"****#{token[-4..]}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def prompt_token(prompt_text = 'API token (input hidden): ')
|
|
91
|
+
print prompt_text
|
|
92
|
+
token = $stdin.noecho(&:gets).strip
|
|
93
|
+
puts
|
|
94
|
+
token
|
|
95
|
+
rescue Errno::ENOTTY
|
|
96
|
+
# stdin is not a tty (e.g. piped input) — read plainly
|
|
97
|
+
print prompt_text
|
|
98
|
+
$stdin.gets.strip
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def prompt_hostname_selection(allow_skip: false)
|
|
102
|
+
puts 'Select your Workato instance:'
|
|
103
|
+
HOSTNAMES.each_with_index do |entry, i|
|
|
104
|
+
puts " #{i + 1}. #{entry[:hostname]} (#{entry[:region]})"
|
|
105
|
+
end
|
|
106
|
+
skip_hint = allow_skip ? ', or Enter to keep current' : ''
|
|
107
|
+
loop do
|
|
108
|
+
print "Enter number (1-#{HOSTNAMES.size})#{skip_hint}: "
|
|
109
|
+
input = $stdin.gets.strip
|
|
110
|
+
return nil if allow_skip && input.empty?
|
|
111
|
+
|
|
112
|
+
index = input.to_i
|
|
113
|
+
if index >= 1 && index <= HOSTNAMES.size
|
|
114
|
+
return HOSTNAMES[index - 1][:hostname]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
warn "Invalid selection. Please enter a number between 1 and #{HOSTNAMES.size}."
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def prompt_name(suggestion: nil)
|
|
122
|
+
if suggestion
|
|
123
|
+
print "Profile name [#{suggestion}]: "
|
|
124
|
+
input = $stdin.gets.strip
|
|
125
|
+
input.empty? ? suggestion : input
|
|
126
|
+
else
|
|
127
|
+
loop do
|
|
128
|
+
print 'Profile name: '
|
|
129
|
+
input = $stdin.gets.strip
|
|
130
|
+
return input unless input.empty?
|
|
131
|
+
|
|
132
|
+
warn 'Name cannot be blank.'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
data/lib/wisco/version.rb
CHANGED
data/lib/wisco.rb
CHANGED
|
@@ -15,6 +15,7 @@ module Wisco
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
require_relative 'wisco/config'
|
|
18
|
+
require_relative 'wisco/profile'
|
|
18
19
|
require_relative 'wisco/connector'
|
|
19
20
|
require_relative 'wisco/commands/init'
|
|
20
21
|
require_relative 'wisco/commands/list'
|
|
@@ -23,6 +24,7 @@ require_relative 'wisco/commands/fixtures'
|
|
|
23
24
|
require_relative 'wisco/commands/pull'
|
|
24
25
|
require_relative 'wisco/commands/push'
|
|
25
26
|
require_relative 'wisco/commands/schema'
|
|
27
|
+
require_relative 'wisco/commands/profile'
|
|
26
28
|
|
|
27
29
|
module Wisco
|
|
28
30
|
class CLI < Thor
|
|
@@ -53,8 +55,9 @@ module Wisco
|
|
|
53
55
|
|
|
54
56
|
desc 'init [PATH]', "Detect connector and initialise #{WISCO_DIR}/"
|
|
55
57
|
long_desc "Searches PATH (default: current directory) for a valid connector file and writes #{WISCO_DIR}/#{CONFIG_FILENAME}."
|
|
58
|
+
option :profile, type: :string, desc: 'Attach a named connection profile instead of entering credentials manually'
|
|
56
59
|
def init(path = nil)
|
|
57
|
-
Wisco::Commands::Init.run(path || Dir.pwd)
|
|
60
|
+
Wisco::Commands::Init.run(path || Dir.pwd, profile: options[:profile])
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
desc 'list [SUBCOMMAND] [PATH]', 'Show connector structure'
|
|
@@ -179,6 +182,20 @@ module Wisco
|
|
|
179
182
|
)
|
|
180
183
|
end
|
|
181
184
|
|
|
185
|
+
desc 'profile SUBCOMMAND ...ARGS', 'Manage connection profiles (~/.wisco/profiles.yaml)'
|
|
186
|
+
long_desc <<~DESC
|
|
187
|
+
Subcommands:
|
|
188
|
+
add [NAME] Create a new profile
|
|
189
|
+
list List all profiles
|
|
190
|
+
show NAME Show profile details
|
|
191
|
+
edit NAME Update a profile's hostname or API token
|
|
192
|
+
remove NAME Remove a profile
|
|
193
|
+
use NAME Attach a profile to this connector project
|
|
194
|
+
extract [NAME] Save this project's inline credentials as a profile
|
|
195
|
+
current Show which profile this project is using
|
|
196
|
+
DESC
|
|
197
|
+
subcommand 'profile', Wisco::Commands::Profile
|
|
198
|
+
|
|
182
199
|
desc 'schema INPUT_FILE [TARGET_DIR]', 'Generate a schema from a JSON or CSV sample file'
|
|
183
200
|
long_desc <<~DESC
|
|
184
201
|
Calls the Workato API to generate a schema from a sample JSON or CSV file.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wisco
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mbillington
|
|
@@ -71,12 +71,14 @@ files:
|
|
|
71
71
|
- lib/wisco/commands/fixtures.rb
|
|
72
72
|
- lib/wisco/commands/init.rb
|
|
73
73
|
- lib/wisco/commands/list.rb
|
|
74
|
+
- lib/wisco/commands/profile.rb
|
|
74
75
|
- lib/wisco/commands/pull.rb
|
|
75
76
|
- lib/wisco/commands/push.rb
|
|
76
77
|
- lib/wisco/commands/schema.rb
|
|
77
78
|
- lib/wisco/config.rb
|
|
78
79
|
- lib/wisco/connector.rb
|
|
79
80
|
- lib/wisco/path_utils.rb
|
|
81
|
+
- lib/wisco/profile.rb
|
|
80
82
|
- lib/wisco/terminal_output.rb
|
|
81
83
|
- lib/wisco/version.rb
|
|
82
84
|
- lib/wisco/workato_api.rb
|