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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7db313badb869b62a2bf9eab0b4558cecffe8946bb8acaa0664cb4d65e3641f
4
- data.tar.gz: fa68ec90e00dec3934be909a3f9b5cb122bd93a8dac3801733eba4f066f2f4f4
3
+ metadata.gz: dc712af30ea2647f51bf72fac2bcf59f57105efcd8382d831afcadceee912f5c
4
+ data.tar.gz: b49f08a071f353521d09469452e8508e31b78f4108545b6e723083e816ecc67d
5
5
  SHA512:
6
- metadata.gz: be79dffcc14ff47e9333e175a0f6c967c28646045ba35094bde7740903b9d64a44a510c98f47ff676b4baf902c651de7bf4beda8b4e0d69f61bc03411e861b41
7
- data.tar.gz: 5340d7f07bd891bc4ac456a576749cffe1cb948df63af61dbe0192e22d92393d4a61b4c3bdb5d642b99446a1af96a48df0d782394728f2b19fda78d4428ef75d
6
+ metadata.gz: 4029281c6717dc8eeef9b9b3d86018998c5846dea82d5703d10229b84bf990c8beb6d8c73928315804878b8c9ab6a5bc541b9e46b34ddef7d601c2590e354331
7
+ data.tar.gz: 7a4161111ce339ffe212cacdd08365f9464b79d02a729b492e04925ed2d2a9f10687fdcd79d0eef0fcadc321cf37c9a5fb05eca57f63f3d7da747b3438204e14
@@ -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
- if current && !current.strip.empty?
64
- puts "Current Workato hostname: #{current}"
65
- print 'Keep this? (y/n): '
66
- return if $stdin.gets.strip.downcase == 'y'
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
- puts 'Select your Workato instance:'
70
- HOSTNAMES.each_with_index do |entry, i|
71
- puts " #{i + 1}. #{entry[:hostname]} (#{entry[:region]})"
72
- end
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
- loop do
75
- print "Enter number (1-#{HOSTNAMES.size}): "
76
- input = $stdin.gets.strip
77
- index = input.to_i
78
- if index >= 1 && index <= HOSTNAMES.size
79
- api_cfg['hostname'] = HOSTNAMES[index - 1][:hostname]
80
- puts "Hostname set to: #{api_cfg['hostname']}"
81
- return
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
- hostname = config.dig('workato_developer_api', 'hostname')
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 present in config.
6
- # Prompts the user for any missing values and saves the updated config.
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 = config['workato_developer_api'] ||= {}
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
@@ -1,3 +1,3 @@
1
1
  module Wisco
2
- VERSION = '0.3.5'
2
+ VERSION = '0.3.6'
3
3
  end
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.5
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