razor-client 0.15.1 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|