wisco 0.3.4 → 0.3.5
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 +0 -0
- data/lib/wisco/commands/list.rb +0 -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 +0 -0
- data/lib/wisco/connector.rb +0 -0
- data/lib/wisco/path_utils.rb +0 -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 +33 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7db313badb869b62a2bf9eab0b4558cecffe8946bb8acaa0664cb4d65e3641f
|
|
4
|
+
data.tar.gz: fa68ec90e00dec3934be909a3f9b5cb122bd93a8dac3801733eba4f066f2f4f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be79dffcc14ff47e9333e175a0f6c967c28646045ba35094bde7740903b9d64a44a510c98f47ff676b4baf902c651de7bf4beda8b4e0d69f61bc03411e861b41
|
|
7
|
+
data.tar.gz: 5340d7f07bd891bc4ac456a576749cffe1cb948df63af61dbe0192e22d92393d4a61b4c3bdb5d642b99446a1af96a48df0d782394728f2b19fda78d4428ef75d
|
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
|
File without changes
|
data/lib/wisco/commands/list.rb
CHANGED
|
File without changes
|
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
|
File without changes
|
data/lib/wisco/connector.rb
CHANGED
|
File without changes
|
data/lib/wisco/path_utils.rb
CHANGED
|
File without changes
|
|
File without changes
|
data/lib/wisco/version.rb
CHANGED
data/lib/wisco/workato_api.rb
CHANGED
|
File without changes
|
data/lib/wisco.rb
CHANGED
|
@@ -22,6 +22,7 @@ require_relative 'wisco/commands/exec'
|
|
|
22
22
|
require_relative 'wisco/commands/fixtures'
|
|
23
23
|
require_relative 'wisco/commands/pull'
|
|
24
24
|
require_relative 'wisco/commands/push'
|
|
25
|
+
require_relative 'wisco/commands/schema'
|
|
25
26
|
|
|
26
27
|
module Wisco
|
|
27
28
|
class CLI < Thor
|
|
@@ -177,5 +178,37 @@ module Wisco
|
|
|
177
178
|
debug: options[:debug]
|
|
178
179
|
)
|
|
179
180
|
end
|
|
181
|
+
|
|
182
|
+
desc 'schema INPUT_FILE [TARGET_DIR]', 'Generate a schema from a JSON or CSV sample file'
|
|
183
|
+
long_desc <<~DESC
|
|
184
|
+
Calls the Workato API to generate a schema from a sample JSON or CSV file.
|
|
185
|
+
File type is auto-detected from the extension (.json or .csv).
|
|
186
|
+
Output defaults to Ruby hash format; use --format=json for raw JSON.
|
|
187
|
+
|
|
188
|
+
Requires workato_developer_api hostname and api_token in #{WISCO_DIR}/#{CONFIG_FILENAME}.
|
|
189
|
+
If not set, you will be prompted on first run.
|
|
190
|
+
DESC
|
|
191
|
+
option :format, type: :string, default: 'ruby', enum: %w[ruby json],
|
|
192
|
+
desc: 'Output format: ruby (default) or json'
|
|
193
|
+
option :ruby_options, type: :string, default: 'multi_line', enum: %w[single_line multi_line],
|
|
194
|
+
desc: 'Ruby output style: single_line or multi_line (default)'
|
|
195
|
+
option :'col-sep', type: :string, default: 'comma',
|
|
196
|
+
enum: %w[comma space tab colon semicolon pipe],
|
|
197
|
+
desc: 'CSV column separator (default: comma)'
|
|
198
|
+
option :save, type: :string, lazy_default: '',
|
|
199
|
+
desc: 'Save output to file. Omit value to auto-name from input file ' \
|
|
200
|
+
'(e.g. input.json → input.schema.rb / input.schema.json)'
|
|
201
|
+
option :debug, type: :boolean, default: false, desc: 'Show API call details'
|
|
202
|
+
def schema(input_file, target_dir = nil)
|
|
203
|
+
Wisco::Commands::Schema.run(
|
|
204
|
+
input_file,
|
|
205
|
+
target_dir || Dir.pwd,
|
|
206
|
+
format: options[:format],
|
|
207
|
+
ruby_options: options[:ruby_options],
|
|
208
|
+
col_sep: options[:col_sep],
|
|
209
|
+
output: options[:save],
|
|
210
|
+
debug: options[:debug]
|
|
211
|
+
)
|
|
212
|
+
end
|
|
180
213
|
end
|
|
181
214
|
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.5
|
|
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
|
|
@@ -73,6 +73,7 @@ files:
|
|
|
73
73
|
- lib/wisco/commands/list.rb
|
|
74
74
|
- lib/wisco/commands/pull.rb
|
|
75
75
|
- lib/wisco/commands/push.rb
|
|
76
|
+
- lib/wisco/commands/schema.rb
|
|
76
77
|
- lib/wisco/config.rb
|
|
77
78
|
- lib/wisco/connector.rb
|
|
78
79
|
- lib/wisco/path_utils.rb
|