wisco 0.3.4 → 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/bin/wisco +0 -0
- data/lib/wisco/assets/.github/workflows/deploy.yml.erb +0 -0
- data/lib/wisco/assets/.gitignore +3 -1
- data/lib/wisco/assets/Gemfile +0 -0
- data/lib/wisco/commands/exec.rb +0 -0
- data/lib/wisco/commands/fixtures.rb +0 -0
- data/lib/wisco/commands/init.rb +74 -31
- data/lib/wisco/commands/list.rb +0 -0
- data/lib/wisco/commands/profile.rb +236 -0
- data/lib/wisco/commands/pull.rb +0 -0
- data/lib/wisco/commands/push.rb +0 -0
- data/lib/wisco/commands/schema.rb +214 -0
- data/lib/wisco/config.rb +53 -3
- data/lib/wisco/connector.rb +0 -0
- data/lib/wisco/path_utils.rb +0 -0
- data/lib/wisco/profile.rb +137 -0
- data/lib/wisco/terminal_output.rb +0 -0
- data/lib/wisco/version.rb +1 -1
- data/lib/wisco/workato_api.rb +0 -0
- data/lib/wisco.rb +51 -1
- metadata +5 -2
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/bin/wisco
CHANGED
|
File without changes
|
|
File without changes
|
data/lib/wisco/assets/.gitignore
CHANGED
data/lib/wisco/assets/Gemfile
CHANGED
|
File without changes
|
data/lib/wisco/commands/exec.rb
CHANGED
|
File without changes
|
|
File without changes
|
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
|
|
data/lib/wisco/commands/list.rb
CHANGED
|
File without changes
|
|
@@ -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/commands/pull.rb
CHANGED
|
File without changes
|
data/lib/wisco/commands/push.rb
CHANGED
|
File without changes
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
require 'rest-client'
|
|
2
|
+
require 'json'
|
|
3
|
+
require_relative '../config'
|
|
4
|
+
require_relative '../terminal_output'
|
|
5
|
+
|
|
6
|
+
module Wisco
|
|
7
|
+
module Commands
|
|
8
|
+
module Schema
|
|
9
|
+
API_GENERATE_SCHEMA_PATH = '/api/sdk/generate_schema'
|
|
10
|
+
KEY_ORDER = %w[name label type of control_type convert_input convert_output].freeze
|
|
11
|
+
SYMBOL_VALUE_KEYS = %w[type of].freeze
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def run(input_file, target_dir, format:, ruby_options:, col_sep:, output:, debug:)
|
|
16
|
+
input_file = File.expand_path(input_file)
|
|
17
|
+
target_dir = File.expand_path(target_dir)
|
|
18
|
+
|
|
19
|
+
unless File.exist?(input_file)
|
|
20
|
+
Wisco::TerminalOutput.emit_error("Error: Input file not found: #{input_file}")
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ext = File.extname(input_file).downcase
|
|
25
|
+
unless %w[.json .csv].include?(ext)
|
|
26
|
+
Wisco::TerminalOutput.emit_error("Error: Unsupported file type '#{ext}'. Must be .json or .csv.")
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
config_path = Wisco.config_path(target_dir)
|
|
31
|
+
unless File.exist?(config_path)
|
|
32
|
+
Wisco::TerminalOutput.emit_error("Error: No #{Wisco::WISCO_DIR}/#{Wisco::CONFIG_FILENAME} found in #{target_dir}.")
|
|
33
|
+
Wisco::TerminalOutput.emit_error(" Run '#{Wisco::CLI_NAME} init' first.")
|
|
34
|
+
exit 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
config = Wisco::Config.load_config(config_path)
|
|
38
|
+
config = Wisco::Config.ensure_api_config(config, config_path)
|
|
39
|
+
hostname = config.dig('workato_developer_api', 'hostname')
|
|
40
|
+
api_token = config.dig('workato_developer_api', 'api_token')
|
|
41
|
+
|
|
42
|
+
schema = fetch_schema(input_file, hostname, api_token, col_sep: col_sep, debug: debug)
|
|
43
|
+
|
|
44
|
+
formatted = if format == 'json'
|
|
45
|
+
format_json(schema)
|
|
46
|
+
else
|
|
47
|
+
format_ruby(schema, style: ruby_options.to_sym)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
output_path = resolve_output_path(output, input_file, format)
|
|
51
|
+
|
|
52
|
+
if output_path
|
|
53
|
+
File.write(output_path, formatted + "\n")
|
|
54
|
+
puts "Written: #{output_path}"
|
|
55
|
+
else
|
|
56
|
+
puts formatted
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Resolves the output file path from the --output option value.
|
|
61
|
+
# nil → no --output flag; return nil (print to stdout)
|
|
62
|
+
# '' → --output with no value; derive from input_file + format
|
|
63
|
+
# other → explicit path; expand and use as-is
|
|
64
|
+
def resolve_output_path(output, input_file, format)
|
|
65
|
+
return nil if output.nil?
|
|
66
|
+
|
|
67
|
+
if output.empty?
|
|
68
|
+
file_ext = format == 'json' ? '.json' : '.rb'
|
|
69
|
+
dir = File.dirname(input_file)
|
|
70
|
+
base = File.basename(input_file, '.*')
|
|
71
|
+
File.join(dir, "#{base}.schema#{file_ext}")
|
|
72
|
+
else
|
|
73
|
+
File.expand_path(output)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ── API call ──────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
def fetch_schema(input_file, hostname, api_token, col_sep:, debug:)
|
|
80
|
+
ext = File.extname(input_file).delete_prefix('.') # 'json' or 'csv'
|
|
81
|
+
url = "https://#{hostname}#{API_GENERATE_SCHEMA_PATH}/#{ext}"
|
|
82
|
+
sample = File.read(input_file)
|
|
83
|
+
sample = wrap_if_array(sample) if ext == 'json'
|
|
84
|
+
|
|
85
|
+
payload = { sample: sample }
|
|
86
|
+
payload[:col_sep] = col_sep if ext == 'csv'
|
|
87
|
+
|
|
88
|
+
if debug
|
|
89
|
+
warn "[schema] url: #{url}"
|
|
90
|
+
warn "[schema] col_sep: #{col_sep}" if ext == 'csv'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
response = RestClient.post(
|
|
94
|
+
url,
|
|
95
|
+
payload.to_json,
|
|
96
|
+
content_type: :json,
|
|
97
|
+
accept: :json,
|
|
98
|
+
'Authorization' => "Bearer #{api_token}"
|
|
99
|
+
)
|
|
100
|
+
JSON.parse(response.body)
|
|
101
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
102
|
+
msg = begin
|
|
103
|
+
JSON.parse(e.response.body)['message']
|
|
104
|
+
rescue StandardError
|
|
105
|
+
e.message
|
|
106
|
+
end
|
|
107
|
+
Wisco::TerminalOutput.emit_error("Error generating schema: #{msg}")
|
|
108
|
+
exit 1
|
|
109
|
+
rescue StandardError => e
|
|
110
|
+
Wisco::TerminalOutput.emit_error("Error generating schema: #{e.message}")
|
|
111
|
+
exit 1
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# If the JSON content is a top-level array, wrap it in {"input": [...]}
|
|
115
|
+
# so the Workato API accepts it (it requires a top-level object).
|
|
116
|
+
# Returns the (possibly modified) JSON string; never modifies the source file.
|
|
117
|
+
def wrap_if_array(json_content)
|
|
118
|
+
parsed = JSON.parse(json_content)
|
|
119
|
+
return json_content unless parsed.is_a?(Array)
|
|
120
|
+
|
|
121
|
+
Wisco::TerminalOutput.emit_info('[INFO] Input JSON is a top-level array.')
|
|
122
|
+
Wisco::TerminalOutput.emit_info('[INFO] Wrapping in {"input": [...]} for Workato API compatibility.')
|
|
123
|
+
JSON.generate({ 'input' => parsed })
|
|
124
|
+
rescue JSON::ParseError
|
|
125
|
+
json_content # unparseable — send as-is and let the API report the error
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# ── JSON output ───────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
def format_json(schema)
|
|
131
|
+
JSON.pretty_generate(schema)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# ── Ruby output ───────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
# Renders the top-level schema array as a Ruby literal.
|
|
137
|
+
def format_ruby(schema, style:)
|
|
138
|
+
indent = 4
|
|
139
|
+
items = schema.map { |field| format_field(field, indent, style) }
|
|
140
|
+
inner = items.map { |item| "#{' ' * indent}#{item}" }.join(",\n")
|
|
141
|
+
"[\n#{inner}\n]"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Renders a single field hash. Recursively handles nested `properties`.
|
|
145
|
+
def format_field(field, base_indent, style)
|
|
146
|
+
# Reorder keys: KEY_ORDER first, then any extras; pull properties out separately
|
|
147
|
+
ordered = KEY_ORDER.select { |k| field.key?(k) }
|
|
148
|
+
extras = field.keys - KEY_ORDER - ['properties']
|
|
149
|
+
pairs = (ordered + extras).map { |k| [k, field[k]] }
|
|
150
|
+
props = field['properties']
|
|
151
|
+
|
|
152
|
+
cont_indent = base_indent + 2 # continuation indent (aligns keys after "{ ")
|
|
153
|
+
prop_indent = base_indent + 4 # properties array indent
|
|
154
|
+
|
|
155
|
+
if style == :single_line
|
|
156
|
+
kv_str = pairs.map { |k, v| "#{k}: #{ruby_scalar(k, v)}" }.join(', ')
|
|
157
|
+
|
|
158
|
+
if props
|
|
159
|
+
prop_lines = format_ruby_array(props, prop_indent, style)
|
|
160
|
+
"{ #{kv_str}, properties:\n#{prop_lines}\n#{' ' * base_indent}}"
|
|
161
|
+
else
|
|
162
|
+
"{ #{kv_str}}"
|
|
163
|
+
end
|
|
164
|
+
else
|
|
165
|
+
# multi_line: first pair on same line as {, rest on new lines
|
|
166
|
+
kv_lines = pairs.map { |k, v| "#{k}: #{ruby_scalar(k, v)}" }
|
|
167
|
+
first = kv_lines.shift
|
|
168
|
+
|
|
169
|
+
if kv_lines.empty? && !props
|
|
170
|
+
# Single pair, no properties
|
|
171
|
+
"{ #{first}}"
|
|
172
|
+
elsif props
|
|
173
|
+
rest = kv_lines.map { |l| "#{' ' * cont_indent}#{l}" }
|
|
174
|
+
prop_line = "#{' ' * cont_indent}properties:"
|
|
175
|
+
prop_lines = format_ruby_array(props, prop_indent, style)
|
|
176
|
+
parts = ["{ #{first}"] + rest + [prop_line]
|
|
177
|
+
"#{parts.join(",\n")}\n#{prop_lines}\n#{' ' * base_indent}}"
|
|
178
|
+
else
|
|
179
|
+
rest = kv_lines.map { |l| "#{' ' * cont_indent}#{l}" }
|
|
180
|
+
parts = ["{ #{first}"] + rest
|
|
181
|
+
"#{parts.join(",\n")}}"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Renders a properties array (a list of nested fields) at the given indent.
|
|
187
|
+
# The opening/closing brackets sit at `indent` spaces; items are indented
|
|
188
|
+
# a further 4 spaces inside the brackets.
|
|
189
|
+
def format_ruby_array(fields, indent, style)
|
|
190
|
+
item_indent = indent + 4
|
|
191
|
+
items = fields.map { |f| format_field(f, item_indent, style) }
|
|
192
|
+
inner = items.map { |item| "#{' ' * item_indent}#{item}" }.join(",\n")
|
|
193
|
+
"#{' ' * indent}[\n#{inner}\n#{' ' * indent}]"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Returns the Ruby literal string for a scalar value.
|
|
197
|
+
def ruby_scalar(key, value)
|
|
198
|
+
if SYMBOL_VALUE_KEYS.include?(key) && value.is_a?(String)
|
|
199
|
+
":#{value}"
|
|
200
|
+
elsif value.is_a?(String)
|
|
201
|
+
"'#{value.gsub('\\', '\\\\\\\\').gsub("'", "\\\\'")}'"
|
|
202
|
+
elsif value.nil?
|
|
203
|
+
'nil'
|
|
204
|
+
elsif value == true || value == false
|
|
205
|
+
value.to_s
|
|
206
|
+
elsif value.is_a?(Numeric)
|
|
207
|
+
value.to_s
|
|
208
|
+
else
|
|
209
|
+
value.inspect
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
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
|
|
data/lib/wisco/connector.rb
CHANGED
|
File without changes
|
data/lib/wisco/path_utils.rb
CHANGED
|
File without changes
|
|
@@ -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
|
|
File without changes
|
data/lib/wisco/version.rb
CHANGED
data/lib/wisco/workato_api.rb
CHANGED
|
File without changes
|
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'
|
|
@@ -22,6 +23,8 @@ require_relative 'wisco/commands/exec'
|
|
|
22
23
|
require_relative 'wisco/commands/fixtures'
|
|
23
24
|
require_relative 'wisco/commands/pull'
|
|
24
25
|
require_relative 'wisco/commands/push'
|
|
26
|
+
require_relative 'wisco/commands/schema'
|
|
27
|
+
require_relative 'wisco/commands/profile'
|
|
25
28
|
|
|
26
29
|
module Wisco
|
|
27
30
|
class CLI < Thor
|
|
@@ -52,8 +55,9 @@ module Wisco
|
|
|
52
55
|
|
|
53
56
|
desc 'init [PATH]', "Detect connector and initialise #{WISCO_DIR}/"
|
|
54
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'
|
|
55
59
|
def init(path = nil)
|
|
56
|
-
Wisco::Commands::Init.run(path || Dir.pwd)
|
|
60
|
+
Wisco::Commands::Init.run(path || Dir.pwd, profile: options[:profile])
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
desc 'list [SUBCOMMAND] [PATH]', 'Show connector structure'
|
|
@@ -177,5 +181,51 @@ module Wisco
|
|
|
177
181
|
debug: options[:debug]
|
|
178
182
|
)
|
|
179
183
|
end
|
|
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
|
+
|
|
199
|
+
desc 'schema INPUT_FILE [TARGET_DIR]', 'Generate a schema from a JSON or CSV sample file'
|
|
200
|
+
long_desc <<~DESC
|
|
201
|
+
Calls the Workato API to generate a schema from a sample JSON or CSV file.
|
|
202
|
+
File type is auto-detected from the extension (.json or .csv).
|
|
203
|
+
Output defaults to Ruby hash format; use --format=json for raw JSON.
|
|
204
|
+
|
|
205
|
+
Requires workato_developer_api hostname and api_token in #{WISCO_DIR}/#{CONFIG_FILENAME}.
|
|
206
|
+
If not set, you will be prompted on first run.
|
|
207
|
+
DESC
|
|
208
|
+
option :format, type: :string, default: 'ruby', enum: %w[ruby json],
|
|
209
|
+
desc: 'Output format: ruby (default) or json'
|
|
210
|
+
option :ruby_options, type: :string, default: 'multi_line', enum: %w[single_line multi_line],
|
|
211
|
+
desc: 'Ruby output style: single_line or multi_line (default)'
|
|
212
|
+
option :'col-sep', type: :string, default: 'comma',
|
|
213
|
+
enum: %w[comma space tab colon semicolon pipe],
|
|
214
|
+
desc: 'CSV column separator (default: comma)'
|
|
215
|
+
option :save, type: :string, lazy_default: '',
|
|
216
|
+
desc: 'Save output to file. Omit value to auto-name from input file ' \
|
|
217
|
+
'(e.g. input.json → input.schema.rb / input.schema.json)'
|
|
218
|
+
option :debug, type: :boolean, default: false, desc: 'Show API call details'
|
|
219
|
+
def schema(input_file, target_dir = nil)
|
|
220
|
+
Wisco::Commands::Schema.run(
|
|
221
|
+
input_file,
|
|
222
|
+
target_dir || Dir.pwd,
|
|
223
|
+
format: options[:format],
|
|
224
|
+
ruby_options: options[:ruby_options],
|
|
225
|
+
col_sep: options[:col_sep],
|
|
226
|
+
output: options[:save],
|
|
227
|
+
debug: options[:debug]
|
|
228
|
+
)
|
|
229
|
+
end
|
|
180
230
|
end
|
|
181
231
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -71,11 +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
|
|
77
|
+
- lib/wisco/commands/schema.rb
|
|
76
78
|
- lib/wisco/config.rb
|
|
77
79
|
- lib/wisco/connector.rb
|
|
78
80
|
- lib/wisco/path_utils.rb
|
|
81
|
+
- lib/wisco/profile.rb
|
|
79
82
|
- lib/wisco/terminal_output.rb
|
|
80
83
|
- lib/wisco/version.rb
|
|
81
84
|
- lib/wisco/workato_api.rb
|