drillbit 2.11.0 → 3.0.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/drillbit.rb +1 -0
- data/lib/drillbit/accept_header.rb +1 -0
- data/lib/drillbit/authorizable_resource.rb +61 -60
- data/lib/drillbit/authorizers/parameters.rb +1 -0
- data/lib/drillbit/authorizers/parameters/filtering.rb +7 -6
- data/lib/drillbit/authorizers/parameters/inclusions.rb +6 -9
- data/lib/drillbit/authorizers/parameters/resource.rb +20 -19
- data/lib/drillbit/authorizers/query.rb +1 -0
- data/lib/drillbit/authorizers/scope.rb +5 -4
- data/lib/drillbit/compatibility/controllers.rb +1 -0
- data/lib/drillbit/configuration.rb +14 -16
- data/lib/drillbit/errors/invalid_api_request.rb +1 -0
- data/lib/drillbit/errors/invalid_request_body.rb +1 -0
- data/lib/drillbit/errors/invalid_subdomain.rb +1 -0
- data/lib/drillbit/errors/invalid_token.rb +1 -0
- data/lib/drillbit/errors/unpermitted_inclusions.rb +1 -0
- data/lib/drillbit/matchers/accept_header.rb +1 -0
- data/lib/drillbit/matchers/generic.rb +4 -3
- data/lib/drillbit/matchers/subdomain.rb +5 -6
- data/lib/drillbit/matchers/version.rb +3 -2
- data/lib/drillbit/middleware/api_request_validator.rb +4 -3
- data/lib/drillbit/middleware/parameter_parser.rb +1 -0
- data/lib/drillbit/middleware/token_processor.rb +1 -0
- data/lib/drillbit/parameters/filter.rb +12 -11
- data/lib/drillbit/parameters/index.rb +3 -2
- data/lib/drillbit/parameters/page.rb +1 -0
- data/lib/drillbit/parameters/sort.rb +1 -0
- data/lib/drillbit/requests/base.rb +1 -1
- data/lib/drillbit/requests/rack.rb +3 -0
- data/lib/drillbit/requests/rails.rb +1 -0
- data/lib/drillbit/resource.rb +1 -0
- data/lib/drillbit/resource/model.rb +5 -4
- data/lib/drillbit/resource/naming.rb +11 -10
- data/lib/drillbit/resource/processors/filtering.rb +1 -0
- data/lib/drillbit/resource/processors/indexing.rb +1 -0
- data/lib/drillbit/resource/processors/paging.rb +4 -3
- data/lib/drillbit/resource/processors/sorting.rb +1 -0
- data/lib/drillbit/responses/invalid_api_request.rb +3 -0
- data/lib/drillbit/responses/invalid_request_body.rb +3 -0
- data/lib/drillbit/responses/invalid_subdomain.rb +3 -0
- data/lib/drillbit/responses/invalid_token.rb +3 -0
- data/lib/drillbit/serializers/json_api.rb +12 -11
- data/lib/drillbit/tokens/base64.rb +1 -0
- data/lib/drillbit/tokens/base64s/invalid.rb +1 -0
- data/lib/drillbit/tokens/base64s/null.rb +1 -0
- data/lib/drillbit/tokens/invalid.rb +1 -0
- data/lib/drillbit/tokens/json_web_token.rb +6 -5
- data/lib/drillbit/tokens/json_web_tokens/invalid.rb +1 -0
- data/lib/drillbit/tokens/json_web_tokens/null.rb +1 -0
- data/lib/drillbit/tokens/json_web_tokens/password_reset.rb +1 -0
- data/lib/drillbit/tokens/null.rb +1 -0
- data/lib/drillbit/utilities/string.rb +1 -0
- data/lib/drillbit/version.rb +2 -1
- metadata +28 -94
- metadata.gz.sig +0 -0
- data/Rakefile +0 -2
- data/spec/drillbit/accept_header_spec.rb +0 -119
- data/spec/drillbit/authorizers/parameters/filtering_spec.rb +0 -101
- data/spec/drillbit/authorizers/parameters/resource_spec.rb +0 -12
- data/spec/drillbit/authorizers/parameters_spec.rb +0 -19
- data/spec/drillbit/authorizers/query_spec.rb +0 -24
- data/spec/drillbit/authorizers/scope_spec.rb +0 -21
- data/spec/drillbit/errors/invalid_api_request_spec.rb +0 -31
- data/spec/drillbit/errors/invalid_request_body_spec.rb +0 -25
- data/spec/drillbit/errors/invalid_subdomain_spec.rb +0 -30
- data/spec/drillbit/errors/invalid_token_spec.rb +0 -24
- data/spec/drillbit/invalid_subdomain_spec.rb +0 -45
- data/spec/drillbit/invalid_token_spec.rb +0 -44
- data/spec/drillbit/matchers/accept_header_spec.rb +0 -114
- data/spec/drillbit/matchers/subdomain_spec.rb +0 -78
- data/spec/drillbit/matchers/version_spec.rb +0 -86
- data/spec/drillbit/middleware/api_request_validator_spec.rb +0 -185
- data/spec/drillbit/middleware/parameter_parser_spec.rb +0 -200
- data/spec/drillbit/middleware/token_processor_spec.rb +0 -27
- data/spec/drillbit/requests/base_spec.rb +0 -37
- data/spec/drillbit/requests/rack_spec.rb +0 -252
- data/spec/drillbit/requests/rails_spec.rb +0 -264
- data/spec/drillbit/resource/model_spec.rb +0 -64
- data/spec/drillbit/resource/processors/filtering_spec.rb +0 -106
- data/spec/drillbit/resource/processors/indexing_spec.rb +0 -45
- data/spec/drillbit/resource/processors/paging_spec.rb +0 -74
- data/spec/drillbit/resource/processors/sorting_spec.rb +0 -66
- data/spec/drillbit/tokens/base64_spec.rb +0 -44
- data/spec/drillbit/tokens/json_web_token_spec.rb +0 -231
- data/spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb +0 -43
- data/spec/fixtures/test_rsa_key +0 -27
- data/spec/fixtures/test_rsa_key.pub +0 -9
- data/spec/spec_helper.rb +0 -4
- data/spec/support/private_keys.rb +0 -42
@@ -1,78 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'rspeckled/spec_helpers/rspeckled'
|
3
|
-
require 'drillbit/requests/base'
|
4
|
-
require 'drillbit/matchers/subdomain'
|
5
|
-
require 'drillbit/configuration'
|
6
|
-
|
7
|
-
module Drillbit
|
8
|
-
module Matchers
|
9
|
-
describe Subdomain do
|
10
|
-
before(:each) do
|
11
|
-
Drillbit.configuration.allowed_subdomains = %w{api}
|
12
|
-
Drillbit.configuration.allowed_api_subdomains = %w{api}
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'matches if the subdomain is API' do
|
16
|
-
env = { 'HTTP_HOST' => 'api.example.com' }
|
17
|
-
request = Requests::Base.resolve(env)
|
18
|
-
matcher = Subdomain.new
|
19
|
-
|
20
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'matches if the first subdomain is API' do
|
24
|
-
env = { 'HTTP_HOST' => 'api.westeros.example.com' }
|
25
|
-
request = Requests::Base.resolve(env)
|
26
|
-
matcher = Subdomain.new
|
27
|
-
|
28
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'does not match if the first subdomain is not API' do
|
32
|
-
env = { 'HTTP_HOST' => 'westeros.example.com' }
|
33
|
-
request = Requests::Base.resolve(env)
|
34
|
-
matcher = Subdomain.new
|
35
|
-
|
36
|
-
expect(matcher.matches?(request)).to be_a FalseClass
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'allows the matched subdomain to be specified' do
|
40
|
-
env = { 'HTTP_HOST' => 'westeros.example.com' }
|
41
|
-
request = Requests::Base.resolve(env)
|
42
|
-
matcher = Subdomain.new(allowed_subdomains: 'westeros')
|
43
|
-
|
44
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'allows more than one subdomain to be matched' do
|
48
|
-
env = { 'HTTP_HOST' => 'westeros.example.com' }
|
49
|
-
request = Requests::Base.resolve(env)
|
50
|
-
matcher = Subdomain.new(allowed_subdomains: %w{api westeros})
|
51
|
-
|
52
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
53
|
-
|
54
|
-
env = { 'HTTP_HOST' => 'api.example.com' }
|
55
|
-
request = Requests::Base.resolve(env)
|
56
|
-
matcher = Subdomain.new(allowed_subdomains: %w{api westeros})
|
57
|
-
|
58
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'can match only the api subdomain' do
|
62
|
-
env = { 'HTTP_HOST' => 'westeros.example.com' }
|
63
|
-
request = Requests::Base.resolve(env)
|
64
|
-
matcher = Subdomain.new(allowed_api_subdomains: %w{westeros})
|
65
|
-
|
66
|
-
expect(matcher.matches_api_subdomain?(request)).to be_a TrueClass
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'matches "api" as an api subdomain by default' do
|
70
|
-
env = { 'HTTP_HOST' => 'api.example.com' }
|
71
|
-
request = Requests::Base.resolve(env)
|
72
|
-
matcher = Subdomain.new
|
73
|
-
|
74
|
-
expect(matcher.matches_api_subdomain?(request)).to be_a TrueClass
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'rspeckled/spec_helpers/rspeckled'
|
3
|
-
require 'drillbit/requests/base'
|
4
|
-
require 'drillbit/matchers/version'
|
5
|
-
|
6
|
-
module Drillbit
|
7
|
-
module Matchers
|
8
|
-
describe Version do
|
9
|
-
context 'when the version is passed in the accept header' do
|
10
|
-
it 'does not match if the subdomain is API but the requested version does not ' \
|
11
|
-
'equal the version constraint' do
|
12
|
-
|
13
|
-
env = {
|
14
|
-
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
15
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=10.0',
|
16
|
-
}
|
17
|
-
request = Requests::Base.resolve(env)
|
18
|
-
|
19
|
-
matcher = Version.new(version_constraint: '10.1')
|
20
|
-
|
21
|
-
expect(matcher.matches?(request)).to be_a FalseClass
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'does match if the subdomain is API and the requested version equals the ' \
|
25
|
-
'version constraint' do
|
26
|
-
|
27
|
-
env = {
|
28
|
-
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
29
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=10.0',
|
30
|
-
}
|
31
|
-
request = Requests::Base.resolve(env)
|
32
|
-
|
33
|
-
matcher = Version.new(version_constraint: '10.0')
|
34
|
-
|
35
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
context 'when the version is not passed in the accept header' do
|
40
|
-
it 'does not match if the subdomain is API but the requested version does not ' \
|
41
|
-
'equal the version constraint' do
|
42
|
-
|
43
|
-
env = {
|
44
|
-
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
45
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep',
|
46
|
-
}
|
47
|
-
request = Requests::Base.resolve(env)
|
48
|
-
|
49
|
-
matcher = Version.new(version_constraint: '10.1',
|
50
|
-
default_version: '10.0')
|
51
|
-
|
52
|
-
expect(matcher.matches?(request)).to be_a FalseClass
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'does match if the subdomain is API and the requested version equals the ' \
|
56
|
-
'version constraint' do
|
57
|
-
|
58
|
-
env = {
|
59
|
-
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
60
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep',
|
61
|
-
}
|
62
|
-
request = Requests::Base.resolve(env)
|
63
|
-
|
64
|
-
matcher = Version.new(version_constraint: '10.0',
|
65
|
-
default_version: '10.0')
|
66
|
-
|
67
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'matches the default version in the configuration if none is passed in' do
|
72
|
-
Drillbit.configuration.default_api_version = '100.0'
|
73
|
-
|
74
|
-
env = {
|
75
|
-
'HTTP_X_APPLICATION_NAME' => 'westeros',
|
76
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep',
|
77
|
-
}
|
78
|
-
request = Requests::Base.resolve(env)
|
79
|
-
|
80
|
-
matcher = Version.new(version_constraint: '100.0')
|
81
|
-
|
82
|
-
expect(matcher.matches?(request)).to be_a TrueClass
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,185 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'rspeckled/spec_helpers/rspeckled'
|
3
|
-
require 'drillbit/middleware/api_request_validator'
|
4
|
-
|
5
|
-
# rubocop:disable Metrics/LineLength
|
6
|
-
module Drillbit
|
7
|
-
module Middleware
|
8
|
-
describe ApiRequestValidator, singletons: Erratum::Configuration do
|
9
|
-
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
10
|
-
|
11
|
-
before(:each) do
|
12
|
-
Erratum.configuration.url_mappings = {
|
13
|
-
'external_documentation_urls' => {
|
14
|
-
'errors.responses.invalid_subdomain' => 'http://example.com/foo',
|
15
|
-
},
|
16
|
-
'developer_documentation_urls' => {
|
17
|
-
'errors.responses.invalid_subdomain' => 'http://example.com/foo',
|
18
|
-
},
|
19
|
-
}
|
20
|
-
|
21
|
-
Drillbit.configure do |config|
|
22
|
-
config.allowed_subdomains = %w{api westeros}
|
23
|
-
config.allowed_api_subdomains = %w{api}
|
24
|
-
config.application_name = 'westeros'
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'allows requests for allowed subdomains without accept headers' do
|
29
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
30
|
-
|
31
|
-
request = {
|
32
|
-
'HTTP_HOST' => 'westeros.example.com',
|
33
|
-
'HTTP_ACCEPT' => '',
|
34
|
-
'QUERY_STRING' => '',
|
35
|
-
}
|
36
|
-
|
37
|
-
status, headers, response = api_request_middleware.call(request)
|
38
|
-
|
39
|
-
expect(status).to eql 200
|
40
|
-
expect(headers).to eql({})
|
41
|
-
expect(response).to eql 'response'
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'does not allow requests if they are not for an allowed subdomain' do
|
45
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
46
|
-
|
47
|
-
request = {
|
48
|
-
'HTTP_HOST' => 'notvalid.example.com',
|
49
|
-
'HTTP_ACCEPT' => '',
|
50
|
-
'QUERY_STRING' => 'first=my_param&accept=application/vnd.silent+redkeep;version=1.0.0',
|
51
|
-
}
|
52
|
-
|
53
|
-
status, headers, response = api_request_middleware.call(request)
|
54
|
-
|
55
|
-
expect(status).to eql 404
|
56
|
-
expect(headers).to eql({})
|
57
|
-
expect(JSON.load(response[0])).to include(
|
58
|
-
'errors' => [
|
59
|
-
{
|
60
|
-
'id' => match(/[a-z0-9\-]+/),
|
61
|
-
'links' => {
|
62
|
-
'about' => nil,
|
63
|
-
'documentation' => nil,
|
64
|
-
},
|
65
|
-
'status' => 404,
|
66
|
-
'code' => 'errors.invalid_subdomain',
|
67
|
-
'title' => 'Invalid Subdomain',
|
68
|
-
'detail' => 'The subdomain you attempted to access is not valid.' \
|
69
|
-
' Please try again.',
|
70
|
-
'source' => {
|
71
|
-
'http_host' => 'notvalid.example.com',
|
72
|
-
},
|
73
|
-
},
|
74
|
-
],
|
75
|
-
)
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'does not allow requests if they are for an allowed subdomain but does ' \
|
79
|
-
'not have a valid accept header' do
|
80
|
-
|
81
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
82
|
-
|
83
|
-
request = {
|
84
|
-
'HTTP_HOST' => 'api.example.com',
|
85
|
-
'HTTP_ACCEPT' => '',
|
86
|
-
'QUERY_STRING' => 'first=my_param&accept=application/vnd.silent+redkeep;version=1.0.0',
|
87
|
-
}
|
88
|
-
|
89
|
-
status, headers, response = api_request_middleware.call(request)
|
90
|
-
|
91
|
-
expect(status).to eql 400
|
92
|
-
expect(headers).to eql({})
|
93
|
-
expect(JSON.load(response[0])).to include(
|
94
|
-
'errors' => [
|
95
|
-
{
|
96
|
-
'id' => match(/[a-z0-9\-]+/),
|
97
|
-
'links' => {
|
98
|
-
'about' => nil,
|
99
|
-
'documentation' => nil,
|
100
|
-
},
|
101
|
-
'status' => 400,
|
102
|
-
'code' => 'errors.invalid_api_request',
|
103
|
-
'title' => 'Invalid API Request',
|
104
|
-
'detail' => 'The accept header that you passed in the request cannot be ' \
|
105
|
-
'parsed, please refer to the documentation to verify.',
|
106
|
-
'source' => {
|
107
|
-
'accept_header' => '',
|
108
|
-
},
|
109
|
-
},
|
110
|
-
],
|
111
|
-
)
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'does allow requests if both the subdomain and the accept header are valid' do
|
115
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
116
|
-
|
117
|
-
request = {
|
118
|
-
'HTTP_HOST' => 'api.example.com',
|
119
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
120
|
-
'QUERY_STRING' => 'first=my_param&accept=application/vnd.westeros+redkeep;version=1.0.0',
|
121
|
-
}
|
122
|
-
|
123
|
-
status, headers, response = api_request_middleware.call(request)
|
124
|
-
|
125
|
-
expect(status).to eql 200
|
126
|
-
expect(headers).to eql({})
|
127
|
-
expect(response).to eql 'response'
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'does allow requests if the subdomain, the accept header and the token are valid' do
|
131
|
-
Drillbit.configuration.token_private_key = test_private_key
|
132
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
133
|
-
|
134
|
-
request = {
|
135
|
-
'HTTP_HOST' => 'api.example.com',
|
136
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
137
|
-
'HTTP_AUTHORIZATION' => "Token #{valid_jwe_token}",
|
138
|
-
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
139
|
-
}
|
140
|
-
|
141
|
-
status, headers, response = api_request_middleware.call(request)
|
142
|
-
|
143
|
-
expect(status).to eql 200
|
144
|
-
expect(headers).to eql({})
|
145
|
-
expect(response).to eql 'response'
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'properly converts the content type for Rails when it is the only one' do
|
149
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
150
|
-
|
151
|
-
request = {
|
152
|
-
'CONTENT_TYPE' => 'application/vnd.api+json',
|
153
|
-
'HTTP_HOST' => 'westeros.example.com',
|
154
|
-
'HTTP_ACCEPT' => '',
|
155
|
-
'QUERY_STRING' => '',
|
156
|
-
}
|
157
|
-
|
158
|
-
allow(app).to receive(:call)
|
159
|
-
|
160
|
-
_response = api_request_middleware.call(request)
|
161
|
-
|
162
|
-
expect(app).to have_received(:call).
|
163
|
-
with(a_hash_including('CONTENT_TYPE' => 'application/json'))
|
164
|
-
end
|
165
|
-
|
166
|
-
it 'properly converts the content type for Rails when it is not the only one' do
|
167
|
-
api_request_middleware = ApiRequestValidator.new(app)
|
168
|
-
|
169
|
-
request = {
|
170
|
-
'CONTENT_TYPE' => 'application/vnd.api+json;other',
|
171
|
-
'HTTP_HOST' => 'westeros.example.com',
|
172
|
-
'HTTP_ACCEPT' => '',
|
173
|
-
'QUERY_STRING' => '',
|
174
|
-
}
|
175
|
-
|
176
|
-
allow(app).to receive(:call)
|
177
|
-
|
178
|
-
_response = api_request_middleware.call(request)
|
179
|
-
|
180
|
-
expect(app).to have_received(:call).
|
181
|
-
with(a_hash_including('CONTENT_TYPE' => 'application/json;other'))
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
@@ -1,200 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
require 'drillbit/middleware/parameter_parser'
|
4
|
-
|
5
|
-
module Drillbit
|
6
|
-
module Middleware
|
7
|
-
describe ParameterParser do
|
8
|
-
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
9
|
-
|
10
|
-
it 'converts JSON API compliant dasherized query params to underscored' do
|
11
|
-
app = ->(env) { [200, env, 'response'] }
|
12
|
-
middleware = ParameterParser.new(app)
|
13
|
-
|
14
|
-
request = {
|
15
|
-
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
16
|
-
'nice-to-meet=you-bob&' \
|
17
|
-
'hows-the-weather=today-bob',
|
18
|
-
}
|
19
|
-
|
20
|
-
_status, headers, _response = middleware.call(request)
|
21
|
-
|
22
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
23
|
-
'nice_to_meet=you-bob&' \
|
24
|
-
'hows_the_weather=today-bob'
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'does not touch query params with no dashes' do
|
28
|
-
app = ->(env) { [200, env, 'response'] }
|
29
|
-
middleware = ParameterParser.new(app)
|
30
|
-
|
31
|
-
request = {
|
32
|
-
'QUERY_STRING' => 'hello_there=bob-jones',
|
33
|
-
}
|
34
|
-
|
35
|
-
_status, headers, _response = middleware.call(request)
|
36
|
-
|
37
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones'
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'can handle weirdly formatted query string parameters' do
|
41
|
-
app = ->(env) { [200, env, 'response'] }
|
42
|
-
middleware = ParameterParser.new(app)
|
43
|
-
|
44
|
-
request = {
|
45
|
-
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
46
|
-
'nice-to-meet=you-bob&=',
|
47
|
-
}
|
48
|
-
|
49
|
-
_status, headers, _response = middleware.call(request)
|
50
|
-
|
51
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
52
|
-
'nice_to_meet=you-bob&='
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'can handle query string parameters with no values' do
|
56
|
-
app = ->(env) { [200, env, 'response'] }
|
57
|
-
middleware = ParameterParser.new(app)
|
58
|
-
|
59
|
-
request = {
|
60
|
-
'QUERY_STRING' => 'hello-there&nice-to-meet=you-bob&=',
|
61
|
-
}
|
62
|
-
|
63
|
-
_status, headers, _response = middleware.call(request)
|
64
|
-
|
65
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there&nice_to_meet=you-bob&='
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'can handle query string parameters with parameter name' do
|
69
|
-
app = ->(env) { [200, env, 'response'] }
|
70
|
-
middleware = ParameterParser.new(app)
|
71
|
-
|
72
|
-
request = {
|
73
|
-
'QUERY_STRING' => 'hello-there=bob-jones&=you-bob&nice-to-meet=you-bob&=',
|
74
|
-
}
|
75
|
-
|
76
|
-
_status, headers, _response = middleware.call(request)
|
77
|
-
|
78
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
79
|
-
'=you-bob&' \
|
80
|
-
'nice_to_meet=you-bob&='
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'converts JSON API compliant dasherized request params to underscored' do
|
84
|
-
app = ->(env) { [200, env, 'response'] }
|
85
|
-
middleware = ParameterParser.new(app)
|
86
|
-
|
87
|
-
request = {
|
88
|
-
'CONTENT_LENGTH' => '1',
|
89
|
-
'CONTENT_TYPE' => 'application/json',
|
90
|
-
'RACK_INPUT' => <<-HEREDOC
|
91
|
-
{
|
92
|
-
"single": "word",
|
93
|
-
"double-word": "double double",
|
94
|
-
"triple": {
|
95
|
-
"trippple-tripple": "mcdipple",
|
96
|
-
"quad": {
|
97
|
-
"another-level": "whoa inception"
|
98
|
-
}
|
99
|
-
},
|
100
|
-
"quint": [
|
101
|
-
{
|
102
|
-
"quint-it-quit": "mc-dipple"
|
103
|
-
},
|
104
|
-
{
|
105
|
-
"quint-it-quit": "big-maccle"
|
106
|
-
}
|
107
|
-
]
|
108
|
-
}
|
109
|
-
HEREDOC
|
110
|
-
}
|
111
|
-
|
112
|
-
_status, headers, _response = middleware.call(request)
|
113
|
-
|
114
|
-
expect(JSON.load(headers['RACK_INPUT'])).to eql(
|
115
|
-
'single' => 'word',
|
116
|
-
'double_word' => 'double double',
|
117
|
-
'triple' => {
|
118
|
-
'trippple_tripple' => 'mcdipple',
|
119
|
-
'quad' => {
|
120
|
-
'another_level' => 'whoa inception',
|
121
|
-
},
|
122
|
-
},
|
123
|
-
'quint' => [
|
124
|
-
{
|
125
|
-
'quint_it_quit' => 'mc-dipple',
|
126
|
-
},
|
127
|
-
{
|
128
|
-
'quint_it_quit' => 'big-maccle',
|
129
|
-
},
|
130
|
-
],
|
131
|
-
)
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'does not convert to JSON if the content type is not JSON' do
|
135
|
-
app = ->(env) { [200, env, 'response'] }
|
136
|
-
middleware = ParameterParser.new(app)
|
137
|
-
|
138
|
-
request = {
|
139
|
-
'CONTENT_LENGTH' => '1',
|
140
|
-
'CONTENT_TYPE' => 'not_j_s_o_n',
|
141
|
-
'RACK_INPUT' => 'This is not JSON',
|
142
|
-
}
|
143
|
-
|
144
|
-
_status, headers, _response = middleware.call(request)
|
145
|
-
|
146
|
-
expect(headers['RACK_INPUT']).to eql 'This is not JSON'
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'does not convert to JSON if there is no content' do
|
150
|
-
app = ->(env) { [200, env, 'response'] }
|
151
|
-
middleware = ParameterParser.new(app)
|
152
|
-
|
153
|
-
request = {
|
154
|
-
'CONTENT_LENGTH' => '0',
|
155
|
-
'CONTENT_TYPE' => 'application/json',
|
156
|
-
'RACK_INPUT' => 'empty',
|
157
|
-
}
|
158
|
-
|
159
|
-
_status, headers, _response = middleware.call(request)
|
160
|
-
|
161
|
-
expect(headers['RACK_INPUT']).to eql 'empty'
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'can handle incorrectly formatted JSON' do
|
165
|
-
app = ->(env) { [200, env, 'response'] }
|
166
|
-
middleware = ParameterParser.new(app)
|
167
|
-
|
168
|
-
request = {
|
169
|
-
'CONTENT_LENGTH' => '1',
|
170
|
-
'CONTENT_TYPE' => 'application/json',
|
171
|
-
'RACK_INPUT' => 'something blah',
|
172
|
-
}
|
173
|
-
|
174
|
-
status, headers, response = middleware.call(request)
|
175
|
-
|
176
|
-
expect(status).to eql 400
|
177
|
-
expect(headers).to eql({})
|
178
|
-
expect(JSON.load(response[0])).to include(
|
179
|
-
'errors' => [
|
180
|
-
{
|
181
|
-
'id' => match(/[a-z0-9\-]+/),
|
182
|
-
'links' => {
|
183
|
-
'about' => nil,
|
184
|
-
'documentation' => nil,
|
185
|
-
},
|
186
|
-
'status' => 400,
|
187
|
-
'code' => 'errors.invalid_request_body',
|
188
|
-
'title' => 'Invalid Request Body',
|
189
|
-
'detail' => 'The information you attempted to send in the ' \
|
190
|
-
'request cannot be parsed as a valid JSON document.',
|
191
|
-
'source' => {
|
192
|
-
'request_body' => 'something blah',
|
193
|
-
},
|
194
|
-
},
|
195
|
-
],
|
196
|
-
)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|