strelka 0.1.0 → 0.2.0
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 +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 )
|