pe-razor-client 0.14.0 → 0.15.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +25 -0
- data/bin/razor +9 -4
- data/lib/razor/cli.rb +10 -0
- data/lib/razor/cli/document.rb +59 -0
- data/lib/razor/cli/format.rb +81 -20
- data/lib/razor/cli/navigate.rb +121 -27
- data/lib/razor/cli/parse.rb +83 -10
- data/lib/razor/cli/transforms.rb +42 -0
- data/lib/razor/cli/version.rb +46 -0
- data/lib/razor/cli/views.rb +25 -0
- data/lib/razor/cli/views.yaml +196 -0
- data/spec/cli/format_spec.rb +99 -0
- data/spec/cli/navigate_spec.rb +111 -2
- data/spec/cli/parse_spec.rb +20 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/argument_formatting/should_allow_spaces.yml +966 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_--help_command_.yml +99 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_-h_command_.yml +99 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_command_--help_.yml +99 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_command_-h_.yml +99 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_command_help_.yml +99 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/for_command_help/should_provide_command_help_for_razor_help_command_.yml +99 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_authentication/should_preserve_that_across_navigation.yml +9 -42
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_authentication/should_supply_that_to_the_API_service.yml +5 -5
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_invalid_parameter/should_fail_with_bad_JSON.yml +228 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_invalid_parameter/should_fail_with_malformed_argument.yml +120 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_an_array_into_an_existing_array.yml +2006 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_the_arguments_as_an_array.yml +2006 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_array/should_merge_the_arguments_into_an_existing_array.yml +2006 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_multiple_arguments_with_same_name/combining_as_an_object/should_construct_a_json_object.yml +234 -0
- 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 +412 -0
- 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 +228 -0
- 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 +164 -0
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_no_parameters/should_fail_with_bad_JSON.yml +36 -0
- data/spec/fixtures/vcr/Razor_CLI_Parse/_new/_help/should_print_a_list_of_known_endpoints.yml +5 -5
- data/spec/spec_helper.rb +12 -1
- data/spec/testing.md +16 -0
- data/spec/version_spec.rb +8 -0
- metadata +67 -60
- data/.gitignore +0 -7
- data/.yardopts +0 -2
- data/Gemfile +0 -35
- data/Gemfile.lock +0 -53
- data/Rakefile +0 -37
- data/lib/razor/cli/navigate.yaml +0 -28
- data/pe-razor-client.gemspec +0 -32
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_a_single_item_path/.yml +0 -69
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_an_invalid_path/.yml +0 -36
- data/spec/fixtures/vcr/Razor_CLI_Navigate/with_no_path/.yml +0 -36
- data/spec/fixtures/vcr/Razor_CLI_Parse/_new/_help/.yml +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec9ee14e06b62d0312820a7ca7e00b614f00c1ee
|
4
|
+
data.tar.gz: a0a3bb05a69607f877708e427ef8e25d13a743f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0780b03661a1692be351db757611ab91b2592d6d4f54d400634558944dea6745e28a8b0c9a28c330545a4a28af20b58c6ec62d87c2f90c35e639a5108249d67a
|
7
|
+
data.tar.gz: c339b1b83dbabe6e3752b302fd8e916f5260b619b1e9861a08271ee63eda825f453d97477187d49349ff83486595f11850c42c6673dde83ef082f58af9bd9140
|
data/NEWS.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Razor Client Release Notes
|
2
|
+
|
3
|
+
## 0.15.1 - 2014-06-12
|
4
|
+
|
5
|
+
Server version compatibility
|
6
|
+
|
7
|
+
* It is highly recommended that razor-client version 0.15.x be used with
|
8
|
+
razor-server version 0.15.x or higher.
|
9
|
+
|
10
|
+
## 0.15.0 - 2014-05-22
|
11
|
+
|
12
|
+
Usability of the client has been greatly enhanced:
|
13
|
+
|
14
|
+
* Tabular views of most collections: things like 'razor nodes' now display
|
15
|
+
a table of results with important details about each node.
|
16
|
+
* Get help on commands via `razor help COMMAND`
|
17
|
+
* Output now includes hints on how to get more details on the things displayed
|
18
|
+
* No need to enter JSON on the command line for most commands (all but
|
19
|
+
create-tag)
|
20
|
+
+ arrays can now be entered by repeating the same option, e.g. `razor
|
21
|
+
create-tag --name ... --tag t1 --tag t2`
|
22
|
+
+ broker configuration is set using `razor create-broker --name
|
23
|
+
.. --configuration var1=value1 --configuration var2=value2 ...`
|
24
|
+
* Clearer error message when server responds with 'Unauthorized'
|
25
|
+
(RAZOR-175)
|
data/bin/razor
CHANGED
@@ -27,7 +27,12 @@ rescue OptionParser::InvalidOption => e
|
|
27
27
|
die e.message + "\nTry 'razor --help' for more information"
|
28
28
|
end
|
29
29
|
|
30
|
-
if parse.
|
30
|
+
if parse.show_version?
|
31
|
+
puts parse.version
|
32
|
+
exit 0
|
33
|
+
end
|
34
|
+
|
35
|
+
if parse.show_help? and not parse.show_command_help?
|
31
36
|
puts parse.help
|
32
37
|
exit 0
|
33
38
|
end
|
@@ -35,9 +40,7 @@ end
|
|
35
40
|
begin
|
36
41
|
document = parse.navigate.get_document
|
37
42
|
url = parse.navigate.last_url
|
38
|
-
puts "From #{url}:\n\n#{format_document document}\n\n"
|
39
|
-
rescue Razor::CLI::Error => e
|
40
|
-
die "#{e}\n#{parse.help}\n\n"
|
43
|
+
puts "From #{url}:\n\n#{format_document document, parse}\n\n"
|
41
44
|
rescue SocketError, Errno::ECONNREFUSED => e
|
42
45
|
puts "Error: Could not connect to the server at #{parse.api_url}"
|
43
46
|
puts " #{e}\n"
|
@@ -58,4 +61,6 @@ rescue RestClient::Exception => e
|
|
58
61
|
puts r.body
|
59
62
|
end
|
60
63
|
die
|
64
|
+
rescue StandardError => e
|
65
|
+
die "#{e.message}\nTry 'razor --help' for more information\n\n"
|
61
66
|
end
|
data/lib/razor/cli.rb
CHANGED
@@ -26,9 +26,19 @@ module Razor
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
class VersionCompatibilityError < Error
|
30
|
+
def initialize(reason)
|
31
|
+
super "Server version is not compatible with client version: #{reason}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
29
35
|
end
|
30
36
|
end
|
31
37
|
|
38
|
+
require_relative 'cli/version'
|
32
39
|
require_relative 'cli/navigate'
|
33
40
|
require_relative 'cli/parse'
|
34
41
|
require_relative 'cli/format'
|
42
|
+
require_relative 'cli/document'
|
43
|
+
require_relative 'cli/views'
|
44
|
+
require_relative 'cli/transforms'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Razor::CLI
|
4
|
+
class Document
|
5
|
+
extend Forwardable
|
6
|
+
attr_reader 'spec', 'items', 'format_view', 'original_items'
|
7
|
+
def initialize(doc, format_type)
|
8
|
+
if doc['spec'].is_a?(Array)
|
9
|
+
@spec, @remaining_navigation = doc['spec']
|
10
|
+
else
|
11
|
+
@spec = doc['spec']
|
12
|
+
end
|
13
|
+
@command = doc['command']
|
14
|
+
if doc.has_key?('items')
|
15
|
+
@type = :list
|
16
|
+
else
|
17
|
+
@type = :single
|
18
|
+
end
|
19
|
+
@items = doc['items'] || Array[doc]
|
20
|
+
@format_view = Razor::CLI::Views.find_formatting(@spec, format_type, @remaining_navigation)
|
21
|
+
|
22
|
+
# Untransformed and unordered for displaying nested views.
|
23
|
+
@original_items = @items
|
24
|
+
@items = hide_or_transform_elements!(items, format_view)
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_list?
|
28
|
+
@type == :list
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
# This method:
|
33
|
+
# - rearranges columns per Razor::CLI::Views.
|
34
|
+
# - hides columns per Razor::CLI::Views.
|
35
|
+
# - transforms data using both Razor::CLI::Views and its `TRANSFORMS`.
|
36
|
+
def hide_or_transform_elements!(items, format_view)
|
37
|
+
if format_view.has_key?('+show')
|
38
|
+
items.map do |item|
|
39
|
+
Hash[
|
40
|
+
format_view['+show'].map do |item_format_spec|
|
41
|
+
# Allow both '+column' as overrides.
|
42
|
+
item_spec = (item_format_spec[1] or {})
|
43
|
+
item_label = item_format_spec[0]
|
44
|
+
item_column = (item_spec['+column'] or item_label)
|
45
|
+
value = Razor::CLI::Views.transform(item[item_column], item_spec['+format'])
|
46
|
+
|
47
|
+
[item_label, value]
|
48
|
+
end
|
49
|
+
].tap do |hash|
|
50
|
+
# Re-add the special 'command' key and value if the key isn't already there.
|
51
|
+
hash['command'] = @command if @command and not hash.has_key?('command')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
items
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/razor/cli/format.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'terminal-table'
|
2
3
|
|
3
4
|
module Razor::CLI
|
4
5
|
module Format
|
5
|
-
|
6
|
+
extend Forwardable
|
7
|
+
PriorityKeys = %w[ id name spec ]
|
6
8
|
SpecNames = {
|
7
9
|
"/spec/object/policy" => "Policy",
|
8
10
|
"/spec/object/tag" => "Tag",
|
@@ -16,12 +18,50 @@ module Razor::CLI
|
|
16
18
|
spec
|
17
19
|
end
|
18
20
|
|
19
|
-
def format_document(doc)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
def format_document(doc, parse = nil)
|
22
|
+
format = parse && parse.format
|
23
|
+
arguments = parse && parse.args
|
24
|
+
doc = Razor::CLI::Document.new(doc, format)
|
25
|
+
|
26
|
+
return "There are no items for this query." if doc.items.empty?
|
27
|
+
return format_objects(doc.items).chomp if parse && parse.show_command_help?
|
28
|
+
|
29
|
+
case (doc.format_view['+layout'] or 'list')
|
30
|
+
when 'list'
|
31
|
+
format_objects(doc.items) + String(additional_details(doc, parse, arguments)).chomp
|
32
|
+
when 'table'
|
33
|
+
case doc.items
|
34
|
+
when Array then
|
35
|
+
get_table(doc.items, doc.format_view) + String(additional_details(doc, parse, arguments))
|
36
|
+
else doc.to_s
|
37
|
+
end
|
38
|
+
else
|
39
|
+
raise ArgumentError, "Unrecognized view format #{doc.format_view['+layout']}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def get_table(doc, formatting)
|
45
|
+
# Use the formatting if it exists, otherwise build from the data.
|
46
|
+
headings = (formatting['+show'] and formatting['+show'].keys or get_headers(doc))
|
47
|
+
Terminal::Table.new do |table|
|
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
|
25
65
|
end
|
26
66
|
|
27
67
|
# We assume that all collections are homogenous
|
@@ -31,21 +71,16 @@ module Razor::CLI
|
|
31
71
|
end.join "\n\n"
|
32
72
|
end
|
33
73
|
|
34
|
-
def format_reference_object(ref, indent = 0)
|
35
|
-
output = ' '* indent + "#{ref['name']} => #{ref['id'].to_s.ljust 4}"
|
36
|
-
end
|
37
|
-
|
38
|
-
|
39
74
|
def format_object(object, indent = 0)
|
40
|
-
if object.
|
41
|
-
|
75
|
+
if object.has_key?('help') and object.has_key?('name')
|
76
|
+
object['help']['full']
|
42
77
|
else
|
43
78
|
format_default_object(object, indent)
|
44
79
|
end
|
45
80
|
end
|
46
81
|
|
47
82
|
def format_default_object(object, indent = 0 )
|
48
|
-
fields = (
|
83
|
+
fields = display_fields(object)
|
49
84
|
key_indent = indent + fields.map {|f| f.length}.max
|
50
85
|
output = ""
|
51
86
|
fields.map do |f|
|
@@ -59,11 +94,13 @@ module Razor::CLI
|
|
59
94
|
"\n" + format_object(value, key_indent + 4).rstrip
|
60
95
|
end
|
61
96
|
when Array
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
97
|
+
if value.all? { |v| v.is_a?(String) }
|
98
|
+
"[" + value.map(&:to_s).join(",") + "]"
|
99
|
+
else
|
100
|
+
"[\n" + format_objects(value, key_indent + 6) + ("\n"+' '*(key_indent+4)+"]")
|
101
|
+
end
|
102
|
+
when String
|
103
|
+
value
|
67
104
|
else
|
68
105
|
case f
|
69
106
|
when "spec" then "\"#{Format.spec_name(value)}\""
|
@@ -72,5 +109,29 @@ module Razor::CLI
|
|
72
109
|
end
|
73
110
|
end.join "\n"
|
74
111
|
end
|
112
|
+
|
113
|
+
def display_fields(object)
|
114
|
+
keys = object.respond_to?(:keys) ? object.keys : []
|
115
|
+
(PriorityKeys & keys) + (keys - PriorityKeys) - ['+spec']
|
116
|
+
end
|
117
|
+
|
118
|
+
def additional_details(doc, parse, arguments)
|
119
|
+
objects = doc.original_items
|
120
|
+
if objects.empty? or (parse and not parse.query?)
|
121
|
+
""
|
122
|
+
elsif doc.is_list? and objects.all? { |it| it.is_a?(Hash) && it.has_key?('name')}
|
123
|
+
# If every element has the 'name' key, it has nested elements.
|
124
|
+
"\n\nQuery an entry by including its name, e.g. `razor #{arguments.join(' ')} #{objects.first['name']}`"
|
125
|
+
elsif objects.any?
|
126
|
+
object = objects.first
|
127
|
+
fields = display_fields(object) - PriorityKeys
|
128
|
+
list = fields.select do |f|
|
129
|
+
object[f].is_a?(Hash) or object[f].is_a?(Array)
|
130
|
+
end.sort
|
131
|
+
if list.any?
|
132
|
+
"\n\nQuery additional details via: `razor #{arguments.join(' ')} [#{list.join(', ')}]`"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
75
136
|
end
|
76
137
|
end
|
data/lib/razor/cli/navigate.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'rest-client'
|
2
2
|
require 'multi_json'
|
3
3
|
require 'yaml'
|
4
|
+
require 'forwardable'
|
4
5
|
|
5
6
|
module Razor::CLI
|
6
7
|
class Navigate
|
8
|
+
extend Forwardable
|
7
9
|
|
8
10
|
def initialize(parse, segments)
|
9
11
|
@parse = parse
|
@@ -28,18 +30,24 @@ module Razor::CLI
|
|
28
30
|
entrypoint["commands"]
|
29
31
|
end
|
30
32
|
|
33
|
+
def server_version
|
34
|
+
entrypoint.has_key?('version') and entrypoint['version']['server'] or 'Unknown'
|
35
|
+
end
|
36
|
+
|
31
37
|
def query?
|
32
|
-
collections.any? { |coll| coll["name"] == @segments.first }
|
38
|
+
@query ||= collections.any? { |coll| coll["name"] == @segments.first }
|
33
39
|
end
|
34
40
|
|
35
41
|
def command(name)
|
36
|
-
commands.find { |coll| coll["name"] == name }
|
42
|
+
@command ||= commands.find { |coll| coll["name"] == name }
|
37
43
|
end
|
38
44
|
|
39
45
|
def command?
|
40
46
|
!! command(@segments.first)
|
41
47
|
end
|
42
48
|
|
49
|
+
def_delegator '@parse', 'show_command_help?'
|
50
|
+
|
43
51
|
def get_document
|
44
52
|
if @segments.empty?
|
45
53
|
entrypoint
|
@@ -48,15 +56,18 @@ module Razor::CLI
|
|
48
56
|
while @segments.any?
|
49
57
|
move_to @segments.shift
|
50
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
|
51
66
|
@doc
|
52
67
|
elsif command?
|
53
68
|
# @todo lutter 2013-08-16: None of this has any tests, and error
|
54
69
|
# handling is heinous at best
|
55
70
|
cmd, body = extract_command
|
56
|
-
if body.empty?
|
57
|
-
raise Razor::CLI::Error,
|
58
|
-
"No arguments for command (did you forget --json ?)"
|
59
|
-
end
|
60
71
|
# Ensure that we copy authentication data from our previous URL.
|
61
72
|
url = cmd["id"]
|
62
73
|
if @doc_url
|
@@ -64,7 +75,18 @@ module Razor::CLI
|
|
64
75
|
url.userinfo = @doc_url.userinfo
|
65
76
|
end
|
66
77
|
|
67
|
-
|
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
|
68
90
|
else
|
69
91
|
raise NavigationError.new(@doc_url, @segments, @doc)
|
70
92
|
end
|
@@ -72,12 +94,36 @@ module Razor::CLI
|
|
72
94
|
|
73
95
|
def extract_command
|
74
96
|
cmd = command(@segments.shift)
|
97
|
+
@cmd_url = cmd['id']
|
75
98
|
body = {}
|
76
99
|
until @segments.empty?
|
77
|
-
|
100
|
+
argument = @segments.shift
|
101
|
+
if argument =~ /\A--([a-z-]+)(=(.+))?\Z/
|
102
|
+
# `--arg=value` or `--arg value`
|
78
103
|
arg, value = [$1, $3]
|
79
104
|
value = @segments.shift if value.nil? && @segments[0] !~ /^--/
|
80
|
-
|
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}"
|
81
127
|
end
|
82
128
|
end
|
83
129
|
|
@@ -89,15 +135,16 @@ module Razor::CLI
|
|
89
135
|
raise Razor::CLI::Error, "File #{body["json"]} not found"
|
90
136
|
rescue Errno::EACCES
|
91
137
|
raise Razor::CLI::Error,
|
92
|
-
|
138
|
+
"Permission to read file #{body["json"]} denied"
|
93
139
|
end
|
94
140
|
[cmd, body]
|
95
141
|
end
|
96
142
|
|
97
143
|
def move_to(key)
|
98
|
-
key = key.to_i if key.to_i.to_s == key
|
99
144
|
if @doc.is_a? Array
|
100
145
|
obj = @doc.find {|x| x.is_a?(Hash) and x["name"] == key }
|
146
|
+
elsif @doc.is_a?(Hash) && @doc['items'].is_a?(Array)
|
147
|
+
obj = @doc['items'].find {|x| x.is_a?(Hash) and x["name"] == key }
|
101
148
|
elsif @doc.is_a? Hash
|
102
149
|
obj = @doc[key]
|
103
150
|
end
|
@@ -112,13 +159,27 @@ module Razor::CLI
|
|
112
159
|
end
|
113
160
|
|
114
161
|
@doc = json_get(url)
|
115
|
-
# strip the wrapper around collections
|
116
|
-
if @doc.is_a? Hash and @doc["items"].is_a? Array
|
117
|
-
@doc = @doc["items"]
|
118
|
-
end
|
119
162
|
@doc_url = url
|
120
|
-
elsif obj.is_a?
|
163
|
+
elsif obj.is_a?(Hash) && obj['spec']
|
121
164
|
@doc = obj
|
165
|
+
elsif obj.is_a?(Hash)
|
166
|
+
# No spec string; use parent's and remember extra navigation.
|
167
|
+
if @doc['+spec'].is_a?(Array)
|
168
|
+
# Something's been added.
|
169
|
+
@doc['+spec'] << key
|
170
|
+
elsif @doc['+spec'].nil? || @doc['+spec'].is_a?(String)
|
171
|
+
@doc['+spec'] = [@doc['spec'], key]
|
172
|
+
end
|
173
|
+
@doc = obj.merge({'+spec' => @doc['+spec']})
|
174
|
+
elsif obj.is_a?(Array)
|
175
|
+
# No spec string; use parent's and remember extra navigation.
|
176
|
+
if @doc['+spec'].is_a?(Array)
|
177
|
+
# Something's already been added.
|
178
|
+
@doc['+spec'] << key
|
179
|
+
elsif @doc['+spec'].nil? || @doc['+spec'].is_a?(String)
|
180
|
+
@doc['+spec'] = [@doc['spec'], key]
|
181
|
+
end
|
182
|
+
@doc = {'+spec' => @doc['+spec'], 'items' => obj}
|
122
183
|
else
|
123
184
|
@doc = nil
|
124
185
|
end
|
@@ -133,7 +194,7 @@ module Razor::CLI
|
|
133
194
|
def json_get(url, headers = {})
|
134
195
|
response = get(url,headers.merge(:accept => :json))
|
135
196
|
unless response.headers[:content_type] =~ /application\/json/
|
136
|
-
|
197
|
+
raise "Received content type #{response.headers[:content_type]}"
|
137
198
|
end
|
138
199
|
MultiJson.load(response.body)
|
139
200
|
end
|
@@ -152,25 +213,58 @@ module Razor::CLI
|
|
152
213
|
end
|
153
214
|
|
154
215
|
private
|
155
|
-
def
|
156
|
-
|
157
|
-
|
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
|
158
222
|
end
|
159
223
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
224
|
+
def arg_type(cmd_name, arg_name)
|
225
|
+
# Short-circuit to allow this as a work-around for backwards compatibility.
|
226
|
+
return nil if arg_name == 'json'
|
227
|
+
cmd = cmd_schema(cmd_name)
|
228
|
+
cmd && cmd[arg_name] && cmd[arg_name]['type'] or nil
|
163
229
|
end
|
164
230
|
|
165
231
|
def convert_arg(cmd_name, arg_name, value)
|
166
232
|
value = nil if value == "null"
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
170
253
|
when "boolean"
|
171
254
|
["true", nil].include?(value)
|
172
|
-
|
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'
|
173
265
|
value
|
266
|
+
else
|
267
|
+
raise Razor::CLI::Error, "Unexpected datatype '#{argument_type}' for argument #{arg_name}"
|
174
268
|
end
|
175
269
|
end
|
176
270
|
end
|