restility 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/License.txt +340 -0
- data/bin/rest_doc +120 -0
- data/bin/rest_test +151 -0
- data/config/hoe.rb +68 -0
- data/config/requirements.rb +16 -0
- data/lib/doc_book_printer.rb +158 -0
- data/lib/rest.rb +459 -0
- data/lib/rest_htmlprinter.rb +191 -0
- data/lib/rest_test.rb +351 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/tasks/deployment.rake +27 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +9 -0
- data/test/test_helper.rb +2 -0
- data/test/test_restility.rb +11 -0
- metadata +94 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require "rest"
|
4
|
+
require 'active_support/builder' unless defined?(Builder)
|
5
|
+
|
6
|
+
class HtmlPrinter < Printer
|
7
|
+
|
8
|
+
attr_accessor :output_dir
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super()
|
12
|
+
@output_dir = "html"
|
13
|
+
@xml_examples = Hash.new
|
14
|
+
@xml_schemas = Hash.new
|
15
|
+
|
16
|
+
@docbook_tag_mapping = {
|
17
|
+
"command" => "tt",
|
18
|
+
"filename" => "tt",
|
19
|
+
"emphasis" => "em",
|
20
|
+
"replaceable" => "em",
|
21
|
+
}
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_prepare
|
26
|
+
unless File.exists? @output_dir
|
27
|
+
Dir.mkdir @output_dir
|
28
|
+
end
|
29
|
+
@index = File.new( @output_dir + "/index.html", "w" )
|
30
|
+
@html = Builder::XmlMarkup.new( :target => @index, :indent => 2 )
|
31
|
+
@html.comment! "This file was generated by restility at #{Time.now}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def do_finish
|
35
|
+
puts "Written #{@index.path}"
|
36
|
+
|
37
|
+
@xml_examples.each do |f,b|
|
38
|
+
if !XmlFile.exist?( f )
|
39
|
+
STDERR.puts "XML Example '#{f}' is missing."
|
40
|
+
else
|
41
|
+
XmlFile.copy f, @output_dir
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@xml_schemas.each do |f,b|
|
45
|
+
if !XmlFile.exist?( f )
|
46
|
+
STDERR.puts "XML Schema '#{f}' is missing."
|
47
|
+
else
|
48
|
+
XmlFile.copy f, @output_dir
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@index.close
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_section section
|
56
|
+
if ( !section.root? )
|
57
|
+
tag = "h#{section.level}"
|
58
|
+
@html.tag!( tag, section )
|
59
|
+
end
|
60
|
+
section.print_children self
|
61
|
+
end
|
62
|
+
|
63
|
+
def print_request request
|
64
|
+
@html.div( "class" => "request" ) do
|
65
|
+
|
66
|
+
@html.p do
|
67
|
+
@html.a( "name" => request.id ) do
|
68
|
+
@html.b request.to_s
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if false
|
73
|
+
host = request.host
|
74
|
+
if ( host )
|
75
|
+
@html.p "Host: " + host.name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
if request.parameters.size > 0
|
80
|
+
@html.p "Arguments:"
|
81
|
+
@html.ul do
|
82
|
+
request.parameters.each do |p|
|
83
|
+
@html.li p.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
request.print_children self
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def replace_docbook_tags text
|
93
|
+
@docbook_tag_mapping.each do |docbook, html|
|
94
|
+
text.gsub! "<#{docbook}>", "<#{html}>"
|
95
|
+
text.gsub! "</#{docbook}>", "</#{html}>"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def print_text text
|
100
|
+
@html.p do |p|
|
101
|
+
text.text.each do |t|
|
102
|
+
replace_docbook_tags t
|
103
|
+
p << t << "\n"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def print_parameter parameter
|
109
|
+
end
|
110
|
+
|
111
|
+
def print_host host
|
112
|
+
@html.p "Host: " + host.name
|
113
|
+
end
|
114
|
+
|
115
|
+
def print_result result
|
116
|
+
@html.p "Result: " + result.name
|
117
|
+
end
|
118
|
+
|
119
|
+
def print_body body
|
120
|
+
@html.p "Body: " + body.name
|
121
|
+
end
|
122
|
+
|
123
|
+
def print_xmlresult result
|
124
|
+
print_xml_links "Result", result.name, result.schema
|
125
|
+
end
|
126
|
+
|
127
|
+
def print_xmlbody body
|
128
|
+
print_xml_links "Body", body.name, body.schema
|
129
|
+
end
|
130
|
+
|
131
|
+
def print_xml_links title, xmlname, schema
|
132
|
+
example = xmlname + ".xml"
|
133
|
+
if ( !schema || schema.empty? )
|
134
|
+
schema = xmlname + ".xsd"
|
135
|
+
end
|
136
|
+
@xml_examples[ example ] = true
|
137
|
+
@xml_schemas[ schema ] = true
|
138
|
+
@html.p do |p|
|
139
|
+
p << title
|
140
|
+
p << ": "
|
141
|
+
has_example = XmlFile.exist? example
|
142
|
+
has_schema = XmlFile.exist? schema
|
143
|
+
if has_example
|
144
|
+
@html.a( "Example", "href" => example )
|
145
|
+
end
|
146
|
+
if has_schema
|
147
|
+
p << " ";
|
148
|
+
@html.a( "Schema", "href" => schema )
|
149
|
+
end
|
150
|
+
if( !has_example && !has_schema )
|
151
|
+
p << xmlname
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def print_contents contents
|
157
|
+
@html.tag! "h#{contents.level}", "Table of Contents"
|
158
|
+
@html.p do |p|
|
159
|
+
p << create_contents_list( contents.root, 1 )
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def create_contents_list section, min_level
|
164
|
+
result = ""
|
165
|
+
section.children.each do |s|
|
166
|
+
if ( s.is_a? Section )
|
167
|
+
result += create_contents_list s, min_level
|
168
|
+
end
|
169
|
+
if ( s.is_a? Request )
|
170
|
+
result += "<li><a href=\"##{s.id}\">" + h( s.to_s ) + "</a></li>\n"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
endresult = ""
|
174
|
+
if ( !result.empty? )
|
175
|
+
if ( section.level > min_level )
|
176
|
+
endresult = "<li>" + h( section.to_s ) + "</li>\n"
|
177
|
+
end
|
178
|
+
if ( section.level >= min_level )
|
179
|
+
endresult += "<ul>\n" + result + "</ul>\n"
|
180
|
+
else
|
181
|
+
endresult = result
|
182
|
+
end
|
183
|
+
end
|
184
|
+
endresult
|
185
|
+
end
|
186
|
+
|
187
|
+
def print_version version
|
188
|
+
@html.p "Version: " + version.to_s
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
data/lib/rest_test.rb
ADDED
@@ -0,0 +1,351 @@
|
|
1
|
+
require "net/https"
|
2
|
+
require "tempfile"
|
3
|
+
|
4
|
+
class ParameterError < Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
class TestContext
|
8
|
+
|
9
|
+
attr_writer :show_xmlbody, :request_filter, :show_passed, :output_html
|
10
|
+
|
11
|
+
def initialize requests
|
12
|
+
@host_aliases = Hash.new
|
13
|
+
|
14
|
+
@output = ""
|
15
|
+
|
16
|
+
@requests = requests
|
17
|
+
start
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
@tested = 0
|
22
|
+
@unsupported = 0
|
23
|
+
@failed = 0
|
24
|
+
@passed = 0
|
25
|
+
@error = 0
|
26
|
+
@skipped = 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def bold str
|
30
|
+
if @output_html
|
31
|
+
str.gsub! /</, "<"
|
32
|
+
str.gsub! />/, ">"
|
33
|
+
"<b>#{str}</b>"
|
34
|
+
else
|
35
|
+
"\033[1m#{str}\033[0m"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def red str
|
40
|
+
bold str
|
41
|
+
# "\E[31m#{str}\E[30m"
|
42
|
+
end
|
43
|
+
|
44
|
+
def green str
|
45
|
+
bold str
|
46
|
+
end
|
47
|
+
|
48
|
+
def magenta str
|
49
|
+
bold str
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_binding
|
53
|
+
return binding()
|
54
|
+
end
|
55
|
+
|
56
|
+
def unsupported
|
57
|
+
out magenta( " UNSUPPORTED" )
|
58
|
+
@unsupported += 1
|
59
|
+
out_flush
|
60
|
+
end
|
61
|
+
|
62
|
+
def failed
|
63
|
+
out red( " FAILED" )
|
64
|
+
@failed += 1
|
65
|
+
out_flush
|
66
|
+
end
|
67
|
+
|
68
|
+
def passed
|
69
|
+
out green( " PASSED" )
|
70
|
+
@passed += 1
|
71
|
+
if ( @show_passed )
|
72
|
+
out_flush
|
73
|
+
else
|
74
|
+
out_clear
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def skipped
|
79
|
+
# out magenta( " SKIPPED" )
|
80
|
+
@skipped += 1
|
81
|
+
out_flush
|
82
|
+
end
|
83
|
+
|
84
|
+
def error str = nil
|
85
|
+
error_str = " ERROR"
|
86
|
+
if ( str )
|
87
|
+
error_str += ": " + str
|
88
|
+
end
|
89
|
+
out red( error_str )
|
90
|
+
@error += 1
|
91
|
+
out_flush
|
92
|
+
end
|
93
|
+
|
94
|
+
def alias_host old, new
|
95
|
+
@host_aliases[ old ] = new
|
96
|
+
end
|
97
|
+
|
98
|
+
def out str
|
99
|
+
@output += str + "\n";
|
100
|
+
end
|
101
|
+
|
102
|
+
def out_clear
|
103
|
+
@output = ""
|
104
|
+
end
|
105
|
+
|
106
|
+
def out_flush
|
107
|
+
print @output
|
108
|
+
out_clear
|
109
|
+
end
|
110
|
+
|
111
|
+
def request arg, return_code = nil, xml_check_wanted = true
|
112
|
+
@tested += 1
|
113
|
+
|
114
|
+
if ( @request_filter && arg !~ /#{@request_filter}/ )
|
115
|
+
skipped
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
out bold( "REQUEST: " + arg )
|
120
|
+
|
121
|
+
request = @requests.find { |r|
|
122
|
+
r.to_s == arg
|
123
|
+
}
|
124
|
+
|
125
|
+
if ( !request )
|
126
|
+
STDERR.puts " Request not defined"
|
127
|
+
return nil
|
128
|
+
end
|
129
|
+
|
130
|
+
xml_bodies = request.all_children XmlBody
|
131
|
+
if ( !xml_bodies.empty? )
|
132
|
+
xml_body = xml_bodies[0]
|
133
|
+
out " XMLBODY: " + xml_body.name
|
134
|
+
end
|
135
|
+
|
136
|
+
xml_results = request.all_children XmlResult
|
137
|
+
if ( !xml_results.empty? )
|
138
|
+
xml_result = xml_results[0]
|
139
|
+
out " XMLRESULT: " + xml_result.name
|
140
|
+
end
|
141
|
+
|
142
|
+
out " host: '#{request.host}'"
|
143
|
+
|
144
|
+
host = request.host.to_s
|
145
|
+
if ( !host || host.empty? )
|
146
|
+
error "No host defined"
|
147
|
+
return nil
|
148
|
+
end
|
149
|
+
|
150
|
+
if @host_aliases[ host ]
|
151
|
+
host = @host_aliases[ host ]
|
152
|
+
end
|
153
|
+
|
154
|
+
out " aliased host: #{host}"
|
155
|
+
|
156
|
+
begin
|
157
|
+
path = substitute_parameters request
|
158
|
+
rescue ParameterError
|
159
|
+
error
|
160
|
+
return nil
|
161
|
+
end
|
162
|
+
|
163
|
+
out " Path: " + path
|
164
|
+
|
165
|
+
splitted_host = host.split( ":" )
|
166
|
+
|
167
|
+
host_name = splitted_host[0]
|
168
|
+
host_port = splitted_host[1]
|
169
|
+
|
170
|
+
out " Host name: #{host_name} port: #{host_port}"
|
171
|
+
|
172
|
+
if ( request.verb == "GET" )
|
173
|
+
req = Net::HTTP::Get.new( path )
|
174
|
+
if ( true||@user )
|
175
|
+
req.basic_auth( @user, @password )
|
176
|
+
end
|
177
|
+
response = Net::HTTP.start( host_name, host_port ) do |http|
|
178
|
+
http.request( req )
|
179
|
+
end
|
180
|
+
if ( response.is_a? Net::HTTPRedirection )
|
181
|
+
location = URI.parse response["location"]
|
182
|
+
out " Redirected to #{location}, scheme is #{location.scheme}"
|
183
|
+
http = Net::HTTP.new( location.host, location.port )
|
184
|
+
if location.scheme == "https"
|
185
|
+
http.use_ssl = true
|
186
|
+
end
|
187
|
+
http.start do |http|
|
188
|
+
req = Net::HTTP::Get.new( location.path )
|
189
|
+
|
190
|
+
if ( @user )
|
191
|
+
out " setting user #{@user}"
|
192
|
+
req.basic_auth( @user, @password )
|
193
|
+
end
|
194
|
+
|
195
|
+
out " calling #{location.host}, #{location.port}"
|
196
|
+
response = http.request( req )
|
197
|
+
end
|
198
|
+
end
|
199
|
+
elsif( request.verb == "POST" )
|
200
|
+
req = Net::HTTP::Post.new( path )
|
201
|
+
if ( @user )
|
202
|
+
req.basic_auth( @user, @password )
|
203
|
+
end
|
204
|
+
response = Net::HTTP.start( host_name, host_port ) do |http|
|
205
|
+
http.request( req, "" )
|
206
|
+
end
|
207
|
+
elsif( request.verb == "PUT" )
|
208
|
+
if ( !@data_body )
|
209
|
+
error "No body data defined for PUT"
|
210
|
+
return nil
|
211
|
+
end
|
212
|
+
|
213
|
+
if ( xml_body && @show_xmlbody )
|
214
|
+
out "Request body:"
|
215
|
+
out @data_body
|
216
|
+
end
|
217
|
+
|
218
|
+
req = Net::HTTP::Put.new( path )
|
219
|
+
if ( @user )
|
220
|
+
req.basic_auth( @user, @password )
|
221
|
+
end
|
222
|
+
response = Net::HTTP.start( host_name, host_port ) do |http|
|
223
|
+
http.request( req, @data_body )
|
224
|
+
end
|
225
|
+
else
|
226
|
+
STDERR.puts " Test of method '#{request.verb}' not supported yet."
|
227
|
+
unsupported
|
228
|
+
return nil
|
229
|
+
end
|
230
|
+
|
231
|
+
if ( response )
|
232
|
+
out " return code: #{response.code}"
|
233
|
+
if ( xml_result && @show_xmlbody )
|
234
|
+
out "Response body:"
|
235
|
+
out response.body
|
236
|
+
end
|
237
|
+
|
238
|
+
if ( ( return_code && response.code == return_code.to_s ) ||
|
239
|
+
( response.is_a? Net::HTTPSuccess ) )
|
240
|
+
if ( xml_check_wanted && xml_result )
|
241
|
+
if ( xml_result.schema )
|
242
|
+
schema_file = xml_result.schema
|
243
|
+
else
|
244
|
+
schema_file = xml_result.name + ".xsd"
|
245
|
+
end
|
246
|
+
if ( validate_xml response.body, schema_file )
|
247
|
+
out " Response validates against schema '#{schema_file}'"
|
248
|
+
passed
|
249
|
+
else
|
250
|
+
failed
|
251
|
+
end
|
252
|
+
else
|
253
|
+
passed
|
254
|
+
end
|
255
|
+
else
|
256
|
+
failed
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
response
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
def substitute_parameters request
|
265
|
+
path = request.path.clone
|
266
|
+
|
267
|
+
request.parameters.each do |parameter|
|
268
|
+
p = parameter.name
|
269
|
+
arg = eval( "@arg_#{parameter.name}" )
|
270
|
+
if ( !arg )
|
271
|
+
out " Can't substitute parameter '#{p}'. " +
|
272
|
+
"No variable @arg_#{p} defined."
|
273
|
+
raise ParameterError
|
274
|
+
end
|
275
|
+
path.gsub! /<#{p}>/, arg
|
276
|
+
end
|
277
|
+
|
278
|
+
path
|
279
|
+
end
|
280
|
+
|
281
|
+
def validate_xml xml, schema_file
|
282
|
+
tmp = Tempfile.new('rest_test_validator')
|
283
|
+
tmp.print xml
|
284
|
+
tmp_path = tmp.path
|
285
|
+
tmp.close
|
286
|
+
|
287
|
+
found_schema_file = XmlFile.find_file schema_file
|
288
|
+
|
289
|
+
if ( !found_schema_file )
|
290
|
+
out " Unable to find schema file '#{schema_file}'"
|
291
|
+
return false
|
292
|
+
end
|
293
|
+
|
294
|
+
cmd = "/usr/bin/xmllint --noout --schema #{found_schema_file} #{tmp_path} 2>&1"
|
295
|
+
# puts "CMD: " + cmd
|
296
|
+
output = `#{cmd}`
|
297
|
+
if $?.exitstatus > 0
|
298
|
+
out "xmllint return value: #{$?.exitstatus}"
|
299
|
+
out output
|
300
|
+
return false
|
301
|
+
end
|
302
|
+
return true
|
303
|
+
end
|
304
|
+
|
305
|
+
def print_summary
|
306
|
+
undefined = @tested - @unsupported - @failed - @passed - @error - @skipped
|
307
|
+
|
308
|
+
puts "#tester passed #{@passed}"
|
309
|
+
puts "#tester failed #{@failed}"
|
310
|
+
puts "#tester error #{@error}"
|
311
|
+
puts "#tester skipped #{@unsupported + @skipped + undefined}"
|
312
|
+
|
313
|
+
puts
|
314
|
+
|
315
|
+
puts "Total #{@tested} tests"
|
316
|
+
puts " #{@passed} passed"
|
317
|
+
puts " #{@failed} failed"
|
318
|
+
if ( @unsupported > 0 )
|
319
|
+
puts " #{@unsupported} unsupported"
|
320
|
+
end
|
321
|
+
if ( @error > 0 )
|
322
|
+
puts " #{@error} errors"
|
323
|
+
end
|
324
|
+
if ( @skipped > 0 )
|
325
|
+
puts " #{@skipped} skipped"
|
326
|
+
end
|
327
|
+
if ( undefined > 0 )
|
328
|
+
puts " #{undefined} undefined"
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
class TestRunner
|
336
|
+
|
337
|
+
attr_reader :context
|
338
|
+
|
339
|
+
def initialize requests
|
340
|
+
@context = TestContext.new requests
|
341
|
+
end
|
342
|
+
|
343
|
+
def run testfile
|
344
|
+
File.open testfile do |file|
|
345
|
+
eval( file.read, @context.get_binding )
|
346
|
+
end
|
347
|
+
|
348
|
+
@context.print_summary
|
349
|
+
end
|
350
|
+
|
351
|
+
end
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
desc 'Release the website and new gem version'
|
2
|
+
task :deploy => [:check_version, :website, :release] do
|
3
|
+
puts "Remember to create SVN tag:"
|
4
|
+
puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
|
5
|
+
"svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
|
6
|
+
puts "Suggested comment:"
|
7
|
+
puts "Tagging release #{CHANGES}"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
|
11
|
+
task :local_deploy => [:website_generate, :install_gem]
|
12
|
+
|
13
|
+
task :check_version do
|
14
|
+
unless ENV['VERSION']
|
15
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
unless ENV['VERSION'] == VERS
|
19
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
|
25
|
+
task :install_gem_no_doc => [:clean, :package] do
|
26
|
+
sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
|
27
|
+
end
|
data/tasks/website.rake
ADDED
data/test/test_helper.rb
ADDED