grape 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (62) hide show
  1. checksums.yaml +5 -13
  2. data/.rubocop.yml +6 -6
  3. data/.travis.yml +11 -2
  4. data/CHANGELOG.md +23 -1
  5. data/Gemfile +11 -10
  6. data/Guardfile +5 -6
  7. data/README.md +194 -13
  8. data/UPGRADING.md +3 -3
  9. data/grape.gemspec +1 -1
  10. data/grape.png +0 -0
  11. data/lib/grape/api.rb +21 -13
  12. data/lib/grape/endpoint.rb +31 -13
  13. data/lib/grape/exceptions/validation.rb +2 -2
  14. data/lib/grape/locale/en.yml +2 -0
  15. data/lib/grape/middleware/auth/oauth2.rb +69 -65
  16. data/lib/grape/middleware/error.rb +4 -2
  17. data/lib/grape/middleware/formatter.rb +2 -2
  18. data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
  19. data/lib/grape/middleware/versioner/header.rb +3 -3
  20. data/lib/grape/util/hash_stack.rb +1 -1
  21. data/lib/grape/validations.rb +22 -8
  22. data/lib/grape/validations/default.rb +1 -1
  23. data/lib/grape/validations/exactly_one_of.rb +26 -0
  24. data/lib/grape/validations/mutual_exclusion.rb +25 -0
  25. data/lib/grape/validations/presence.rb +1 -1
  26. data/lib/grape/validations/regexp.rb +2 -1
  27. data/lib/grape/validations/values.rb +7 -1
  28. data/lib/grape/version.rb +1 -1
  29. data/spec/grape/api_spec.rb +390 -333
  30. data/spec/grape/endpoint_spec.rb +129 -99
  31. data/spec/grape/entity_spec.rb +47 -27
  32. data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
  33. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -1
  34. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -2
  35. data/spec/grape/exceptions/missing_option_spec.rb +1 -1
  36. data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
  37. data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
  38. data/spec/grape/middleware/auth/basic_spec.rb +3 -3
  39. data/spec/grape/middleware/auth/digest_spec.rb +4 -4
  40. data/spec/grape/middleware/auth/oauth2_spec.rb +11 -11
  41. data/spec/grape/middleware/base_spec.rb +9 -9
  42. data/spec/grape/middleware/error_spec.rb +4 -4
  43. data/spec/grape/middleware/exception_spec.rb +17 -17
  44. data/spec/grape/middleware/formatter_spec.rb +38 -38
  45. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +11 -11
  46. data/spec/grape/middleware/versioner/header_spec.rb +39 -39
  47. data/spec/grape/middleware/versioner/param_spec.rb +10 -10
  48. data/spec/grape/middleware/versioner/path_spec.rb +7 -7
  49. data/spec/grape/middleware/versioner_spec.rb +4 -4
  50. data/spec/grape/path_spec.rb +6 -6
  51. data/spec/grape/util/hash_stack_spec.rb +22 -22
  52. data/spec/grape/validations/coerce_spec.rb +34 -34
  53. data/spec/grape/validations/default_spec.rb +16 -16
  54. data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
  55. data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
  56. data/spec/grape/validations/presence_spec.rb +34 -34
  57. data/spec/grape/validations/regexp_spec.rb +11 -4
  58. data/spec/grape/validations/values_spec.rb +34 -20
  59. data/spec/grape/validations_spec.rb +300 -147
  60. data/spec/shared/versioning_examples.rb +18 -18
  61. data/spec/spec_helper.rb +2 -1
  62. metadata +81 -38
@@ -74,50 +74,50 @@ describe Grape::Validations::DefaultValidator do
74
74
 
75
75
  it 'set default value for optional param' do
76
76
  get("/")
77
- last_response.status.should == 200
78
- last_response.body.should == { id: nil, type: 'default-type' }.to_json
77
+ expect(last_response.status).to eq(200)
78
+ expect(last_response.body).to eq({ id: nil, type: 'default-type' }.to_json)
79
79
  end
80
80
 
81
81
  it 'set default values for optional params' do
82
82
  get("/user")
83
- last_response.status.should == 200
84
- last_response.body.should == { type1: 'default-type1', type2: 'default-type2' }.to_json
83
+ expect(last_response.status).to eq(200)
84
+ expect(last_response.body).to eq({ type1: 'default-type1', type2: 'default-type2' }.to_json)
85
85
  end
86
86
 
87
87
  it 'set default values for missing params in the request' do
88
88
  get("/user?type2=value2")
89
- last_response.status.should == 200
90
- last_response.body.should == { type1: 'default-type1', type2: 'value2' }.to_json
89
+ expect(last_response.status).to eq(200)
90
+ expect(last_response.body).to eq({ type1: 'default-type1', type2: 'value2' }.to_json)
91
91
  end
92
92
 
93
93
  it 'set default values for optional params and allow to use required fields in the same time' do
94
94
  get("/message?id=1")
95
- last_response.status.should == 200
96
- last_response.body.should == { id: '1', type1: 'default-type1', type2: 'default-type2' }.to_json
95
+ expect(last_response.status).to eq(200)
96
+ expect(last_response.body).to eq({ id: '1', type1: 'default-type1', type2: 'default-type2' }.to_json)
97
97
  end
98
98
 
99
99
  it 'sets lambda based defaults at the time of call' do
100
100
  get("/numbers")
101
- last_response.status.should == 200
101
+ expect(last_response.status).to eq(200)
102
102
  before = JSON.parse(last_response.body)
103
103
  get("/numbers")
104
- last_response.status.should == 200
104
+ expect(last_response.status).to eq(200)
105
105
  after = JSON.parse(last_response.body)
106
106
 
107
- before['non_random_number'].should == after['non_random_number']
108
- before['random_number'].should_not == after['random_number']
107
+ expect(before['non_random_number']).to eq(after['non_random_number'])
108
+ expect(before['random_number']).not_to eq(after['random_number'])
109
109
  end
110
110
 
111
111
  it 'set default values for optional grouped params' do
112
112
  get('/group')
113
- last_response.status.should == 200
114
- last_response.body.should == { foo_bar: 'foo-bar' }.to_json
113
+ expect(last_response.status).to eq(200)
114
+ expect(last_response.body).to eq({ foo_bar: 'foo-bar' }.to_json)
115
115
  end
116
116
 
117
117
  it 'sets default values for grouped arrays' do
118
118
  get('/array?array[][name]=name&array[][name]=name2&array[][with_default]=bar2')
119
- last_response.status.should == 200
120
- last_response.body.should == { array: [{ name: "name", with_default: "default" }, { name: "name2", with_default: "bar2" }] }.to_json
119
+ expect(last_response.status).to eq(200)
120
+ expect(last_response.body).to eq({ array: [{ name: "name", with_default: "default" }, { name: "name2", with_default: "bar2" }] }.to_json)
121
121
  end
122
122
 
123
123
  end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::ExactlyOneOfValidator do
4
+ describe '#validate!' do
5
+ let(:scope) do
6
+ Struct.new(:opts) do
7
+ def params(arg); end
8
+ end
9
+ end
10
+ let(:exactly_one_of_params) { [:beer, :wine, :grapefruit] }
11
+ let(:validator) { described_class.new(exactly_one_of_params, {}, false, scope.new) }
12
+
13
+ context 'when all restricted params are present' do
14
+ let(:params) { { beer: true, wine: true, grapefruit: true } }
15
+
16
+ it 'raises a validation exception' do
17
+ expect {
18
+ validator.validate! params
19
+ }.to raise_error(Grape::Exceptions::Validation)
20
+ end
21
+
22
+ context 'mixed with other params' do
23
+ let(:mixed_params) { params.merge!(other: true, andanother: true) }
24
+
25
+ it 'still raises a validation exception' do
26
+ expect {
27
+ validator.validate! mixed_params
28
+ }.to raise_error(Grape::Exceptions::Validation)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when a subset of restricted params are present' do
34
+ let(:params) { { beer: true, grapefruit: true } }
35
+
36
+ it 'raises a validation exception' do
37
+ expect {
38
+ validator.validate! params
39
+ }.to raise_error(Grape::Exceptions::Validation)
40
+ end
41
+ end
42
+
43
+ context 'when params keys come as strings' do
44
+ let(:params) { { 'beer' => true, 'grapefruit' => true } }
45
+
46
+ it 'raises a validation exception' do
47
+ expect {
48
+ validator.validate! params
49
+ }.to raise_error(Grape::Exceptions::Validation)
50
+ end
51
+ end
52
+
53
+ context 'when none of the restricted params is selected' do
54
+ let(:params) { { somethingelse: true } }
55
+
56
+ it 'raises a validation exception' do
57
+ expect {
58
+ validator.validate! params
59
+ }.to raise_error(Grape::Exceptions::Validation)
60
+ end
61
+ end
62
+
63
+ context 'when exactly one of the restricted params is selected' do
64
+ let(:params) { { beer: true, somethingelse: true } }
65
+
66
+ it 'params' do
67
+ expect(validator.validate!(params)).to eql params
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Grape::Validations::MutualExclusionValidator do
4
+ describe '#validate!' do
5
+ let(:scope) do
6
+ Struct.new(:opts) do
7
+ def params(arg); end
8
+ end
9
+ end
10
+ let(:mutually_exclusive_params) { [:beer, :wine, :grapefruit] }
11
+ let(:validator) { described_class.new(mutually_exclusive_params, {}, false, scope.new) }
12
+
13
+ context 'when all mutually exclusive params are present' do
14
+ let(:params) { { beer: true, wine: true, grapefruit: true } }
15
+
16
+ it 'raises a validation exception' do
17
+ expect {
18
+ validator.validate! params
19
+ }.to raise_error(Grape::Exceptions::Validation)
20
+ end
21
+
22
+ context 'mixed with other params' do
23
+ let(:mixed_params) { params.merge!(other: true, andanother: true) }
24
+
25
+ it 'still raises a validation exception' do
26
+ expect {
27
+ validator.validate! mixed_params
28
+ }.to raise_error(Grape::Exceptions::Validation)
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'when a subset of mutually exclusive params are present' do
34
+ let(:params) { { beer: true, grapefruit: true } }
35
+
36
+ it 'raises a validation exception' do
37
+ expect {
38
+ validator.validate! params
39
+ }.to raise_error(Grape::Exceptions::Validation)
40
+ end
41
+ end
42
+
43
+ context 'when params keys come as strings' do
44
+ let(:params) { { 'beer' => true, 'grapefruit' => true } }
45
+
46
+ it 'raises a validation exception' do
47
+ expect {
48
+ validator.validate! params
49
+ }.to raise_error(Grape::Exceptions::Validation)
50
+ end
51
+ end
52
+
53
+ context 'when no mutually exclusive params are present' do
54
+ let(:params) { { beer: true, somethingelse: true } }
55
+
56
+ it 'params' do
57
+ expect(validator.validate!(params)).to eql params
58
+ end
59
+ end
60
+ end
61
+ end
@@ -61,82 +61,82 @@ describe Grape::Validations::PresenceValidator do
61
61
 
62
62
  it 'does not validate for any params' do
63
63
  get "/bacons"
64
- last_response.status.should == 200
65
- last_response.body.should == "All the bacon".to_json
64
+ expect(last_response.status).to eq(200)
65
+ expect(last_response.body).to eq("All the bacon".to_json)
66
66
  end
67
67
 
68
68
  it 'validates id' do
69
69
  post '/'
70
- last_response.status.should == 400
71
- last_response.body.should == '{"error":"id is missing"}'
70
+ expect(last_response.status).to eq(400)
71
+ expect(last_response.body).to eq('{"error":"id is missing"}')
72
72
 
73
73
  io = StringIO.new('{"id" : "a56b"}')
74
74
  post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
75
- last_response.body.should == '{"error":"id is invalid"}'
76
- last_response.status.should == 400
75
+ expect(last_response.body).to eq('{"error":"id is invalid"}')
76
+ expect(last_response.status).to eq(400)
77
77
 
78
78
  io = StringIO.new('{"id" : 56}')
79
79
  post '/', {}, 'rack.input' => io, 'CONTENT_TYPE' => 'application/json', 'CONTENT_LENGTH' => io.length
80
- last_response.body.should == '{"ret":56}'
81
- last_response.status.should == 201
80
+ expect(last_response.body).to eq('{"ret":56}')
81
+ expect(last_response.status).to eq(201)
82
82
  end
83
83
 
84
84
  it 'validates name, company' do
85
85
  get '/'
86
- last_response.status.should == 400
87
- last_response.body.should == '{"error":"name is missing"}'
86
+ expect(last_response.status).to eq(400)
87
+ expect(last_response.body).to eq('{"error":"name is missing"}')
88
88
 
89
89
  get '/', name: "Bob"
90
- last_response.status.should == 400
91
- last_response.body.should == '{"error":"company is missing"}'
90
+ expect(last_response.status).to eq(400)
91
+ expect(last_response.body).to eq('{"error":"company is missing"}')
92
92
 
93
93
  get '/', name: "Bob", company: "TestCorp"
94
- last_response.status.should == 200
95
- last_response.body.should == "Hello".to_json
94
+ expect(last_response.status).to eq(200)
95
+ expect(last_response.body).to eq("Hello".to_json)
96
96
  end
97
97
 
98
98
  it 'validates nested parameters' do
99
99
  get '/nested'
100
- last_response.status.should == 400
101
- last_response.body.should == '{"error":"user is missing, user[first_name] is missing, user[last_name] is missing"}'
100
+ expect(last_response.status).to eq(400)
101
+ expect(last_response.body).to eq('{"error":"user is missing, user[first_name] is missing, user[last_name] is missing"}')
102
102
 
103
103
  get '/nested', user: { first_name: "Billy" }
104
- last_response.status.should == 400
105
- last_response.body.should == '{"error":"user[last_name] is missing"}'
104
+ expect(last_response.status).to eq(400)
105
+ expect(last_response.body).to eq('{"error":"user[last_name] is missing"}')
106
106
 
107
107
  get '/nested', user: { first_name: "Billy", last_name: "Bob" }
108
- last_response.status.should == 200
109
- last_response.body.should == "Nested".to_json
108
+ expect(last_response.status).to eq(200)
109
+ expect(last_response.body).to eq("Nested".to_json)
110
110
  end
111
111
 
112
112
  it 'validates triple nested parameters' do
113
113
  get '/nested_triple'
114
- last_response.status.should == 400
115
- last_response.body.should include '{"error":"admin is missing'
114
+ expect(last_response.status).to eq(400)
115
+ expect(last_response.body).to include '{"error":"admin is missing'
116
116
 
117
117
  get '/nested_triple', user: { first_name: "Billy" }
118
- last_response.status.should == 400
119
- last_response.body.should include '{"error":"admin is missing'
118
+ expect(last_response.status).to eq(400)
119
+ expect(last_response.body).to include '{"error":"admin is missing'
120
120
 
121
121
  get '/nested_triple', admin: { super: { first_name: "Billy" } }
122
- last_response.status.should == 400
123
- last_response.body.should == '{"error":"admin[admin_name] is missing, admin[super][user] is missing, admin[super][user][first_name] is missing, admin[super][user][last_name] is missing"}'
122
+ expect(last_response.status).to eq(400)
123
+ expect(last_response.body).to eq('{"error":"admin[admin_name] is missing, admin[super][user] is missing, admin[super][user][first_name] is missing, admin[super][user][last_name] is missing"}')
124
124
 
125
125
  get '/nested_triple', super: { user: { first_name: "Billy", last_name: "Bob" } }
126
- last_response.status.should == 400
127
- last_response.body.should include '{"error":"admin is missing'
126
+ expect(last_response.status).to eq(400)
127
+ expect(last_response.body).to include '{"error":"admin is missing'
128
128
 
129
129
  get '/nested_triple', admin: { super: { user: { first_name: "Billy" } } }
130
- last_response.status.should == 400
131
- last_response.body.should == '{"error":"admin[admin_name] is missing, admin[super][user][last_name] is missing"}'
130
+ expect(last_response.status).to eq(400)
131
+ expect(last_response.body).to eq('{"error":"admin[admin_name] is missing, admin[super][user][last_name] is missing"}')
132
132
 
133
133
  get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: "Billy" } } }
134
- last_response.status.should == 400
135
- last_response.body.should == '{"error":"admin[super][user][last_name] is missing"}'
134
+ expect(last_response.status).to eq(400)
135
+ expect(last_response.body).to eq('{"error":"admin[super][user][last_name] is missing"}')
136
136
 
137
137
  get '/nested_triple', admin: { admin_name: 'admin', super: { user: { first_name: "Billy", last_name: "Bob" } } }
138
- last_response.status.should == 200
139
- last_response.body.should == "Nested triple".to_json
138
+ expect(last_response.status).to eq(200)
139
+ expect(last_response.body).to eq("Nested triple".to_json)
140
140
  end
141
141
 
142
142
  end
@@ -20,14 +20,21 @@ describe Grape::Validations::RegexpValidator do
20
20
  ValidationsSpec::RegexpValidatorSpec::API
21
21
  end
22
22
 
23
- it 'refuses invalid input' do
24
- get '/', name: "invalid name"
25
- last_response.status.should == 400
23
+ context 'invalid input' do
24
+ it 'refuses inapppopriate' do
25
+ get '/', name: "invalid name"
26
+ expect(last_response.status).to eq(400)
27
+ end
28
+
29
+ it 'refuses nil' do
30
+ get '/', name: nil
31
+ expect(last_response.status).to eq(400)
32
+ end
26
33
  end
27
34
 
28
35
  it 'accepts valid input' do
29
36
  get '/', name: "bob"
30
- last_response.status.should == 200
37
+ expect(last_response.status).to eq(200)
31
38
  end
32
39
 
33
40
  end
@@ -49,6 +49,13 @@ describe Grape::Validations::ValuesValidator do
49
49
  get '/values/coercion' do
50
50
  { type: params[:type] }
51
51
  end
52
+
53
+ params do
54
+ optional :optional do
55
+ requires :type, values: ["a", "b"]
56
+ end
57
+ end
58
+ get '/optional_with_required_values'
52
59
  end
53
60
  end
54
61
  end
@@ -59,54 +66,61 @@ describe Grape::Validations::ValuesValidator do
59
66
 
60
67
  it 'allows a valid value for a parameter' do
61
68
  get("/", type: 'valid-type1')
62
- last_response.status.should eq 200
63
- last_response.body.should eq({ type: "valid-type1" }.to_json)
69
+ expect(last_response.status).to eq 200
70
+ expect(last_response.body).to eq({ type: "valid-type1" }.to_json)
64
71
  end
65
72
 
66
73
  it 'does not allow an invalid value for a parameter' do
67
74
  get("/", type: 'invalid-type')
68
- last_response.status.should eq 400
69
- last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
75
+ expect(last_response.status).to eq 400
76
+ expect(last_response.body).to eq({ error: "type does not have a valid value" }.to_json)
70
77
  end
71
78
 
72
- it 'does not allow nil value for a parameter' do
73
- get("/", type: nil)
74
- last_response.status.should eq 400
75
- last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
79
+ context 'nil value for a parameter' do
80
+ it 'does not allow for root params scope' do
81
+ get("/", type: nil)
82
+ expect(last_response.status).to eq 400
83
+ expect(last_response.body).to eq({ error: "type does not have a valid value" }.to_json)
84
+ end
85
+
86
+ it 'allows for a required param in child scope' do
87
+ get('/optional_with_required_values')
88
+ expect(last_response.status).to eq 200
89
+ end
76
90
  end
77
91
 
78
92
  it 'allows a valid default value' do
79
93
  get("/default/valid")
80
- last_response.status.should eq 200
81
- last_response.body.should eq({ type: "valid-type2" }.to_json)
94
+ expect(last_response.status).to eq 200
95
+ expect(last_response.body).to eq({ type: "valid-type2" }.to_json)
82
96
  end
83
97
 
84
98
  it 'allows a proc for values' do
85
99
  get('/lambda', type: 'valid-type1')
86
- last_response.status.should eq 200
87
- last_response.body.should eq({ type: "valid-type1" }.to_json)
100
+ expect(last_response.status).to eq 200
101
+ expect(last_response.body).to eq({ type: "valid-type1" }.to_json)
88
102
  end
89
103
 
90
104
  it 'does not validate updated values without proc' do
91
105
  ValuesModel.add_value('valid-type4')
92
106
 
93
107
  get('/', type: 'valid-type4')
94
- last_response.status.should eq 400
95
- last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
108
+ expect(last_response.status).to eq 400
109
+ expect(last_response.body).to eq({ error: "type does not have a valid value" }.to_json)
96
110
  end
97
111
 
98
112
  it 'validates against values in a proc' do
99
113
  ValuesModel.add_value('valid-type4')
100
114
 
101
115
  get('/lambda', type: 'valid-type4')
102
- last_response.status.should eq 200
103
- last_response.body.should eq({ type: "valid-type4" }.to_json)
116
+ expect(last_response.status).to eq 200
117
+ expect(last_response.body).to eq({ type: "valid-type4" }.to_json)
104
118
  end
105
119
 
106
120
  it 'does not allow an invalid value for a parameter using lambda' do
107
121
  get("/lambda", type: 'invalid-type')
108
- last_response.status.should eq 400
109
- last_response.body.should eq({ error: "type does not have a valid value" }.to_json)
122
+ expect(last_response.status).to eq 400
123
+ expect(last_response.body).to eq({ error: "type does not have a valid value" }.to_json)
110
124
  end
111
125
 
112
126
  it 'raises IncompatibleOptionValues on an invalid default value' do
@@ -125,8 +139,8 @@ describe Grape::Validations::ValuesValidator do
125
139
 
126
140
  it 'allows values to be a kind of the coerced type not just an instance of it' do
127
141
  get("/values/coercion", type: 10)
128
- last_response.status.should eq 200
129
- last_response.body.should eq({ type: 10 }.to_json)
142
+ expect(last_response.status).to eq 200
143
+ expect(last_response.body).to eq({ type: 10 }.to_json)
130
144
  end
131
145
 
132
146
  it 'raises IncompatibleOptionValues when values contains a value that is not a kind of the type' do