strelka 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +133 -1
- data/History.rdoc +15 -0
- data/Rakefile +1 -1
- data/bin/strelka +23 -0
- data/lib/strelka.rb +2 -2
- data/lib/strelka/app/auth.rb +15 -12
- data/lib/strelka/app/parameters.rb +8 -2
- data/lib/strelka/app/restresources.rb +67 -32
- data/lib/strelka/app/routing.rb +1 -1
- data/lib/strelka/httprequest.rb +10 -2
- data/lib/strelka/httpresponse.rb +2 -2
- data/lib/strelka/paramvalidator.rb +63 -8
- data/spec/strelka/app/auth_spec.rb +50 -33
- data/spec/strelka/app/filters_spec.rb +2 -2
- data/spec/strelka/app/parameters_spec.rb +30 -1
- data/spec/strelka/app/restresources_spec.rb +51 -6
- data/spec/strelka/app/routing_spec.rb +2 -9
- data/spec/strelka/httprequest_spec.rb +5 -1
- data/spec/strelka/paramvalidator_spec.rb +293 -66
- metadata +37 -82
- metadata.gz.sig +0 -0
data/lib/strelka/app/routing.rb
CHANGED
@@ -295,7 +295,7 @@ module Strelka::App::Routing
|
|
295
295
|
# Track which route was chosen for later plugins
|
296
296
|
request.notes[:routing][:route] = route
|
297
297
|
# Bind the action of the route and call it
|
298
|
-
return route[:action].bind( self ).call( request, &block )
|
298
|
+
return super { route[:action].bind( self ).call( request, &block ) }
|
299
299
|
else
|
300
300
|
finish_with HTTP::NOT_FOUND, "The requested resource was not found on this server."
|
301
301
|
end
|
data/lib/strelka/httprequest.rb
CHANGED
@@ -44,7 +44,7 @@ class Strelka::HTTPRequest < Mongrel2::HTTPRequest
|
|
44
44
|
@uri = nil
|
45
45
|
@verb = self.headers[:method].to_sym
|
46
46
|
@params = nil
|
47
|
-
@notes = Hash.new
|
47
|
+
@notes = Hash.new {|h,k| h[k] = {} }
|
48
48
|
@cookies = nil
|
49
49
|
end
|
50
50
|
|
@@ -174,7 +174,15 @@ class Strelka::HTTPRequest < Mongrel2::HTTPRequest
|
|
174
174
|
### ?arg1=yes&arg2=no&arg3 #=> {'arg1' => 'yes', 'arg2' => 'no', 'arg3' => nil}
|
175
175
|
def parse_query_args
|
176
176
|
return {} if self.uri.query.nil?
|
177
|
-
|
177
|
+
query_args = begin
|
178
|
+
URI.decode_www_form( self.uri.query )
|
179
|
+
rescue => err
|
180
|
+
self.log.error "%p while parsing query %p: %s" %
|
181
|
+
[ err.class, self.uri.query, err.message ]
|
182
|
+
{}
|
183
|
+
end
|
184
|
+
|
185
|
+
return merge_query_args( query_args )
|
178
186
|
end
|
179
187
|
|
180
188
|
|
data/lib/strelka/httpresponse.rb
CHANGED
@@ -28,7 +28,7 @@ class Strelka::HTTPResponse < Mongrel2::HTTPResponse
|
|
28
28
|
@charset = nil
|
29
29
|
@languages = []
|
30
30
|
@encodings = []
|
31
|
-
@notes = Hash.new
|
31
|
+
@notes = Hash.new {|h,k| h[k] = {} }
|
32
32
|
@cookies = nil
|
33
33
|
|
34
34
|
super
|
@@ -133,7 +133,7 @@ class Strelka::HTTPResponse < Mongrel2::HTTPResponse
|
|
133
133
|
return ( self.charset ||
|
134
134
|
self.content_type_charset ||
|
135
135
|
self.entity_body_charset ||
|
136
|
-
|
136
|
+
Encoding.default_internal ||
|
137
137
|
Encoding.default_external ||
|
138
138
|
Encoding::ISO_8859_1 )
|
139
139
|
end
|
@@ -2,12 +2,9 @@
|
|
2
2
|
# vim: set nosta noet ts=4 sw=4:
|
3
3
|
# encoding: utf-8
|
4
4
|
|
5
|
-
#encoding: utf-8
|
6
|
-
|
7
5
|
require 'uri'
|
8
6
|
require 'forwardable'
|
9
7
|
require 'date'
|
10
|
-
require 'formvalidator'
|
11
8
|
require 'loggability'
|
12
9
|
|
13
10
|
require 'strelka/mixins'
|
@@ -53,7 +50,7 @@ require 'strelka/app' unless defined?( Strelka::App )
|
|
53
50
|
# return tmpl
|
54
51
|
# end
|
55
52
|
#
|
56
|
-
class Strelka::ParamValidator
|
53
|
+
class Strelka::ParamValidator
|
57
54
|
extend Forwardable,
|
58
55
|
Loggability,
|
59
56
|
Strelka::MethodUtilities
|
@@ -379,6 +376,40 @@ class Strelka::ParamValidator < ::FormValidator
|
|
379
376
|
hostname = /\A(#{domainlabel}\.)*#{toplabel}\z/
|
380
377
|
end
|
381
378
|
|
379
|
+
# Validation regexp for JSON
|
380
|
+
# Converted to oniguruma syntax from the PCRE example at:
|
381
|
+
# http://stackoverflow.com/questions/2583472/regex-to-validate-json
|
382
|
+
JSON_VALIDATOR_RE = begin
|
383
|
+
pair = ''
|
384
|
+
|
385
|
+
json = /^
|
386
|
+
(?<json> \s* (?:
|
387
|
+
# number
|
388
|
+
(?: 0 | -? [1-9]\d* (?:\.\d+)? (?:[eE][+-]?\d+)? )
|
389
|
+
|
|
390
|
+
# boolean
|
391
|
+
(?: true | false | null )
|
392
|
+
|
|
393
|
+
# string
|
394
|
+
"(?: [^"\\[:cntrl:]]* | \\["\\bfnrt\/] | \\u\p{XDigit}{4} )*"
|
395
|
+
|
|
396
|
+
# array
|
397
|
+
\[ (?: \g<json> (?: , \g<json> )* )? \s* \]
|
398
|
+
|
|
399
|
+
# object
|
400
|
+
\{
|
401
|
+
(?:
|
402
|
+
# first pair
|
403
|
+
\s* "(?: [^"\\]* | \\["\\bfnrt\/] | \\u\p{XDigit}{4} )*" \s* : \g<json>
|
404
|
+
# following pairs
|
405
|
+
(?: , \s* "(?: [^"\\]* | \\["\\bfnrt\/] | \\u\p{XDigit}{4} )*" \s* : \g<json> )*
|
406
|
+
)?
|
407
|
+
\s*
|
408
|
+
\}
|
409
|
+
) \s* )
|
410
|
+
\z/ux
|
411
|
+
end
|
412
|
+
|
382
413
|
# The Hash of builtin constraints that are validated against a regular
|
383
414
|
# expression.
|
384
415
|
# :TODO: Document that these are the built-in constraints that can be used in a route
|
@@ -396,18 +427,40 @@ class Strelka::ParamValidator < ::FormValidator
|
|
396
427
|
:uri => /^(?<uri>#{URI::URI_REF})$/,
|
397
428
|
:uuid => /^(?<uuid>[[:xdigit:]]{8}(?:-[[:xdigit:]]{4}){3}-[[:xdigit:]]{12})$/i,
|
398
429
|
:date => /.*\d.*/,
|
430
|
+
:json => JSON_VALIDATOR_RE,
|
399
431
|
}
|
400
432
|
|
401
433
|
# Field values which result in a valid ‘true’ value for :boolean constraints
|
402
434
|
TRUE_VALUES = %w[t true y yes 1]
|
403
435
|
|
404
436
|
|
437
|
+
#
|
438
|
+
# Class methods
|
439
|
+
#
|
440
|
+
|
441
|
+
##
|
442
|
+
# Hash of named constraint patterns
|
443
|
+
singleton_attr_reader :constraint_patterns
|
444
|
+
@constraint_patterns = BUILTIN_CONSTRAINT_PATTERNS.dup
|
445
|
+
|
446
|
+
|
405
447
|
### Return true if name is the name of a built-in constraint.
|
406
448
|
def self::valid?( name )
|
407
449
|
return BUILTIN_CONSTRAINT_PATTERNS.key?( name.to_sym )
|
408
450
|
end
|
409
451
|
|
410
452
|
|
453
|
+
### Reset the named patterns to the defaults. Mostly used for testing.
|
454
|
+
def self::reset_constraint_patterns
|
455
|
+
@constraint_patterns.replace( BUILTIN_CONSTRAINT_PATTERNS )
|
456
|
+
end
|
457
|
+
|
458
|
+
|
459
|
+
|
460
|
+
#
|
461
|
+
# Instance methods
|
462
|
+
#
|
463
|
+
|
411
464
|
### Create a new BuiltinConstraint using the pattern named name for the specified field.
|
412
465
|
def initialize( field, name, *options, &block )
|
413
466
|
name ||= field
|
@@ -434,7 +487,7 @@ class Strelka::ParamValidator < ::FormValidator
|
|
434
487
|
return custom_block
|
435
488
|
else
|
436
489
|
post_processor = "post_process_%s" % [ @pattern_name ]
|
437
|
-
return nil unless self.respond_to?( post_processor )
|
490
|
+
return nil unless self.respond_to?( post_processor, true )
|
438
491
|
return self.method( post_processor )
|
439
492
|
end
|
440
493
|
end
|
@@ -710,12 +763,13 @@ class Strelka::ParamValidator < ::FormValidator
|
|
710
763
|
end
|
711
764
|
|
712
765
|
|
713
|
-
## Fetch the constraint/s that apply to the parameter named name as a Regexp, if possible.
|
766
|
+
## Fetch the constraint/s that apply to the parameter named +name+ as a Regexp, if possible.
|
714
767
|
def constraint_regexp_for( name )
|
715
768
|
self.log.debug " searching for a constraint for %p" % [ name ]
|
716
769
|
|
717
770
|
# Fetch the constraint's regexp
|
718
|
-
constraint = self.constraints[ name.to_sym ]
|
771
|
+
constraint = self.constraints[ name.to_sym ] or
|
772
|
+
raise NameError, "no such parameter %p" % [ name ]
|
719
773
|
raise ScriptError,
|
720
774
|
"can't route on a parameter with a %p" % [ constraint.class ] unless
|
721
775
|
constraint.respond_to?( :pattern )
|
@@ -758,6 +812,7 @@ class Strelka::ParamValidator < ::FormValidator
|
|
758
812
|
### Index fetch operator; fetch the validated (and possible parsed) value for
|
759
813
|
### form field +key+.
|
760
814
|
def []( key )
|
815
|
+
self.validate unless self.validated?
|
761
816
|
return @valid[ key.to_sym ]
|
762
817
|
end
|
763
818
|
|
@@ -766,7 +821,7 @@ class Strelka::ParamValidator < ::FormValidator
|
|
766
821
|
### to the specified +val+.
|
767
822
|
def []=( key, val )
|
768
823
|
@parsed_params = nil
|
769
|
-
|
824
|
+
@valid[ key.to_sym ] = val
|
770
825
|
end
|
771
826
|
|
772
827
|
|
@@ -234,7 +234,6 @@ describe Strelka::App::Auth do
|
|
234
234
|
|
235
235
|
end
|
236
236
|
|
237
|
-
|
238
237
|
it "allows negative auth criteria to be declared with a string" do
|
239
238
|
@app.no_auth_for( '/string' )
|
240
239
|
app = @app.new
|
@@ -268,10 +267,7 @@ describe Strelka::App::Auth do
|
|
268
267
|
end
|
269
268
|
|
270
269
|
it "allows negative auth criteria to be declared with a string and a block" do
|
271
|
-
@app.no_auth_for( 'string' )
|
272
|
-
Strelka.log.debug "Checking request verb: %p" % [ req.verb ]
|
273
|
-
req.verb == :GET
|
274
|
-
end
|
270
|
+
@app.no_auth_for( 'string' ) {|req| req.verb == :GET }
|
275
271
|
|
276
272
|
app = @app.new
|
277
273
|
|
@@ -309,8 +305,8 @@ describe Strelka::App::Auth do
|
|
309
305
|
it "allows negative auth criteria to be declared with just a block" do
|
310
306
|
@app.no_auth_for do |req|
|
311
307
|
req.app_path == '/foom' &&
|
312
|
-
|
313
|
-
|
308
|
+
req.verb == :GET &&
|
309
|
+
req.headers.accept.include?( 'text/plain' )
|
314
310
|
end
|
315
311
|
|
316
312
|
app = @app.new
|
@@ -351,10 +347,9 @@ describe Strelka::App::Auth do
|
|
351
347
|
end
|
352
348
|
|
353
349
|
it "allows perms criteria to be declared with a string and a block" do
|
354
|
-
@app.require_perms_for( '/string'
|
355
|
-
|
356
|
-
|
357
|
-
perms
|
350
|
+
@app.require_perms_for( '/string', :stringperm, :otherperm )
|
351
|
+
@app.require_perms_for( '/string', :rawdata ) do |req|
|
352
|
+
req.headers.accept && req.headers.accept =~ /json/i
|
358
353
|
end
|
359
354
|
app = @app.new
|
360
355
|
|
@@ -364,34 +359,60 @@ describe Strelka::App::Auth do
|
|
364
359
|
app.required_perms_for( req ).should == []
|
365
360
|
end
|
366
361
|
|
362
|
+
it "allows multiple perms criteria for the same path" do
|
363
|
+
@app.no_auth_for( '' ) {|req| req.verb == :GET }
|
364
|
+
@app.require_perms_for %r{.*}, :it_assets_webapp
|
365
|
+
@app.require_perms_for( %r{.*}, :@sysadmin ) {|req, m| req.verb != :GET }
|
366
|
+
|
367
|
+
app = @app.new
|
368
|
+
|
369
|
+
req = @request_factory.get( '/api/v1' )
|
370
|
+
app.required_perms_for( req ).should == [ :it_assets_webapp ]
|
371
|
+
req = @request_factory.post( '/api/v1' )
|
372
|
+
app.required_perms_for( req ).should == [ :it_assets_webapp, :@sysadmin ]
|
373
|
+
req = @request_factory.get( '/api/v1/users' )
|
374
|
+
app.required_perms_for( req ).should == [ :it_assets_webapp ]
|
375
|
+
req = @request_factory.post( '/api/v1/users' )
|
376
|
+
app.required_perms_for( req ).should == [ :it_assets_webapp, :@sysadmin ]
|
377
|
+
end
|
378
|
+
|
367
379
|
it "allows perms criteria to be declared with a regexp and a block" do
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
380
|
+
userclass = Class.new do
|
381
|
+
def self::[]( username )
|
382
|
+
self.new(username)
|
383
|
+
end
|
384
|
+
def initialize( username ); @username = username; end
|
385
|
+
def is_admin?
|
386
|
+
@username == 'madeline'
|
387
|
+
end
|
388
|
+
end
|
389
|
+
@app.require_perms_for( %r{^/user}, :admin )
|
390
|
+
@app.require_perms_for( %r{^/user(/(?<username>\w+))?}, :superuser ) do |req, match|
|
391
|
+
user = userclass[ match[:username] ]
|
392
|
+
user.is_admin?
|
372
393
|
end
|
373
394
|
app = @app.new
|
374
395
|
|
375
|
-
req = @request_factory.get( '/api/v1/
|
396
|
+
req = @request_factory.get( '/api/v1/user' )
|
376
397
|
app.required_perms_for( req ).should == [ :admin ]
|
377
|
-
req = @request_factory.get( '/api/v1/
|
378
|
-
app.required_perms_for( req ).should == [ :admin
|
379
|
-
req = @request_factory.get( '/api/v1/
|
380
|
-
app.required_perms_for( req ).should == []
|
398
|
+
req = @request_factory.get( '/api/v1/user/jzero' )
|
399
|
+
app.required_perms_for( req ).should == [ :admin ]
|
400
|
+
req = @request_factory.get( '/api/v1/user/madeline' )
|
401
|
+
app.required_perms_for( req ).should == [ :admin, :superuser ]
|
381
402
|
end
|
382
403
|
|
383
|
-
it "allows perms
|
404
|
+
it "allows perms the same as the appid to be declared with just a block" do
|
384
405
|
@app.require_perms_for do |req|
|
385
|
-
req.
|
406
|
+
req.verb != :GET
|
386
407
|
end
|
387
408
|
app = @app.new
|
388
409
|
|
389
|
-
req = @request_factory.get( '/api/v1/
|
390
|
-
app.required_perms_for( req ).should == [
|
391
|
-
req = @request_factory.
|
392
|
-
app.required_perms_for( req ).should == [ :
|
393
|
-
req = @request_factory.
|
394
|
-
app.required_perms_for( req ).should == [ :
|
410
|
+
req = @request_factory.get( '/api/v1/accounts' )
|
411
|
+
app.required_perms_for( req ).should == []
|
412
|
+
req = @request_factory.post( '/api/v1/accounts', '' )
|
413
|
+
app.required_perms_for( req ).should == [ :auth_test ]
|
414
|
+
req = @request_factory.put( '/api/v1/accounts/1', '' )
|
415
|
+
app.required_perms_for( req ).should == [ :auth_test ]
|
395
416
|
end
|
396
417
|
|
397
418
|
it "allows negative perms criteria to be declared with a string" do
|
@@ -434,11 +455,7 @@ describe Strelka::App::Auth do
|
|
434
455
|
@app.no_perms_for( %r{^/collection/(?<collname>[^/]+)} ) do |req, match|
|
435
456
|
public_collections = %w[degasse ione champhion]
|
436
457
|
collname = match[:collname]
|
437
|
-
|
438
|
-
true
|
439
|
-
else
|
440
|
-
false
|
441
|
-
end
|
458
|
+
public_collections.include?( collname )
|
442
459
|
end
|
443
460
|
app = @app.new
|
444
461
|
|
@@ -155,7 +155,7 @@ describe Strelka::App::Filters do
|
|
155
155
|
res = @app.new.handle( req )
|
156
156
|
|
157
157
|
req.notes[:saw][:request].should be_true()
|
158
|
-
res.notes[:saw][:response].should
|
158
|
+
res.notes[:saw][:response].should be_nil()
|
159
159
|
end
|
160
160
|
|
161
161
|
end
|
@@ -194,7 +194,7 @@ describe Strelka::App::Filters do
|
|
194
194
|
|
195
195
|
res = @app.new.handle( req )
|
196
196
|
|
197
|
-
req.notes[:saw][:request].should
|
197
|
+
req.notes[:saw][:request].should be_nil()
|
198
198
|
res.notes[:saw][:response].should be_true()
|
199
199
|
end
|
200
200
|
|
@@ -64,7 +64,7 @@ describe Strelka::App::Parameters do
|
|
64
64
|
@app.paramvalidator.should be_a( Strelka::ParamValidator )
|
65
65
|
end
|
66
66
|
|
67
|
-
it "can declare a parameter with a
|
67
|
+
it "can declare a parameter with a regular-expression constraint" do
|
68
68
|
@app.class_eval do
|
69
69
|
param :username, /\w+/i
|
70
70
|
end
|
@@ -72,6 +72,35 @@ describe Strelka::App::Parameters do
|
|
72
72
|
@app.paramvalidator.param_names.should == [ 'username' ]
|
73
73
|
end
|
74
74
|
|
75
|
+
it "can declare a parameter with a builtin constraint" do
|
76
|
+
@app.class_eval do
|
77
|
+
param :comment_body, :printable
|
78
|
+
end
|
79
|
+
|
80
|
+
@app.paramvalidator.param_names.should == [ 'comment_body' ]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can declare a parameter with a Proc constraint" do
|
84
|
+
@app.class_eval do
|
85
|
+
param :start_time do |val|
|
86
|
+
Time.parse( val ) rescue nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
@app.paramvalidator.param_names.should == [ 'start_time' ]
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
it "can declare a parameter with a block constraint" do
|
95
|
+
@app.class_eval do
|
96
|
+
param :created_at do |val|
|
97
|
+
Time.parse(val) rescue nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@app.paramvalidator.param_names.should == [ 'created_at' ]
|
102
|
+
end
|
103
|
+
|
75
104
|
|
76
105
|
it "inherits parameters from its superclass" do
|
77
106
|
@app.class_eval do
|
@@ -32,6 +32,15 @@ describe Strelka::App::RestResources do
|
|
32
32
|
setup_config_db()
|
33
33
|
|
34
34
|
@request_factory = Mongrel2::RequestFactory.new( route: '/api/v1' )
|
35
|
+
|
36
|
+
# Add some dataset methods via various alternative methods to ensure they show up too
|
37
|
+
name_selection = Module.new do
|
38
|
+
def by_name( name )
|
39
|
+
return self.filter( name: name )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
Mongrel2::Config::Server.subset( :with_ephemeral_ports ) { port > 1024 }
|
43
|
+
Mongrel2::Config::Server.dataset_module( name_selection )
|
35
44
|
end
|
36
45
|
|
37
46
|
after( :all ) do
|
@@ -139,6 +148,8 @@ describe Strelka::App::RestResources do
|
|
139
148
|
describe "auto-generates routes:" do
|
140
149
|
|
141
150
|
before( :each ) do
|
151
|
+
Mongrel2::Config.subclasses.each {|klass| klass.truncate }
|
152
|
+
|
142
153
|
# Create two servers in the config db to test with
|
143
154
|
server 'test-server' do
|
144
155
|
name "Test"
|
@@ -146,6 +157,7 @@ describe Strelka::App::RestResources do
|
|
146
157
|
host 'monitor'
|
147
158
|
host 'adminpanel'
|
148
159
|
host 'api'
|
160
|
+
port 80
|
149
161
|
end
|
150
162
|
server 'step-server' do
|
151
163
|
name 'Step'
|
@@ -190,7 +202,8 @@ describe Strelka::App::RestResources do
|
|
190
202
|
end
|
191
203
|
|
192
204
|
it "supports limiting the result set when fetching the resource collection" do
|
193
|
-
req = @request_factory.get( '/api/v1/servers?limit=1',
|
205
|
+
req = @request_factory.get( '/api/v1/servers?limit=1',
|
206
|
+
'Accept' => 'application/json' )
|
194
207
|
res = @app.new.handle( req )
|
195
208
|
|
196
209
|
res.status.should == HTTP::OK
|
@@ -202,7 +215,8 @@ describe Strelka::App::RestResources do
|
|
202
215
|
end
|
203
216
|
|
204
217
|
it "supports paging the result set when fetching the resource collection" do
|
205
|
-
req = @request_factory.get( '/api/v1/servers?limit=1;offset=1',
|
218
|
+
req = @request_factory.get( '/api/v1/servers?limit=1;offset=1',
|
219
|
+
'Accept' => 'application/json' )
|
206
220
|
res = @app.new.handle( req )
|
207
221
|
|
208
222
|
res.status.should == HTTP::OK
|
@@ -214,7 +228,8 @@ describe Strelka::App::RestResources do
|
|
214
228
|
end
|
215
229
|
|
216
230
|
it "supports ordering the result by a single column" do
|
217
|
-
req = @request_factory.get( '/api/v1/servers?order=name',
|
231
|
+
req = @request_factory.get( '/api/v1/servers?order=name',
|
232
|
+
'Accept' => 'application/json' )
|
218
233
|
res = @app.new.handle( req )
|
219
234
|
|
220
235
|
res.status.should == HTTP::OK
|
@@ -226,8 +241,8 @@ describe Strelka::App::RestResources do
|
|
226
241
|
end
|
227
242
|
|
228
243
|
it "supports ordering the result by multiple columns" do
|
229
|
-
|
230
|
-
|
244
|
+
req = @request_factory.get( '/api/v1/servers?order=id;order=name',
|
245
|
+
'Accept' => 'application/json' )
|
231
246
|
res = @app.new.handle( req )
|
232
247
|
|
233
248
|
res.status.should == HTTP::OK
|
@@ -268,7 +283,8 @@ describe Strelka::App::RestResources do
|
|
268
283
|
end
|
269
284
|
|
270
285
|
it "has a GET route for fetching the resource via one of its dataset methods" do
|
271
|
-
req = @request_factory.get( '/api/v1/servers/by_uuid/test-server',
|
286
|
+
req = @request_factory.get( '/api/v1/servers/by_uuid/test-server',
|
287
|
+
:accept => 'application/json' )
|
272
288
|
res = @app.new.handle( req )
|
273
289
|
|
274
290
|
res.status.should == HTTP::OK
|
@@ -280,6 +296,35 @@ describe Strelka::App::RestResources do
|
|
280
296
|
body.first['uuid'].should == 'test-server'
|
281
297
|
end
|
282
298
|
|
299
|
+
it "has a GET route for fetching the resource via a subset" do
|
300
|
+
req = @request_factory.get( '/api/v1/servers/with_ephemeral_ports',
|
301
|
+
:accept => 'application/json' )
|
302
|
+
res = @app.new.handle( req )
|
303
|
+
|
304
|
+
res.status.should == HTTP::OK
|
305
|
+
body = Yajl.load( res.body )
|
306
|
+
|
307
|
+
body.should be_an( Array )
|
308
|
+
body.should have( 1 ).member
|
309
|
+
body.first.should be_a( Hash )
|
310
|
+
body.first['port'].should > 1024
|
311
|
+
end
|
312
|
+
|
313
|
+
it "has a GET route for methods declared in a named dataset module" do
|
314
|
+
req = @request_factory.get( '/api/v1/servers/by_name/Step',
|
315
|
+
:accept => 'application/json' )
|
316
|
+
res = @app.new.handle( req )
|
317
|
+
|
318
|
+
res.status.should == HTTP::OK
|
319
|
+
body = Yajl.load( res.body )
|
320
|
+
|
321
|
+
body.should be_an( Array )
|
322
|
+
body.should have( 1 ).member
|
323
|
+
body.first.should be_a( Hash )
|
324
|
+
body.first['name'].should == 'Step'
|
325
|
+
end
|
326
|
+
|
327
|
+
|
283
328
|
it "has a GET route for fetching the resource's associated objects" do
|
284
329
|
req = @request_factory.get( '/api/v1/servers/1/hosts' )
|
285
330
|
res = @app.new.handle( req )
|