razor-client 0.15.1 → 0.16.0
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 +7 -0
- data/NEWS.md +22 -0
- data/bin/razor +24 -0
- data/lib/razor/cli/command.rb +139 -0
- data/lib/razor/cli/document.rb +8 -4
- data/lib/razor/cli/format.rb +46 -22
- data/lib/razor/cli/navigate.rb +27 -149
- data/lib/razor/cli/parse.rb +21 -5
- data/lib/razor/cli/query.rb +68 -0
- data/lib/razor/cli/table_format.rb +41 -0
- data/lib/razor/cli/transforms.rb +18 -0
- data/lib/razor/cli/version.rb +1 -1
- data/lib/razor/cli/views.yaml +38 -0
- data/lib/razor/cli.rb +5 -2
- data/spec/cli/format_spec.rb +95 -5
- data/spec/cli/navigate_spec.rb +49 -5
- data/spec/cli/parse_spec.rb +21 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/argument_formatting/should_allow_in_string.yml +322 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_API_command_help_for_razor_--api_command_--help_.yml +110 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_API_command_help_for_razor_--api_command_help_.yml +110 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_API_command_help_for_razor_--api_help_command_.yml +110 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_API_command_help_for_razor_--help_--api_command_.yml +110 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_API_command_help_for_razor_-a_-h_command_.yml +110 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_API_command_help_for_razor_-a_command_-h_.yml +110 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object.yml +181 -49
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object_with_unicode.yml +121 -46
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_array_then_hash_.yml +68 -65
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_fail_with_mixed_types_hash_then_array_.yml +177 -45
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_query_parameters/should_append_limit.yml +69 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_query_parameters/should_append_start.yml +69 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_query_parameters/should_not_fail_when_query_returns_details_for_one_item.yml +421 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_query_parameters/should_store_query_without_query_parameters.yml +773 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_query_parameters/should_throw_an_error_if_the_query_parameter_is_not_in_the_API.yml +36 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_query_parameters/should_throw_an_error_if_the_query_parameter_is_not_in_the_API_from_a_single_item.yml +388 -0
- metadata +94 -82
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a069f6a924314034052a7cb2f0d1f3192110570e
|
4
|
+
data.tar.gz: df77eb2e540658b2d7af2dfd9991f6ba731156db
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dc9d5e138578e0d199bb6b044286aa357323810ba2e0d488aa0942ec7ee6ef13e415b6b842481a2c3c2aec80371684b832b843a0cf84c3211756c1da81e20017
|
7
|
+
data.tar.gz: dc84915443ae5e8b14291fd878ed7472ed84ce412e1e276da2558ca40afe8707751235ceacba56c63a13249ee5a7ca8468324ad190619194f5f665ca9bc383f1
|
data/NEWS.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Razor Client Release Notes
|
2
2
|
|
3
|
+
## 0.16.0 - 2015-01-05
|
4
|
+
|
5
|
+
* BUGFIX: Commands were not always including authentication
|
6
|
+
information in every request.
|
7
|
+
* IMPROVEMENT: Ruby version compatibility: Now works with Ruby < 1.9.2.
|
8
|
+
* NEW: Separate API and CLI help examples: There are now two formats for help
|
9
|
+
examples. The new CLI format shows help text as a standard razor-client
|
10
|
+
command. CLI is used by default. The API format is the same as before,
|
11
|
+
and will show examples in JSON format.
|
12
|
+
* NEW: The `events` collection is new and has a special client-side display.
|
13
|
+
* NEW: RAZOR_API and `razor -u $url` URLs need to be explicit about `http:`
|
14
|
+
and `https:`.
|
15
|
+
* NEW: Event queries can be limited (`razor events --limit 5`) and offset
|
16
|
+
(`razor events --start 5`). This also works for `razor nodes $name log`.
|
17
|
+
* IMPROVEMENT: Viewing all columns in a query is now possible via
|
18
|
+
`razor $collection_path --full` rather than `razor --full $collection_path`.
|
19
|
+
* NEW: razor-client now has an 'insecure' flag to ignore SSL verification
|
20
|
+
errors.
|
21
|
+
* IMPROVEMENT: Argument types were previously not very context-aware. Now,
|
22
|
+
for example, names can include the '=' character.
|
23
|
+
* BUGFIX: A reasonable error will be thrown if help is requested but does not exist.
|
24
|
+
|
3
25
|
## 0.15.1 - 2014-06-12
|
4
26
|
|
5
27
|
Server version compatibility
|
data/bin/razor
CHANGED
@@ -37,16 +37,40 @@ if parse.show_help? and not parse.show_command_help?
|
|
37
37
|
exit 0
|
38
38
|
end
|
39
39
|
|
40
|
+
def unexpected_error(e)
|
41
|
+
die <<-ERROR
|
42
|
+
An unexpected error has occurred.
|
43
|
+
|
44
|
+
Backtrace:
|
45
|
+
#{e.backtrace.take(10).join('
|
46
|
+
')}
|
47
|
+
|
48
|
+
Error: #{e}
|
49
|
+
|
50
|
+
Please inspect server logs for the cause, then report issue to:
|
51
|
+
https://tickets.puppetlabs.com/browse/RAZOR
|
52
|
+
|
53
|
+
ERROR
|
54
|
+
end
|
55
|
+
|
40
56
|
begin
|
41
57
|
document = parse.navigate.get_document
|
42
58
|
url = parse.navigate.last_url
|
43
59
|
puts "From #{url}:\n\n#{format_document document, parse}\n\n"
|
60
|
+
rescue OptionParser::InvalidOption => e
|
61
|
+
# Occurs when invalid flags are passed in the navigation.
|
62
|
+
die e.message + "\nTry 'razor --help' for more information"
|
44
63
|
rescue SocketError, Errno::ECONNREFUSED => e
|
45
64
|
puts "Error: Could not connect to the server at #{parse.api_url}"
|
46
65
|
puts " #{e}\n"
|
47
66
|
die
|
67
|
+
rescue RestClient::SSLCertificateNotVerified
|
68
|
+
puts "Error: SSL certificate could not be verified against known CA certificates."
|
69
|
+
puts " To turn off verification, use the -k or --insecure option."
|
70
|
+
die
|
48
71
|
rescue RestClient::Exception => e
|
49
72
|
r = e.response
|
73
|
+
unexpected_error(e) if r.nil?
|
50
74
|
puts "Error from doing #{r.args[:method].to_s.upcase} #{r.args[:url]}"
|
51
75
|
puts e.message
|
52
76
|
begin
|
@@ -0,0 +1,139 @@
|
|
1
|
+
class Razor::CLI::Command
|
2
|
+
def initialize(parse, navigate, commands, segments)
|
3
|
+
@parse = parse
|
4
|
+
@navigate = navigate
|
5
|
+
@commands = commands
|
6
|
+
@segments = segments
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
# @todo lutter 2013-08-16: None of this has any tests, and error
|
11
|
+
# handling is heinous at best
|
12
|
+
cmd, body = extract_command
|
13
|
+
# Ensure that we copy authentication data from our previous URL.
|
14
|
+
url = URI.parse(cmd["id"])
|
15
|
+
if @doc_resource
|
16
|
+
url = URI.parse(url.to_s)
|
17
|
+
end
|
18
|
+
|
19
|
+
if @parse.show_command_help?
|
20
|
+
@navigate.json_get(url)
|
21
|
+
else
|
22
|
+
if body.empty?
|
23
|
+
raise Razor::CLI::Error,
|
24
|
+
"No arguments for command (did you forget --json ?)"
|
25
|
+
end
|
26
|
+
result = @navigate.json_post(url, body)
|
27
|
+
# Get actual object from the id.
|
28
|
+
result = result.merge(@navigate.json_get(URI.parse(result['id']))) if result['id']
|
29
|
+
result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def command(name)
|
34
|
+
@command ||= @commands.find { |coll| coll["name"] == name }
|
35
|
+
end
|
36
|
+
|
37
|
+
def extract_command
|
38
|
+
cmd = command(@segments.shift)
|
39
|
+
@cmd_url = URI.parse(cmd['id'])
|
40
|
+
body = {}
|
41
|
+
until @segments.empty?
|
42
|
+
argument = @segments.shift
|
43
|
+
if argument =~ /\A--([a-z-]+)(=(.+))?\Z/
|
44
|
+
# `--arg=value` or `--arg value`
|
45
|
+
arg, value = [$1, $3]
|
46
|
+
value = @segments.shift if value.nil? && @segments[0] !~ /^--/
|
47
|
+
body[arg] = convert_arg(cmd["name"], arg, value, body[arg])
|
48
|
+
else
|
49
|
+
raise ArgumentError, "Unexpected argument #{argument}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
begin
|
54
|
+
body = MultiJson::load(File::read(body["json"])) if body["json"]
|
55
|
+
rescue MultiJson::LoadError
|
56
|
+
raise Razor::CLI::Error, "File #{body["json"]} is not valid JSON"
|
57
|
+
rescue Errno::ENOENT
|
58
|
+
raise Razor::CLI::Error, "File #{body["json"]} not found"
|
59
|
+
rescue Errno::EACCES
|
60
|
+
raise Razor::CLI::Error,
|
61
|
+
"Permission to read file #{body["json"]} denied"
|
62
|
+
end
|
63
|
+
[cmd, body]
|
64
|
+
end
|
65
|
+
|
66
|
+
def cmd_schema(cmd_name)
|
67
|
+
begin
|
68
|
+
@navigate.json_get(@cmd_url)['schema']
|
69
|
+
rescue RestClient::ResourceNotFound => _
|
70
|
+
raise VersionCompatibilityError, 'Server must supply the expected datatypes for command arguments; use `--json` or upgrade razor-server'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def arg_type(cmd_name, arg_name)
|
75
|
+
# Short-circuit to allow this as a work-around for backwards compatibility.
|
76
|
+
return nil if arg_name == 'json'
|
77
|
+
cmd = cmd_schema(cmd_name)
|
78
|
+
cmd && cmd[arg_name] && cmd[arg_name]['type'] or nil
|
79
|
+
end
|
80
|
+
|
81
|
+
# `cmd_name`: The name of the command being executed.
|
82
|
+
# `arg_name`: The name of the argument being formatted.
|
83
|
+
# `value`: The original value provided by the user.
|
84
|
+
# `existing_value`: The value already assigned to this variable
|
85
|
+
# by previous calls to this method. The new `value` will be
|
86
|
+
# concatenated to an array or hash if an array/hash is
|
87
|
+
# accepted by the command for the given argument.
|
88
|
+
def convert_arg(cmd_name, arg_name, value, existing_value)
|
89
|
+
value = nil if value == "null"
|
90
|
+
|
91
|
+
argument_type = arg_type(cmd_name, arg_name)
|
92
|
+
|
93
|
+
# This might be helpful, since there's no other method for debug-level logging on the client.
|
94
|
+
puts "Formatting argument #{arg_name} with value #{value} as #{argument_type}\n" if @parse.dump_response?
|
95
|
+
|
96
|
+
case argument_type
|
97
|
+
when "array"
|
98
|
+
existing_value ||= []
|
99
|
+
begin
|
100
|
+
MultiJson::load(value).tap do |value|
|
101
|
+
value = Array(value)
|
102
|
+
existing_value + value
|
103
|
+
end
|
104
|
+
rescue MultiJson::LoadError => _
|
105
|
+
existing_value + Array(value)
|
106
|
+
end
|
107
|
+
when "object"
|
108
|
+
existing_value ||= {}
|
109
|
+
begin
|
110
|
+
if value =~ /\A(.+?)=(.+)?\z/
|
111
|
+
# `--arg name=value`
|
112
|
+
existing_value.merge($1 => $2)
|
113
|
+
else
|
114
|
+
MultiJson::load(value).tap do |value|
|
115
|
+
value.is_a?(Hash) or raise ArgumentError, "Invalid object for argument '#{arg_name}'"
|
116
|
+
existing_value.merge(value)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
rescue MultiJson::LoadError => error
|
120
|
+
raise ArgumentError, "Invalid object for argument '#{arg_name}': #{error.message}"
|
121
|
+
end
|
122
|
+
when "boolean"
|
123
|
+
["true", nil].include?(value)
|
124
|
+
when "number"
|
125
|
+
begin
|
126
|
+
Integer(value)
|
127
|
+
rescue ArgumentError
|
128
|
+
raise ArgumentError, "Invalid integer for argument '#{arg_name}': #{value}"
|
129
|
+
end
|
130
|
+
when "null"
|
131
|
+
raise ArgumentError, "Expected nothing for argument '#{arg_name}', but was: '#{value}'" unless value.nil?
|
132
|
+
nil
|
133
|
+
when "string", nil # `nil` for 'might be an alias, send as-is'
|
134
|
+
value
|
135
|
+
else
|
136
|
+
raise Razor::CLI::Error, "Unexpected datatype '#{argument_type}' for argument #{arg_name}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/lib/razor/cli/document.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
3
|
module Razor::CLI
|
4
|
+
class HideColumnError < RuntimeError; end
|
4
5
|
class Document
|
5
6
|
extend Forwardable
|
6
7
|
attr_reader 'spec', 'items', 'format_view', 'original_items'
|
@@ -42,10 +43,13 @@ module Razor::CLI
|
|
42
43
|
item_spec = (item_format_spec[1] or {})
|
43
44
|
item_label = item_format_spec[0]
|
44
45
|
item_column = (item_spec['+column'] or item_label)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
begin
|
47
|
+
value = Razor::CLI::Views.transform(item[item_column], item_spec['+format'])
|
48
|
+
[item_label, value]
|
49
|
+
rescue Razor::CLI::HideColumnError
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end.reject {|k| k.nil? }
|
49
53
|
].tap do |hash|
|
50
54
|
# Re-add the special 'command' key and value if the key isn't already there.
|
51
55
|
hash['command'] = @command if @command and not hash.has_key?('command')
|
data/lib/razor/cli/format.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'forwardable'
|
2
|
-
require '
|
2
|
+
require 'command_line_reporter'
|
3
3
|
|
4
4
|
module Razor::CLI
|
5
5
|
module Format
|
@@ -20,11 +20,11 @@ module Razor::CLI
|
|
20
20
|
|
21
21
|
def format_document(doc, parse = nil)
|
22
22
|
format = parse && parse.format
|
23
|
-
arguments = parse && parse.
|
23
|
+
arguments = parse && parse.stripped_args
|
24
24
|
doc = Razor::CLI::Document.new(doc, format)
|
25
25
|
|
26
26
|
return "There are no items for this query." if doc.items.empty?
|
27
|
-
return
|
27
|
+
return format_command_help(doc, parse.show_api_help?) if parse && parse.show_command_help?
|
28
28
|
|
29
29
|
case (doc.format_view['+layout'] or 'list')
|
30
30
|
when 'list'
|
@@ -43,25 +43,8 @@ module Razor::CLI
|
|
43
43
|
private
|
44
44
|
def get_table(doc, formatting)
|
45
45
|
# Use the formatting if it exists, otherwise build from the data.
|
46
|
-
|
47
|
-
|
48
|
-
table.rows = doc.map do |page|
|
49
|
-
headings.map do |heading|
|
50
|
-
page[heading]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
table.headings = headings
|
54
|
-
end.to_s
|
55
|
-
end
|
56
|
-
|
57
|
-
def get_headers(doc)
|
58
|
-
[].tap do |headers|
|
59
|
-
doc.map do |page|
|
60
|
-
page.map do |item|
|
61
|
-
headers << item[0] unless headers.include?(item[0])
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
46
|
+
column_overrides = formatting['+show'] && formatting['+show'].keys
|
47
|
+
Razor::CLI::TableFormat.new.run(doc, column_overrides)
|
65
48
|
end
|
66
49
|
|
67
50
|
# We assume that all collections are homogenous
|
@@ -79,6 +62,47 @@ module Razor::CLI
|
|
79
62
|
end
|
80
63
|
end
|
81
64
|
|
65
|
+
def format_command_help(doc, show_api_help)
|
66
|
+
item = doc.items.first
|
67
|
+
raise Razor::CLI::Error, 'Could not find help for that entry' unless item.has_key?('help')
|
68
|
+
if show_api_help and (item['help'].has_key?('summary') or item['help'].has_key?('description'))
|
69
|
+
format_composed_help(item['help']).chomp
|
70
|
+
elsif item['help'].has_key?('summary') or item['help'].has_key?('description')
|
71
|
+
format_composed_help(item['help'], item['help']['examples']['cli']).chomp
|
72
|
+
else
|
73
|
+
format_full_help(item['help']).chomp
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def format_composed_help(object, examples = object['examples']['api'])
|
78
|
+
ret = ''
|
79
|
+
ret = ret + <<-SYNOPSIS if object.has_key?('summary')
|
80
|
+
# SYNOPSIS
|
81
|
+
#{object['summary']}
|
82
|
+
|
83
|
+
SYNOPSIS
|
84
|
+
ret = ret + <<-DESCRIPTION if object.has_key?('description')
|
85
|
+
# DESCRIPTION
|
86
|
+
#{object['description']}
|
87
|
+
|
88
|
+
#{object['schema']}
|
89
|
+
DESCRIPTION
|
90
|
+
ret = ret + <<-RETURNS if object.has_key?('returns')
|
91
|
+
# RETURNS
|
92
|
+
#{object['returns'].gsub(/^/, ' ')}
|
93
|
+
RETURNS
|
94
|
+
ret = ret + <<-EXAMPLES if object.has_key?('examples') && object['examples'].has_key?('cli')
|
95
|
+
# EXAMPLES
|
96
|
+
|
97
|
+
#{examples.gsub(/^/, ' ')}
|
98
|
+
EXAMPLES
|
99
|
+
ret
|
100
|
+
end
|
101
|
+
|
102
|
+
def format_full_help(object)
|
103
|
+
object['full']
|
104
|
+
end
|
105
|
+
|
82
106
|
def format_default_object(object, indent = 0 )
|
83
107
|
fields = display_fields(object)
|
84
108
|
key_indent = indent + fields.map {|f| f.length}.max
|
data/lib/razor/cli/navigate.rb
CHANGED
@@ -11,11 +11,14 @@ module Razor::CLI
|
|
11
11
|
@parse = parse
|
12
12
|
@segments = segments||[]
|
13
13
|
@doc = entrypoint
|
14
|
-
@
|
14
|
+
@username, @password = parse.api_url.userinfo.to_s.split(':')
|
15
|
+
@doc_resource = create_resource parse.api_url, {:accept => :json}
|
15
16
|
end
|
16
17
|
|
18
|
+
attr_accessor :doc_resource
|
19
|
+
|
17
20
|
def last_url
|
18
|
-
@
|
21
|
+
@doc_resource
|
19
22
|
end
|
20
23
|
|
21
24
|
def entrypoint
|
@@ -52,95 +55,16 @@ module Razor::CLI
|
|
52
55
|
if @segments.empty?
|
53
56
|
entrypoint
|
54
57
|
elsif query?
|
55
|
-
@
|
56
|
-
while @segments.any?
|
57
|
-
move_to @segments.shift
|
58
|
-
end
|
59
|
-
|
60
|
-
# Get the next level if it's a list of objects.
|
61
|
-
if @doc.is_a?(Hash) and @doc['items'].is_a?(Array)
|
62
|
-
@doc['items'] = @doc['items'].map do |item|
|
63
|
-
item.is_a?(Hash) && item.has_key?('id') ? json_get(item['id']) : item
|
64
|
-
end
|
65
|
-
end
|
66
|
-
@doc
|
58
|
+
Razor::CLI::Query.new(@parse, self, collections, @segments).run
|
67
59
|
elsif command?
|
68
|
-
|
69
|
-
# handling is heinous at best
|
70
|
-
cmd, body = extract_command
|
71
|
-
# Ensure that we copy authentication data from our previous URL.
|
72
|
-
url = cmd["id"]
|
73
|
-
if @doc_url
|
74
|
-
url = URI.parse(url.to_s)
|
75
|
-
url.userinfo = @doc_url.userinfo
|
76
|
-
end
|
77
|
-
|
78
|
-
if show_command_help?
|
79
|
-
json_get(url)
|
80
|
-
else
|
81
|
-
if body.empty?
|
82
|
-
raise Razor::CLI::Error,
|
83
|
-
"No arguments for command (did you forget --json ?)"
|
84
|
-
end
|
85
|
-
result = json_post(url, body)
|
86
|
-
# Get actual object from the id.
|
87
|
-
result = result.merge(json_get(result['id'])) if result['id']
|
88
|
-
result
|
89
|
-
end
|
60
|
+
Razor::CLI::Command.new(@parse, self, commands, @segments).run
|
90
61
|
else
|
91
|
-
raise NavigationError.new(@
|
62
|
+
raise NavigationError.new(@doc_resource, @segments, @doc)
|
92
63
|
end
|
93
64
|
end
|
94
65
|
|
95
|
-
def
|
96
|
-
|
97
|
-
@cmd_url = cmd['id']
|
98
|
-
body = {}
|
99
|
-
until @segments.empty?
|
100
|
-
argument = @segments.shift
|
101
|
-
if argument =~ /\A--([a-z-]+)(=(.+))?\Z/
|
102
|
-
# `--arg=value` or `--arg value`
|
103
|
-
arg, value = [$1, $3]
|
104
|
-
value = @segments.shift if value.nil? && @segments[0] !~ /^--/
|
105
|
-
if value =~ /\A(.+?)=(\S+)?\z/
|
106
|
-
# `--arg name=value`
|
107
|
-
unless body[arg].nil? or body[arg].is_a?(Hash)
|
108
|
-
# Error: `--arg value --arg name=value`
|
109
|
-
raise ArgumentError, "Cannot handle mixed types for argument #{arg}"
|
110
|
-
end
|
111
|
-
# Do not convert, assume the above is the conversion.
|
112
|
-
body[arg] = (body[arg].nil? ? {} : body[arg]).merge($1 => $2)
|
113
|
-
elsif body[arg].is_a?(Hash)
|
114
|
-
# Error: `--arg name=value --arg value`
|
115
|
-
raise ArgumentError, "Cannot handle mixed types for argument #{arg}"
|
116
|
-
else
|
117
|
-
value = convert_arg(cmd["name"], arg, value)
|
118
|
-
if body[arg].nil?
|
119
|
-
body[arg] = value
|
120
|
-
else
|
121
|
-
# Either/both `body[arg]` or/and `value` might be an array at this point.
|
122
|
-
body[arg] = Array(body[arg]) + Array(value)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
else
|
126
|
-
raise ArgumentError, "Unexpected argument #{argument}"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
begin
|
131
|
-
body = MultiJson::load(File::read(body["json"])) if body["json"]
|
132
|
-
rescue MultiJson::LoadError
|
133
|
-
raise Razor::CLI::Error, "File #{body["json"]} is not valid JSON"
|
134
|
-
rescue Errno::ENOENT
|
135
|
-
raise Razor::CLI::Error, "File #{body["json"]} not found"
|
136
|
-
rescue Errno::EACCES
|
137
|
-
raise Razor::CLI::Error,
|
138
|
-
"Permission to read file #{body["json"]} denied"
|
139
|
-
end
|
140
|
-
[cmd, body]
|
141
|
-
end
|
142
|
-
|
143
|
-
def move_to(key)
|
66
|
+
def move_to(key, doc = @doc, params = {})
|
67
|
+
@doc = doc
|
144
68
|
if @doc.is_a? Array
|
145
69
|
obj = @doc.find {|x| x.is_a?(Hash) and x["name"] == key }
|
146
70
|
elsif @doc.is_a?(Hash) && @doc['items'].is_a?(Array)
|
@@ -149,17 +73,12 @@ module Razor::CLI
|
|
149
73
|
obj = @doc[key]
|
150
74
|
end
|
151
75
|
|
152
|
-
raise NavigationError.new(@
|
76
|
+
raise NavigationError.new(@doc_resource, key, @doc) unless obj
|
153
77
|
|
154
78
|
if obj.is_a?(Hash) && obj["id"]
|
155
|
-
url = obj["id"]
|
156
|
-
if @doc_url
|
157
|
-
url = URI.parse(url.to_s)
|
158
|
-
url.userinfo = @doc_url.userinfo
|
159
|
-
end
|
79
|
+
url = URI.parse(obj["id"])
|
160
80
|
|
161
|
-
@doc = json_get(url)
|
162
|
-
@doc_url = url
|
81
|
+
@doc = json_get(url, {}, params)
|
163
82
|
elsif obj.is_a?(Hash) && obj['spec']
|
164
83
|
@doc = obj
|
165
84
|
elsif obj.is_a?(Hash)
|
@@ -186,12 +105,17 @@ module Razor::CLI
|
|
186
105
|
end
|
187
106
|
|
188
107
|
def get(url, headers={})
|
189
|
-
|
108
|
+
resource = create_resource(url, headers)
|
109
|
+
response = resource.get
|
190
110
|
print "GET #{url.to_s}\n#{response.body}\n\n" if @parse.dump_response?
|
191
111
|
response
|
192
112
|
end
|
193
113
|
|
194
|
-
def json_get(url, headers = {})
|
114
|
+
def json_get(url, headers = {}, params = {})
|
115
|
+
# Add extra parameters to URL.
|
116
|
+
url.query = URI.encode_www_form(params)
|
117
|
+
url.query = nil if url.query.empty? # Remove dangling '?' from URL.
|
118
|
+
|
195
119
|
response = get(url,headers.merge(:accept => :json))
|
196
120
|
unless response.headers[:content_type] =~ /application\/json/
|
197
121
|
raise "Received content type #{response.headers[:content_type]}"
|
@@ -202,7 +126,8 @@ module Razor::CLI
|
|
202
126
|
def json_post(url, body)
|
203
127
|
headers = { :accept=>:json, "Content-Type" => :json }
|
204
128
|
begin
|
205
|
-
|
129
|
+
resource = create_resource(url, headers)
|
130
|
+
response = resource.post MultiJson::dump(body)
|
206
131
|
ensure
|
207
132
|
if @parse.dump_response?
|
208
133
|
print "POST #{url.to_s}\n#{body}\n-->\n"
|
@@ -213,59 +138,12 @@ module Razor::CLI
|
|
213
138
|
end
|
214
139
|
|
215
140
|
private
|
216
|
-
def cmd_schema(cmd_name)
|
217
|
-
begin
|
218
|
-
json_get(@cmd_url)['schema']
|
219
|
-
rescue RestClient::ResourceNotFound => _
|
220
|
-
raise VersionCompatibilityError, 'Server must supply the expected datatypes for command arguments; use `--json` or upgrade razor-server'
|
221
|
-
end
|
222
|
-
end
|
223
141
|
|
224
|
-
def
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
end
|
230
|
-
|
231
|
-
def convert_arg(cmd_name, arg_name, value)
|
232
|
-
value = nil if value == "null"
|
233
|
-
|
234
|
-
argument_type = arg_type(cmd_name, arg_name)
|
235
|
-
|
236
|
-
# This might be helpful, since there's no other method for debug-level logging on the client.
|
237
|
-
puts "Formatting argument #{arg_name} with value #{value} as #{argument_type}\n" if @parse.dump_response?
|
238
|
-
|
239
|
-
case argument_type
|
240
|
-
when "array"
|
241
|
-
# 'array' datatype arguments will never fail. At worst, they'll be wrapped in an array.
|
242
|
-
begin
|
243
|
-
MultiJson::load(value)
|
244
|
-
rescue MultiJson::LoadError => _
|
245
|
-
Array(value)
|
246
|
-
end
|
247
|
-
when "object"
|
248
|
-
begin
|
249
|
-
MultiJson::load(value)
|
250
|
-
rescue MultiJson::LoadError => error
|
251
|
-
raise ArgumentError, "Invalid JSON for argument '#{arg_name}': #{error.message}"
|
252
|
-
end
|
253
|
-
when "boolean"
|
254
|
-
["true", nil].include?(value)
|
255
|
-
when "number"
|
256
|
-
begin
|
257
|
-
Integer(value)
|
258
|
-
rescue ArgumentError
|
259
|
-
raise ArgumentError, "Invalid integer for argument '#{arg_name}': #{value}"
|
260
|
-
end
|
261
|
-
when "null"
|
262
|
-
raise ArgumentError, "Expected nothing for argument '#{arg_name}', but was: '#{value}'" unless value.nil?
|
263
|
-
nil
|
264
|
-
when "string", nil # `nil` for 'might be an alias, send as-is'
|
265
|
-
value
|
266
|
-
else
|
267
|
-
raise Razor::CLI::Error, "Unexpected datatype '#{argument_type}' for argument #{arg_name}"
|
268
|
-
end
|
142
|
+
def create_resource(url, headers)
|
143
|
+
@doc_resource = RestClient::Resource.new(url.to_s, :headers => headers,
|
144
|
+
:verify_ssl => @parse.verify_ssl?,
|
145
|
+
:user => @username,
|
146
|
+
:password => @password)
|
269
147
|
end
|
270
148
|
end
|
271
149
|
end
|