pe-razor-client 0.14.0 → 0.15.2
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/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
|