grape 0.1.5 → 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.
- data/.gitignore +13 -1
- data/.rspec +1 -1
- data/.travis.yml +1 -0
- data/Gemfile +10 -0
- data/Guardfile +15 -0
- data/README.markdown +472 -67
- data/grape.gemspec +4 -4
- data/lib/grape.rb +17 -5
- data/lib/grape/api.rb +227 -124
- data/lib/grape/cookies.rb +41 -0
- data/lib/grape/endpoint.rb +256 -27
- data/lib/grape/entity.rb +227 -0
- data/lib/grape/middleware/auth/oauth2.rb +1 -2
- data/lib/grape/middleware/base.rb +59 -6
- data/lib/grape/middleware/error.rb +15 -6
- data/lib/grape/middleware/filter.rb +17 -0
- data/lib/grape/middleware/formatter.rb +25 -31
- data/lib/grape/middleware/versioner.rb +20 -20
- data/lib/grape/middleware/versioner/header.rb +59 -0
- data/lib/grape/middleware/versioner/path.rb +42 -0
- data/lib/grape/route.rb +23 -0
- data/lib/grape/util/hash_stack.rb +100 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +651 -162
- data/spec/grape/endpoint_spec.rb +216 -18
- data/spec/grape/entity_spec.rb +320 -0
- data/spec/grape/middleware/auth/basic_spec.rb +2 -2
- data/spec/grape/middleware/auth/digest_spec.rb +4 -6
- data/spec/grape/middleware/exception_spec.rb +1 -0
- data/spec/grape/middleware/formatter_spec.rb +81 -27
- data/spec/grape/middleware/versioner/header_spec.rb +148 -0
- data/spec/grape/middleware/versioner/path_spec.rb +40 -0
- data/spec/grape/middleware/versioner_spec.rb +6 -34
- data/spec/grape/util/hash_stack_spec.rb +133 -0
- data/spec/shared/versioning_examples.rb +77 -0
- data/spec/spec_helper.rb +11 -3
- data/spec/support/basic_auth_encode_helpers.rb +4 -0
- data/spec/support/rack_patch.rb +25 -0
- data/spec/support/versioned_helpers.rb +34 -0
- metadata +140 -241
- data/.yardoc/checksums +0 -13
- data/.yardoc/objects/Grape.dat +0 -0
- data/.yardoc/objects/Grape/API.dat +0 -0
- data/.yardoc/objects/Grape/API/auth_c.dat +0 -0
- data/.yardoc/objects/Grape/API/build_endpoint_c.dat +0 -0
- data/.yardoc/objects/Grape/API/call_c.dat +0 -0
- data/.yardoc/objects/Grape/API/compile_path_c.dat +0 -0
- data/.yardoc/objects/Grape/API/default_format_c.dat +0 -0
- data/.yardoc/objects/Grape/API/delete_c.dat +0 -0
- data/.yardoc/objects/Grape/API/get_c.dat +0 -0
- data/.yardoc/objects/Grape/API/group_c.dat +0 -0
- data/.yardoc/objects/Grape/API/head_c.dat +0 -0
- data/.yardoc/objects/Grape/API/helpers_c.dat +0 -0
- data/.yardoc/objects/Grape/API/http_basic_c.dat +0 -0
- data/.yardoc/objects/Grape/API/inherited_c.dat +0 -0
- data/.yardoc/objects/Grape/API/logger_c.dat +0 -0
- data/.yardoc/objects/Grape/API/namespace_c.dat +0 -0
- data/.yardoc/objects/Grape/API/nest_c.dat +0 -0
- data/.yardoc/objects/Grape/API/post_c.dat +0 -0
- data/.yardoc/objects/Grape/API/prefix_c.dat +0 -0
- data/.yardoc/objects/Grape/API/put_c.dat +0 -0
- data/.yardoc/objects/Grape/API/reset_21_c.dat +0 -0
- data/.yardoc/objects/Grape/API/resource_c.dat +0 -0
- data/.yardoc/objects/Grape/API/resources_c.dat +0 -0
- data/.yardoc/objects/Grape/API/route_c.dat +0 -0
- data/.yardoc/objects/Grape/API/route_set_c.dat +0 -0
- data/.yardoc/objects/Grape/API/scope_c.dat +0 -0
- data/.yardoc/objects/Grape/API/set_c.dat +0 -0
- data/.yardoc/objects/Grape/API/settings_c.dat +0 -0
- data/.yardoc/objects/Grape/API/settings_stack_c.dat +0 -0
- data/.yardoc/objects/Grape/API/version_c.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/block_3D_c.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/block_c.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/call_c.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/call_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/env_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/error_21_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/generate_c.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/header_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/params_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/request_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/status_i.dat +0 -0
- data/.yardoc/objects/Grape/Endpoint/version_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/Basic.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/Basic/authenticator_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/Basic/basic_request_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/Basic/before_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/Basic/credentials_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/Basic/initialize_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/before_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/default_options_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/error_out_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/parse_authorization_header_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/token_class_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Auth/OAuth2/verify_token_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/after_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/app_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/before_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/call_21_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/call_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/default_options_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/env_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/initialize_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/options_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/request_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Base/response_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Error.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Error/call_21_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Error/error_response_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/CONTENT_TYPES.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/after_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/before_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/content_types_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/default_options_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/encode_json_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/encode_txt_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/format_from_extension_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/format_from_header_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/headers_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/mime_array_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Formatter/mime_types_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Prefixer.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Prefixer/before_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Prefixer/prefix_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Versioner.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Versioner/before_i.dat +0 -0
- data/.yardoc/objects/Grape/Middleware/Versioner/default_options_i.dat +0 -0
- data/.yardoc/objects/Grape/MiddlewareStack.dat +0 -0
- data/.yardoc/objects/Grape/MiddlewareStack/initialize_i.dat +0 -0
- data/.yardoc/objects/Grape/MiddlewareStack/stack_i.dat +0 -0
- data/.yardoc/objects/Grape/MiddlewareStack/to_app_i.dat +0 -0
- data/.yardoc/objects/Grape/MiddlewareStack/use_i.dat +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardoc/proxy_types +0 -2
- data/Gemfile.lock +0 -52
- data/autotest/discover.rb +0 -1
|
@@ -20,12 +20,12 @@ describe Grape::Middleware::Auth::Basic do
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it 'should authenticate if given valid creds' do
|
|
23
|
-
get '/whatever', {}, 'HTTP_AUTHORIZATION' =>
|
|
23
|
+
get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin','admin')
|
|
24
24
|
last_response.status.should == 200
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
it 'should throw a 401 is wrong auth is given' do
|
|
28
|
-
get '/whatever', {}, 'HTTP_AUTHORIZATION' =>
|
|
28
|
+
get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('admin','wrong')
|
|
29
29
|
last_response.status.should == 401
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -9,8 +9,6 @@ RSpec::Matchers.define :be_challenge do
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
class Test < Grape::API
|
|
12
|
-
version '1'
|
|
13
|
-
|
|
14
12
|
http_digest({:realm => 'Test Api', :opaque => 'secret'}) do |username|
|
|
15
13
|
{'foo' => 'bar'}[username]
|
|
16
14
|
end
|
|
@@ -26,24 +24,24 @@ describe Grape::Middleware::Auth::Digest do
|
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
it 'should be a digest authentication challenge' do
|
|
29
|
-
get '/
|
|
27
|
+
get '/test'
|
|
30
28
|
last_response.should be_challenge
|
|
31
29
|
end
|
|
32
30
|
|
|
33
31
|
it 'should throw a 401 if no auth is given' do
|
|
34
|
-
get '/
|
|
32
|
+
get '/test'
|
|
35
33
|
last_response.status.should == 401
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
it 'should authenticate if given valid creds' do
|
|
39
37
|
digest_authorize "foo", "bar"
|
|
40
|
-
get '/
|
|
38
|
+
get '/test'
|
|
41
39
|
last_response.status.should == 200
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
it 'should throw a 401 if given invalid creds' do
|
|
45
43
|
digest_authorize "bar", "foo"
|
|
46
|
-
get '/
|
|
44
|
+
get '/test'
|
|
47
45
|
last_response.status.should == 401
|
|
48
46
|
end
|
|
49
47
|
end
|
|
@@ -1,42 +1,65 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Grape::Middleware::Formatter do
|
|
4
|
-
subject{ Grape::Middleware::Formatter.new(app
|
|
4
|
+
subject{ Grape::Middleware::Formatter.new(app) }
|
|
5
5
|
before{ subject.stub!(:dup).and_return(subject) }
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
let(:app){ lambda{|env| [200, {}, [@body]]} }
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
context 'serialization' do
|
|
10
10
|
it 'should look at the bodies for possibly serializable data' do
|
|
11
11
|
@body = {"abc" => "def"}
|
|
12
|
-
status, headers, bodies = *subject.call({'PATH_INFO' => '/somewhere'})
|
|
12
|
+
status, headers, bodies = *subject.call({'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json'})
|
|
13
13
|
bodies.each{|b| b.should == MultiJson.encode(@body) }
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
it 'should call #to_json first if it is available' do
|
|
17
|
-
@body =
|
|
17
|
+
@body = ['foo']
|
|
18
18
|
@body.instance_eval do
|
|
19
19
|
def to_json
|
|
20
20
|
"\"bar\""
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
-
|
|
24
|
-
subject.call({'PATH_INFO' => '/somewhere'}).last.each{|b| b.should == '"bar"'}
|
|
23
|
+
|
|
24
|
+
subject.call({'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json'}).last.each{|b| b.should == '"bar"'}
|
|
25
25
|
end
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
it 'should serialize the #serializable_hash if that is available' do
|
|
28
28
|
class SimpleExample
|
|
29
29
|
def serializable_hash
|
|
30
30
|
{:abc => 'def'}
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
@body = [SimpleExample.new, SimpleExample.new]
|
|
35
|
+
|
|
36
|
+
subject.call({'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json'}).last.each{|b| b.should == '[{"abc":"def"},{"abc":"def"}]'}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should serialize multiple objects that respond to #serializable_hash' do
|
|
40
|
+
class SimpleExample
|
|
41
|
+
def serializable_hash
|
|
42
|
+
{:abc => 'def'}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
34
46
|
@body = SimpleExample.new
|
|
35
|
-
|
|
36
|
-
subject.call({'PATH_INFO' => '/somewhere'}).last.each{|b| b.should == '{"abc":"def"}'}
|
|
47
|
+
|
|
48
|
+
subject.call({'PATH_INFO' => '/somewhere', 'HTTP_ACCEPT' => 'application/json'}).last.each{|b| b.should == '{"abc":"def"}'}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should call #to_xml if the content type is xml' do
|
|
52
|
+
@body = "string"
|
|
53
|
+
@body.instance_eval do
|
|
54
|
+
def to_xml
|
|
55
|
+
"<bar/>"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
subject.call({'PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json'}).last.each{|b| b.should == '<bar/>'}
|
|
37
60
|
end
|
|
38
61
|
end
|
|
39
|
-
|
|
62
|
+
|
|
40
63
|
context 'detection' do
|
|
41
64
|
it 'should use the extension if one is provided' do
|
|
42
65
|
subject.call({'PATH_INFO' => '/info.xml'})
|
|
@@ -44,45 +67,60 @@ describe Grape::Middleware::Formatter do
|
|
|
44
67
|
subject.call({'PATH_INFO' => '/info.json'})
|
|
45
68
|
subject.env['api.format'].should == :json
|
|
46
69
|
end
|
|
47
|
-
|
|
70
|
+
|
|
48
71
|
it 'should use the default format if none is provided' do
|
|
49
72
|
subject.call({'PATH_INFO' => '/info'})
|
|
73
|
+
subject.env['api.format'].should == :txt
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'should use the requested format if provided in headers' do
|
|
77
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json'})
|
|
50
78
|
subject.env['api.format'].should == :json
|
|
51
79
|
end
|
|
80
|
+
|
|
81
|
+
it 'should use the file extension format if provided before headers' do
|
|
82
|
+
subject.call({'PATH_INFO' => '/info.txt', 'HTTP_ACCEPT' => 'application/json'})
|
|
83
|
+
subject.env['api.format'].should == :txt
|
|
84
|
+
end
|
|
52
85
|
|
|
53
86
|
it 'should throw an error on an unrecognized format' do
|
|
54
87
|
err = catch(:error){ subject.call({'PATH_INFO' => '/info.barklar'}) }
|
|
55
88
|
err.should == {:status => 406, :message => "The requested format is not supported."}
|
|
56
89
|
end
|
|
57
90
|
end
|
|
58
|
-
|
|
91
|
+
|
|
59
92
|
context 'Accept header detection' do
|
|
60
93
|
it 'should detect from the Accept header' do
|
|
61
|
-
subject.call({'PATH_INFO' => '/info', '
|
|
94
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/xml'})
|
|
62
95
|
subject.env['api.format'].should == :xml
|
|
63
96
|
end
|
|
64
|
-
|
|
97
|
+
|
|
65
98
|
it 'should look for case-indifferent headers' do
|
|
66
|
-
subject.call({'PATH_INFO' => '/info', '
|
|
99
|
+
subject.call({'PATH_INFO' => '/info', 'http_accept' => 'application/xml'})
|
|
67
100
|
subject.env['api.format'].should == :xml
|
|
68
101
|
end
|
|
69
|
-
|
|
102
|
+
|
|
70
103
|
it 'should use quality rankings to determine formats' do
|
|
71
|
-
subject.call({'PATH_INFO' => '/info', '
|
|
104
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=0.3,application/xml; q=1.0'})
|
|
72
105
|
subject.env['api.format'].should == :xml
|
|
73
|
-
subject.call({'PATH_INFO' => '/info', '
|
|
106
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; q=1.0,application/xml; q=0.3'})
|
|
74
107
|
subject.env['api.format'].should == :json
|
|
75
108
|
end
|
|
76
|
-
|
|
109
|
+
|
|
77
110
|
it 'should handle quality rankings mixed with nothing' do
|
|
78
|
-
subject.call({'PATH_INFO' => '/info', '
|
|
111
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json,application/xml; q=1.0'})
|
|
79
112
|
subject.env['api.format'].should == :xml
|
|
80
113
|
end
|
|
81
|
-
|
|
114
|
+
|
|
82
115
|
it 'should properly parse headers with other attributes' do
|
|
83
|
-
subject.call({'PATH_INFO' => '/info', '
|
|
116
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json; abc=2.3; q=1.0,application/xml; q=0.7'})
|
|
84
117
|
subject.env['api.format'].should == :json
|
|
85
118
|
end
|
|
119
|
+
|
|
120
|
+
it 'should properly parse headers with vendor and api version' do
|
|
121
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/vnd.test-v1+xml'})
|
|
122
|
+
subject.env['api.format'].should == :xml
|
|
123
|
+
end
|
|
86
124
|
end
|
|
87
125
|
|
|
88
126
|
context 'Content-type' do
|
|
@@ -113,9 +151,9 @@ describe Grape::Middleware::Formatter do
|
|
|
113
151
|
body.body.should == ['CUSTOM FORMAT']
|
|
114
152
|
end
|
|
115
153
|
it 'should use default json formatter' do
|
|
116
|
-
@body = 'blah'
|
|
154
|
+
@body = ['blah']
|
|
117
155
|
_, _, body = subject.call({'PATH_INFO' => '/info.json'})
|
|
118
|
-
body.body.should == ['"blah"']
|
|
156
|
+
body.body.should == ['["blah"]']
|
|
119
157
|
end
|
|
120
158
|
it 'should use custom json formatter' do
|
|
121
159
|
subject.options[:formatters][:json] = lambda { |obj| 'CUSTOM JSON FORMAT' }
|
|
@@ -123,4 +161,20 @@ describe Grape::Middleware::Formatter do
|
|
|
123
161
|
body.body.should == ['CUSTOM JSON FORMAT']
|
|
124
162
|
end
|
|
125
163
|
end
|
|
164
|
+
|
|
165
|
+
context 'Input' do
|
|
166
|
+
it 'should parse the body from a POST/PUT and put the contents into rack.request.form_hash' do
|
|
167
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json', 'rack.input' => StringIO.new('{"is_boolean":true,"string":"thing"}')})
|
|
168
|
+
subject.env['rack.request.form_hash']['is_boolean'].should be_true
|
|
169
|
+
subject.env['rack.request.form_hash']['string'].should == 'thing'
|
|
170
|
+
end
|
|
171
|
+
it 'should parse the body from an xml POST/PUT and put the contents into rack.request.from_hash' do
|
|
172
|
+
subject.call({'PATH_INFO' => '/info.xml', 'HTTP_ACCEPT' => 'application/xml', 'rack.input' => StringIO.new('<thing><name>Test</name></thing>')})
|
|
173
|
+
subject.env['rack.request.form_hash']['thing']['name'].should == 'Test'
|
|
174
|
+
end
|
|
175
|
+
it 'should be able to fail gracefully if the body is regular POST content' do
|
|
176
|
+
subject.call({'PATH_INFO' => '/info', 'HTTP_ACCEPT' => 'application/json', 'rack.input' => StringIO.new('name=Other+Test+Thing')})
|
|
177
|
+
subject.env['rack.request.form_hash'].should be_nil
|
|
178
|
+
end
|
|
179
|
+
end
|
|
126
180
|
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Grape::Middleware::Versioner::Header do
|
|
4
|
+
let(:app) { lambda{|env| [200, env, env]} }
|
|
5
|
+
let(:accept) { 'application/vnd.vendor-v1+json' }
|
|
6
|
+
subject { Grape::Middleware::Versioner::Header.new(app, @options || {}) }
|
|
7
|
+
|
|
8
|
+
context 'api.type and api.subtype' do
|
|
9
|
+
it 'should set any type and any subtype' do
|
|
10
|
+
env = subject.call('HTTP_ACCEPT' => '*/*').last
|
|
11
|
+
env['api.type'].should eql '*'
|
|
12
|
+
env['api.subtype'].should eql '*'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should set preferred type and subtype' do
|
|
16
|
+
env = subject.call('HTTP_ACCEPT' => 'text/html').last
|
|
17
|
+
env['api.type'].should eql 'text'
|
|
18
|
+
env['api.subtype'].should eql 'html'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'api.format' do
|
|
23
|
+
it 'should be set' do
|
|
24
|
+
env = subject.call('HTTP_ACCEPT' => accept).last
|
|
25
|
+
env['api.format'].should eql 'json'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should be nil if not provided' do
|
|
29
|
+
env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1').last
|
|
30
|
+
env['api.format'].should eql nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'matched version' do
|
|
35
|
+
before do
|
|
36
|
+
@options = {
|
|
37
|
+
:versions => ['v1'],
|
|
38
|
+
:version_options => {:using => :header}
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'should set api.vendor' do
|
|
43
|
+
env = subject.call('HTTP_ACCEPT' => accept).last
|
|
44
|
+
env['api.vendor'].should eql 'vendor'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should set api.version' do
|
|
48
|
+
env = subject.call('HTTP_ACCEPT' => accept).last
|
|
49
|
+
env['api.version'].should eql 'v1'
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
context 'no header' do
|
|
54
|
+
it 'should return a 200 when no header is set and no strict setting is done' do
|
|
55
|
+
@options = {
|
|
56
|
+
:versions => ['v1'],
|
|
57
|
+
:version_options => {:using => :header}
|
|
58
|
+
}
|
|
59
|
+
subject.call('HTTP_ACCEPT' => '').first.should == 200
|
|
60
|
+
subject.call({}).first.should == 200
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should return a 200 when no header is set but strict header based versioning is disabled' do
|
|
64
|
+
@options = {
|
|
65
|
+
:versions => ['v1'],
|
|
66
|
+
:version_options => {:using => :header, :strict => false}
|
|
67
|
+
}
|
|
68
|
+
subject.call('HTTP_ACCEPT' => '').first.should == 200
|
|
69
|
+
subject.call({}).first.should == 200
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context 'when strict header versioning is used' do
|
|
73
|
+
it 'should return a 406 when no header' do
|
|
74
|
+
@options = {
|
|
75
|
+
:versions => ['v1'],
|
|
76
|
+
:version_options => {:using => :header, :strict => true}
|
|
77
|
+
}
|
|
78
|
+
expect {
|
|
79
|
+
env = subject.call('HTTP_ACCEPT' => '').last
|
|
80
|
+
}.to throw_symbol(
|
|
81
|
+
:error,
|
|
82
|
+
:status => 406,
|
|
83
|
+
:headers => {'X-Cascade' => 'pass'},
|
|
84
|
+
:message => "406 API Version Not Found"
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should return a 406 when incorrect header format is used' do
|
|
89
|
+
@options = {
|
|
90
|
+
:versions => ['v1'],
|
|
91
|
+
:version_options => {:using => :header, :strict => true}
|
|
92
|
+
}
|
|
93
|
+
expect {
|
|
94
|
+
env = subject.call('HTTP_ACCEPT' => '*/*').last
|
|
95
|
+
}.to throw_symbol(
|
|
96
|
+
:error,
|
|
97
|
+
:status => 406,
|
|
98
|
+
:headers => {'X-Cascade' => 'pass'},
|
|
99
|
+
:message => "406 API Version Not Found"
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should return a 200 when proper header is set' do
|
|
104
|
+
@options = {
|
|
105
|
+
:versions => ['v1'],
|
|
106
|
+
:version_options => {:using => :header, :strict => true}
|
|
107
|
+
}
|
|
108
|
+
subject.call('HTTP_ACCEPT' => 'application/vnd.testing-v1+json').first.should == 200
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context 'vendors' do
|
|
115
|
+
before do
|
|
116
|
+
@options = {
|
|
117
|
+
:version => ['v1'],
|
|
118
|
+
:version_options => {:using => :header, :vendor => 'vendor'}
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'should match with correct vendor' do
|
|
123
|
+
status = subject.call('HTTP_ACCEPT' => accept).first
|
|
124
|
+
status.should == 200
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'should not match with an incorrect vendor' do
|
|
128
|
+
expect {
|
|
129
|
+
env = subject.call('HTTP_ACCEPT' => 'application/vnd.othervendor-v1+json').last
|
|
130
|
+
}.to throw_symbol(:error, :status => 406, :headers => {'X-Cascade' => 'pass'}, :message => "406 API Version Not Found")
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context 'no matched version' do
|
|
135
|
+
before do
|
|
136
|
+
@options = {
|
|
137
|
+
:versions => ['unknown_version'],
|
|
138
|
+
:version_options => {:using => :header}
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'should throw 406 error with X-Cascade header set to pass' do
|
|
143
|
+
expect {
|
|
144
|
+
env = subject.call('HTTP_ACCEPT' => accept).last
|
|
145
|
+
}.to throw_symbol(:error, :status => 406, :headers => {'X-Cascade' => 'pass'}, :message => "406 API Version Not Found")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Grape::Middleware::Versioner::Path do
|
|
4
|
+
let(:app) { lambda{|env| [200, env, env['api.version']]} }
|
|
5
|
+
subject { Grape::Middleware::Versioner::Path.new(app, @options || {}) }
|
|
6
|
+
|
|
7
|
+
it 'should set the API version based on the first path' do
|
|
8
|
+
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should cut the version out of the path' do
|
|
12
|
+
subject.call('PATH_INFO' => '/v1/awesome')[1]['PATH_INFO'].should == '/awesome'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should provide a nil version if no path is given' do
|
|
16
|
+
subject.call('PATH_INFO' => '/').last.should be_nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context 'with a pattern' do
|
|
20
|
+
before{ @options = {:pattern => /v./i} }
|
|
21
|
+
it 'should set the version if it matches' do
|
|
22
|
+
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should ignore the version if it fails to match' do
|
|
26
|
+
subject.call('PATH_INFO' => '/awesome/radical').last.should be_nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context 'with specified versions' do
|
|
31
|
+
before{ @options = {:versions => ['v1', 'v2']}}
|
|
32
|
+
it 'should throw an error if a non-allowed version is specified' do
|
|
33
|
+
catch(:error){subject.call('PATH_INFO' => '/v3/awesome')}[:status].should == 404
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'should allow versions that have been specified' do
|
|
37
|
+
subject.call('PATH_INFO' => '/v1/asoasd').last.should == 'v1'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -1,40 +1,12 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Grape::Middleware::Versioner do
|
|
4
|
-
let(:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
it 'should set the API version based on the first path' do
|
|
8
|
-
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
|
|
4
|
+
let(:klass) { Grape::Middleware::Versioner }
|
|
5
|
+
it 'should recognize :path' do
|
|
6
|
+
klass.using(:path).should == Grape::Middleware::Versioner::Path
|
|
9
7
|
end
|
|
10
|
-
|
|
11
|
-
it 'should
|
|
12
|
-
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'should provide a nil version if no path is given' do
|
|
16
|
-
subject.call('PATH_INFO' => '/').last.should be_nil
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
context 'with a pattern' do
|
|
20
|
-
before{ @options = {:pattern => /v./i} }
|
|
21
|
-
it 'should set the version if it matches' do
|
|
22
|
-
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it 'should ignore the version if it fails to match' do
|
|
26
|
-
subject.call('PATH_INFO' => '/awesome/radical').last.should be_nil
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
context 'with specified versions' do
|
|
31
|
-
before{ @options = {:versions => ['v1', 'v2']}}
|
|
32
|
-
it 'should throw an error if a non-allowed version is specified' do
|
|
33
|
-
catch(:error){subject.call('PATH_INFO' => '/v3/awesome')}[:status].should == 404
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it 'should allow versions that have been specified' do
|
|
37
|
-
subject.call('PATH_INFO' => '/v1/asoasd').last.should == 'v1'
|
|
38
|
-
end
|
|
8
|
+
|
|
9
|
+
it 'should recognize :header' do
|
|
10
|
+
klass.using(:header).should == Grape::Middleware::Versioner::Header
|
|
39
11
|
end
|
|
40
12
|
end
|