committee 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/committee/drivers/open_api_2.rb +54 -22
- data/lib/committee/middleware/base.rb +1 -0
- data/lib/committee/middleware/request_validation.rb +4 -3
- data/lib/committee/request_unpacker.rb +11 -2
- data/lib/committee/request_validator.rb +12 -3
- data/test/drivers/open_api_2_test.rb +23 -0
- data/test/middleware/request_validation_test.rb +2 -2
- data/test/parameter_coercer_test.rb +1 -1
- data/test/request_unpacker_test.rb +23 -13
- data/test/request_validator_test.rb +126 -81
- data/test/string_params_coercer_test.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b64e50881610fea8f1bb4ee2f0c5ec42f196d15
|
4
|
+
data.tar.gz: b1a449811bf1750b00f3dde8acd48520a42af196
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 577dcd6f0ff08f595af7c031d3f37cd96b9186858066716b27a9d6d150141309da0ccfab324b6c03fc38784a8456b0ddb523b1c37493b29d9d6baa343ee2ef53
|
7
|
+
data.tar.gz: 4193d95a73e0af8d5e66d51a05e2cd5e500874712c1bb498b7a9f89f829c9d4fac2ec69c9c27ed79e87d6ed3977e7966d3bcd924700559aec743c6c882a8ff28
|
@@ -81,19 +81,66 @@ module Committee::Drivers
|
|
81
81
|
# data.
|
82
82
|
attr_accessor :target_schema
|
83
83
|
|
84
|
+
attr_accessor :header_schema
|
85
|
+
|
84
86
|
def rel
|
85
87
|
raise "Committee: rel not implemented for OpenAPI"
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
89
|
-
|
90
|
-
# quite JSON schemas (but will be in OpenAPI 3) into synthetic schemas that
|
91
|
-
# we can use to do some basic request validation.
|
92
|
-
class ParameterSchemaBuilder
|
91
|
+
class SchemaBuilder
|
93
92
|
def initialize(link_data)
|
94
93
|
self.link_data = link_data
|
95
94
|
end
|
96
95
|
|
96
|
+
private
|
97
|
+
|
98
|
+
LINK_REQUIRED_FIELDS = [
|
99
|
+
:name
|
100
|
+
].map(&:to_s).freeze
|
101
|
+
|
102
|
+
attr_accessor :link_data
|
103
|
+
|
104
|
+
def check_required_fields!(param_data)
|
105
|
+
LINK_REQUIRED_FIELDS.each do |field|
|
106
|
+
if !param_data[field]
|
107
|
+
raise ArgumentError,
|
108
|
+
"Committee: no #{field} section in link data."
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class HeaderSchemaBuilder < SchemaBuilder
|
115
|
+
def call
|
116
|
+
if link_data["parameters"]
|
117
|
+
link_schema = JsonSchema::Schema.new
|
118
|
+
link_schema.properties = {}
|
119
|
+
link_schema.required = []
|
120
|
+
|
121
|
+
header_parameters = link_data["parameters"].select { |param_data| param_data["in"] == "header" }
|
122
|
+
header_parameters.each do |param_data|
|
123
|
+
check_required_fields!(param_data)
|
124
|
+
|
125
|
+
param_schema = JsonSchema::Schema.new
|
126
|
+
|
127
|
+
param_schema.type = [param_data["type"]]
|
128
|
+
|
129
|
+
link_schema.properties[param_data["name"]] = param_schema
|
130
|
+
if param_data["required"] == true
|
131
|
+
link_schema.required << param_data["name"]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
link_schema
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# ParameterSchemaBuilder converts OpenAPI 2 link parameters, which are not
|
141
|
+
# quite JSON schemas (but will be in OpenAPI 3) into synthetic schemas that
|
142
|
+
# we can use to do some basic request validation.
|
143
|
+
class ParameterSchemaBuilder < SchemaBuilder
|
97
144
|
# Returns a tuple of (schema, schema_data) where only one of the two
|
98
145
|
# values is present. This is either a full schema that's ready to go _or_
|
99
146
|
# a hash of unparsed schema data.
|
@@ -115,7 +162,8 @@ module Committee::Drivers
|
|
115
162
|
link_schema.properties = {}
|
116
163
|
link_schema.required = []
|
117
164
|
|
118
|
-
link_data["parameters"].
|
165
|
+
parameters = link_data["parameters"].reject { |param_data| param_data["in"] == "header" }
|
166
|
+
parameters.each do |param_data|
|
119
167
|
check_required_fields!(param_data)
|
120
168
|
|
121
169
|
param_schema = JsonSchema::Schema.new
|
@@ -143,23 +191,6 @@ module Committee::Drivers
|
|
143
191
|
end
|
144
192
|
end
|
145
193
|
end
|
146
|
-
|
147
|
-
private
|
148
|
-
|
149
|
-
LINK_REQUIRED_FIELDS = [
|
150
|
-
:name
|
151
|
-
].map(&:to_s).freeze
|
152
|
-
|
153
|
-
attr_accessor :link_data
|
154
|
-
|
155
|
-
def check_required_fields!(param_data)
|
156
|
-
LINK_REQUIRED_FIELDS.each do |field|
|
157
|
-
if !param_data[field]
|
158
|
-
raise ArgumentError,
|
159
|
-
"Committee: no #{field} section in link data."
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
194
|
end
|
164
195
|
|
165
196
|
class Schema < Committee::Drivers::Schema
|
@@ -266,6 +297,7 @@ module Committee::Drivers
|
|
266
297
|
# Convert the spec's parameter pseudo-schemas into JSON schemas that
|
267
298
|
# we can use for some basic request validation.
|
268
299
|
link.schema, schema_data = ParameterSchemaBuilder.new(link_data).call
|
300
|
+
link.header_schema = HeaderSchemaBuilder.new(link_data).call
|
269
301
|
|
270
302
|
# If data came back instead of a schema (this occurs when a route has
|
271
303
|
# a single `body` parameter instead of a collection of URL/query/form
|
@@ -5,6 +5,7 @@ module Committee::Middleware
|
|
5
5
|
|
6
6
|
@error_class = options.fetch(:error_class, Committee::ValidationError)
|
7
7
|
@params_key = options[:params_key] || "committee.params"
|
8
|
+
@headers_key = options[:headers_key] || "committee.headers"
|
8
9
|
@raise = options[:raise]
|
9
10
|
@schema = get_schema(options[:schema] ||
|
10
11
|
raise(ArgumentError, "Committee: need option `schema`"))
|
@@ -6,6 +6,7 @@ module Committee::Middleware
|
|
6
6
|
@allow_form_params = options.fetch(:allow_form_params, true)
|
7
7
|
@allow_query_params = options.fetch(:allow_query_params, true)
|
8
8
|
@check_content_type = options.fetch(:check_content_type, true)
|
9
|
+
@check_header = options.fetch(:check_header, true)
|
9
10
|
@optimistic_json = options.fetch(:optimistic_json, false)
|
10
11
|
@strict = options[:strict]
|
11
12
|
@coerce_date_times = options.fetch(:coerce_date_times, false)
|
@@ -39,7 +40,7 @@ module Committee::Middleware
|
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
42
|
-
request.env[@params_key] = Committee::RequestUnpacker.new(
|
43
|
+
request.env[@params_key], request.env[@headers_key] = Committee::RequestUnpacker.new(
|
43
44
|
request,
|
44
45
|
allow_form_params: @allow_form_params,
|
45
46
|
allow_query_params: @allow_query_params,
|
@@ -51,8 +52,8 @@ module Committee::Middleware
|
|
51
52
|
request.env[@params_key].merge!(param_matches) if param_matches
|
52
53
|
|
53
54
|
if link
|
54
|
-
validator = Committee::RequestValidator.new(link, check_content_type: @check_content_type)
|
55
|
-
validator.call(request, request.env[@params_key])
|
55
|
+
validator = Committee::RequestValidator.new(link, check_content_type: @check_content_type, check_header: @check_header)
|
56
|
+
validator.call(request, request.env[@params_key], request.env[@headers_key])
|
56
57
|
|
57
58
|
parameter_coerce!(request, link, @params_key)
|
58
59
|
parameter_coerce!(request, link, "rack.request.query_hash") if !request.GET.nil? && !link.schema.nil?
|
@@ -40,9 +40,9 @@ module Committee
|
|
40
40
|
end
|
41
41
|
|
42
42
|
if @allow_query_params
|
43
|
-
indifferent_params(@request.GET).merge(params)
|
43
|
+
[indifferent_params(@request.GET).merge(params), headers]
|
44
44
|
else
|
45
|
-
params
|
45
|
+
[params, headers]
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -87,5 +87,14 @@ module Committee
|
|
87
87
|
nil
|
88
88
|
end
|
89
89
|
end
|
90
|
+
|
91
|
+
def headers
|
92
|
+
env = @request.env
|
93
|
+
env.keys.grep(/HTTP_/).inject({}) do |headers, key|
|
94
|
+
headerized_key = key.gsub(/^HTTP_/, '').gsub(/_/, '-')
|
95
|
+
headers[headerized_key] = env[key]
|
96
|
+
headers
|
97
|
+
end
|
98
|
+
end
|
90
99
|
end
|
91
100
|
end
|
@@ -3,12 +3,21 @@ module Committee
|
|
3
3
|
def initialize(link, options = {})
|
4
4
|
@link = link
|
5
5
|
@check_content_type = options.fetch(:check_content_type, true)
|
6
|
+
@check_header = options.fetch(:check_header, true)
|
6
7
|
end
|
7
8
|
|
8
|
-
def call(request,
|
9
|
-
check_content_type!(request,
|
9
|
+
def call(request, params, headers)
|
10
|
+
check_content_type!(request, params) if @check_content_type
|
10
11
|
if @link.schema
|
11
|
-
valid, errors = @link.schema.validate(
|
12
|
+
valid, errors = @link.schema.validate(params)
|
13
|
+
if !valid
|
14
|
+
errors = JsonSchema::SchemaError.aggregate(errors).join("\n")
|
15
|
+
raise InvalidRequest, "Invalid request.\n\n#{errors}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if @check_header && @link.respond_to?(:header_schema) && @link.header_schema
|
20
|
+
valid, errors = @link.header_schema.validate(headers)
|
12
21
|
if !valid
|
13
22
|
errors = JsonSchema::SchemaError.aggregate(errors).join("\n")
|
14
23
|
raise InvalidRequest, "Invalid request.\n\n#{errors}"
|
@@ -298,3 +298,26 @@ describe Committee::Drivers::OpenAPI2::ParameterSchemaBuilder do
|
|
298
298
|
Committee::Drivers::OpenAPI2::ParameterSchemaBuilder.new(data).call
|
299
299
|
end
|
300
300
|
end
|
301
|
+
|
302
|
+
describe Committee::Drivers::OpenAPI2::HeaderSchemaBuilder do
|
303
|
+
it "returns schema data for header" do
|
304
|
+
data = {
|
305
|
+
"parameters" => [
|
306
|
+
{
|
307
|
+
"name" => "AUTH_TOKEN",
|
308
|
+
"type" => "string",
|
309
|
+
"in" => "header",
|
310
|
+
}
|
311
|
+
]
|
312
|
+
}
|
313
|
+
schema = call(data)
|
314
|
+
|
315
|
+
assert_equal ["string"], schema.properties["AUTH_TOKEN"].type
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
def call(data)
|
321
|
+
Committee::Drivers::OpenAPI2::HeaderSchemaBuilder.new(data).call
|
322
|
+
end
|
323
|
+
end
|
@@ -347,13 +347,13 @@ describe Committee::Middleware::RequestValidation do
|
|
347
347
|
}
|
348
348
|
|
349
349
|
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_2_schema)
|
350
|
-
get "/api/pets?limit=3"
|
350
|
+
get "/api/pets?limit=3", nil, { "HTTP_AUTH_TOKEN" => "xxx" }
|
351
351
|
assert_equal 200, last_response.status
|
352
352
|
end
|
353
353
|
|
354
354
|
it "detects an invalid request for OpenAPI" do
|
355
355
|
@app = new_rack_app(schema: open_api_2_schema)
|
356
|
-
get "/api/pets?limit=foo"
|
356
|
+
get "/api/pets?limit=foo", nil, { "HTTP_AUTH_TOKEN" => "xxx" }
|
357
357
|
assert_equal 400, last_response.status
|
358
358
|
assert_match /invalid request/i, last_response.body
|
359
359
|
end
|
@@ -7,7 +7,7 @@ describe Committee::ParameterCoercer do
|
|
7
7
|
@schema = JsonSchema.parse!(hyper_schema_data)
|
8
8
|
@schema.expand_references!
|
9
9
|
# POST /apps/:id
|
10
|
-
@link = @
|
10
|
+
@link = @schema.properties["app"].links[0]
|
11
11
|
end
|
12
12
|
|
13
13
|
it "pass datetime string" do
|
@@ -9,7 +9,7 @@ describe Committee::RequestUnpacker do
|
|
9
9
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
10
10
|
}
|
11
11
|
request = Rack::Request.new(env)
|
12
|
-
params = Committee::RequestUnpacker.new(request).call
|
12
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
13
13
|
assert_equal({ "x" => "y" }, params)
|
14
14
|
end
|
15
15
|
|
@@ -18,7 +18,7 @@ describe Committee::RequestUnpacker do
|
|
18
18
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
19
19
|
}
|
20
20
|
request = Rack::Request.new(env)
|
21
|
-
params = Committee::RequestUnpacker.new(request).call
|
21
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
22
22
|
assert_equal({ "x" => "y" }, params)
|
23
23
|
end
|
24
24
|
|
@@ -28,7 +28,7 @@ describe Committee::RequestUnpacker do
|
|
28
28
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
29
29
|
}
|
30
30
|
request = Rack::Request.new(env)
|
31
|
-
params = Committee::RequestUnpacker.new(request).call
|
31
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
32
32
|
assert_equal({}, params)
|
33
33
|
end
|
34
34
|
|
@@ -38,7 +38,7 @@ describe Committee::RequestUnpacker do
|
|
38
38
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
39
39
|
}
|
40
40
|
request = Rack::Request.new(env)
|
41
|
-
params = Committee::RequestUnpacker.new(request, optimistic_json: true).call
|
41
|
+
params, _ = Committee::RequestUnpacker.new(request, optimistic_json: true).call
|
42
42
|
assert_equal({ "x" => "y" }, params)
|
43
43
|
end
|
44
44
|
|
@@ -48,7 +48,7 @@ describe Committee::RequestUnpacker do
|
|
48
48
|
"rack.input" => StringIO.new('x=y&foo=42'),
|
49
49
|
}
|
50
50
|
request = Rack::Request.new(env)
|
51
|
-
params = Committee::RequestUnpacker.new(request, optimistic_json: true).call
|
51
|
+
params, _ = Committee::RequestUnpacker.new(request, optimistic_json: true).call
|
52
52
|
assert_equal({}, params)
|
53
53
|
end
|
54
54
|
|
@@ -58,7 +58,7 @@ describe Committee::RequestUnpacker do
|
|
58
58
|
"rack.input" => StringIO.new(""),
|
59
59
|
}
|
60
60
|
request = Rack::Request.new(env)
|
61
|
-
params = Committee::RequestUnpacker.new(request).call
|
61
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
62
62
|
assert_equal({}, params)
|
63
63
|
end
|
64
64
|
|
@@ -68,7 +68,7 @@ describe Committee::RequestUnpacker do
|
|
68
68
|
"rack.input" => StringIO.new("x=y"),
|
69
69
|
}
|
70
70
|
request = Rack::Request.new(env)
|
71
|
-
params = Committee::RequestUnpacker.new(request).call
|
71
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
72
72
|
assert_equal({}, params)
|
73
73
|
end
|
74
74
|
|
@@ -78,7 +78,7 @@ describe Committee::RequestUnpacker do
|
|
78
78
|
"rack.input" => StringIO.new("x=y"),
|
79
79
|
}
|
80
80
|
request = Rack::Request.new(env)
|
81
|
-
params = Committee::RequestUnpacker.new(request, allow_form_params: true).call
|
81
|
+
params, _ = Committee::RequestUnpacker.new(request, allow_form_params: true).call
|
82
82
|
assert_equal({ "x" => "y" }, params)
|
83
83
|
end
|
84
84
|
|
@@ -92,7 +92,7 @@ describe Committee::RequestUnpacker do
|
|
92
92
|
"rack.input" => StringIO.new("x=1"),
|
93
93
|
}
|
94
94
|
request = Rack::Request.new(env)
|
95
|
-
params = Committee::RequestUnpacker.new(
|
95
|
+
params, _ = Committee::RequestUnpacker.new(
|
96
96
|
request,
|
97
97
|
allow_form_params: true,
|
98
98
|
coerce_form_params: true,
|
@@ -108,7 +108,7 @@ describe Committee::RequestUnpacker do
|
|
108
108
|
"QUERY_STRING" => "a=b"
|
109
109
|
}
|
110
110
|
request = Rack::Request.new(env)
|
111
|
-
params = Committee::RequestUnpacker.new(request, allow_form_params: true, allow_query_params: true).call
|
111
|
+
params, _ = Committee::RequestUnpacker.new(request, allow_form_params: true, allow_query_params: true).call
|
112
112
|
assert_equal({ "x" => "y", "a" => "b" }, params)
|
113
113
|
end
|
114
114
|
|
@@ -118,7 +118,7 @@ describe Committee::RequestUnpacker do
|
|
118
118
|
"QUERY_STRING" => "a=b"
|
119
119
|
}
|
120
120
|
request = Rack::Request.new(env)
|
121
|
-
params = Committee::RequestUnpacker.new(request, allow_query_params: true).call
|
121
|
+
params, _ = Committee::RequestUnpacker.new(request, allow_query_params: true).call
|
122
122
|
assert_equal({ "a" => "b" }, params)
|
123
123
|
end
|
124
124
|
|
@@ -139,7 +139,7 @@ describe Committee::RequestUnpacker do
|
|
139
139
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
140
140
|
}
|
141
141
|
request = Rack::Request.new(env)
|
142
|
-
params = Committee::RequestUnpacker.new(request).call
|
142
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
143
143
|
assert_equal({}, params)
|
144
144
|
end
|
145
145
|
|
@@ -149,7 +149,17 @@ describe Committee::RequestUnpacker do
|
|
149
149
|
"rack.input" => StringIO.new('{"x":[]}'),
|
150
150
|
}
|
151
151
|
request = Rack::Request.new(env)
|
152
|
-
params = Committee::RequestUnpacker.new(request).call
|
152
|
+
params, _ = Committee::RequestUnpacker.new(request).call
|
153
153
|
assert_equal({ "x" => [] }, params)
|
154
154
|
end
|
155
|
+
|
156
|
+
it "unpacks http header" do
|
157
|
+
env = {
|
158
|
+
"HTTP_FOO_BAR" => "some header value",
|
159
|
+
"rack.input" => StringIO.new(""),
|
160
|
+
}
|
161
|
+
request = Rack::Request.new(env)
|
162
|
+
_, headers = Committee::RequestUnpacker.new(request, { allow_header_params: true }).call
|
163
|
+
assert_equal({ "FOO-BAR" => "some header value" }, headers)
|
164
|
+
end
|
155
165
|
end
|
@@ -3,102 +3,147 @@ require_relative "test_helper"
|
|
3
3
|
require "stringio"
|
4
4
|
|
5
5
|
describe Committee::RequestValidator do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
6
|
+
describe 'HyperSchema' do
|
7
|
+
before do
|
8
|
+
@schema = JsonSchema.parse!(hyper_schema_data)
|
9
|
+
@schema.expand_references!
|
10
|
+
# POST /apps/:id
|
11
|
+
@link = @schema.properties["app"].links[0]
|
12
|
+
@request = Rack::Request.new({
|
13
|
+
"CONTENT_TYPE" => "application/json",
|
14
|
+
"rack.input" => StringIO.new("{}"),
|
15
|
+
"REQUEST_METHOD" => "POST"
|
16
|
+
})
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
it "passes through a valid request with Content-Type options" do
|
20
|
+
@headers = { "Content-Type" => "application/json; charset=utf-8" }
|
21
|
+
call({})
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
it "passes through a valid request" do
|
25
|
+
data = {
|
26
|
+
"name" => "heroku-api",
|
27
|
+
}
|
28
|
+
call(data)
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
it "passes through a valid request with Content-Type options" do
|
32
|
+
@request =
|
33
|
+
Rack::Request.new({
|
34
|
+
"CONTENT_TYPE" => "application/json; charset=utf-8",
|
35
|
+
"rack.input" => StringIO.new("{}"),
|
36
|
+
})
|
37
|
+
data = {
|
38
|
+
"name" => "heroku-api",
|
39
|
+
}
|
40
|
+
call(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "detects an invalid request Content-Type" do
|
44
|
+
e = assert_raises(Committee::InvalidRequest) {
|
45
|
+
@request =
|
46
|
+
Rack::Request.new({
|
47
|
+
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
48
|
+
"rack.input" => StringIO.new("{}"),
|
49
|
+
})
|
50
|
+
call({})
|
51
|
+
}
|
52
|
+
message =
|
53
|
+
%{"Content-Type" request header must be set to "application/json".}
|
54
|
+
assert_equal message, e.message
|
55
|
+
end
|
41
56
|
|
42
|
-
|
43
|
-
e = assert_raises(Committee::InvalidRequest) {
|
57
|
+
it "allows skipping content_type check" do
|
44
58
|
@request =
|
45
59
|
Rack::Request.new({
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
call({})
|
50
|
-
|
51
|
-
message =
|
52
|
-
%{"Content-Type" request header must be set to "application/json".}
|
53
|
-
assert_equal message, e.message
|
54
|
-
end
|
60
|
+
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
61
|
+
"rack.input" => StringIO.new("{}"),
|
62
|
+
})
|
63
|
+
call({}, {}, check_content_type: false)
|
64
|
+
end
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
it "detects an missing parameter in GET requests" do
|
67
|
+
# GET /apps/search?query=...
|
68
|
+
@link = @schema.properties["app"].links[5]
|
69
|
+
@request = Rack::Request.new({})
|
70
|
+
e = assert_raises(Committee::InvalidRequest) do
|
71
|
+
call({})
|
72
|
+
end
|
73
|
+
message =
|
74
|
+
%{Invalid request.\n\n#: failed schema #/definitions/app/links/5/schema: "query" wasn't supplied.}
|
75
|
+
assert_equal message, e.message
|
76
|
+
end
|
64
77
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
78
|
+
it "allows an invalid Content-Type with an empty body" do
|
79
|
+
@request =
|
80
|
+
Rack::Request.new({
|
81
|
+
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
82
|
+
"rack.input" => StringIO.new(""),
|
83
|
+
})
|
70
84
|
call({})
|
71
85
|
end
|
72
|
-
message =
|
73
|
-
%{Invalid request.\n\n#: failed schema #/definitions/app/links/5/schema: "query" wasn't supplied.}
|
74
|
-
assert_equal message, e.message
|
75
|
-
end
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
87
|
+
it "detects a parameter of the wrong pattern" do
|
88
|
+
data = {
|
89
|
+
"name" => "%@!"
|
90
|
+
}
|
91
|
+
e = assert_raises(Committee::InvalidRequest) do
|
92
|
+
call(data)
|
93
|
+
end
|
94
|
+
message = %{Invalid request.\n\n#/name: failed schema #/definitions/app/links/0/schema/properties/name: %@! does not match /^[a-z][a-z0-9-]{3,30}$/.}
|
95
|
+
assert_equal message, e.message
|
96
|
+
end
|
85
97
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
call(data)
|
98
|
+
private
|
99
|
+
|
100
|
+
def call(data, headers={}, options={})
|
101
|
+
# hyper-schema link should be dropped into driver wrapper before it's used
|
102
|
+
link = Committee::Drivers::HyperSchema::Link.new(@link)
|
103
|
+
Committee::RequestValidator.new(link, options).call(@request, data, headers)
|
92
104
|
end
|
93
|
-
message = %{Invalid request.\n\n#/name: failed schema #/definitions/app/links/0/schema/properties/name: %@! does not match /^[a-z][a-z0-9-]{3,30}$/.}
|
94
|
-
assert_equal message, e.message
|
95
105
|
end
|
96
106
|
|
97
|
-
|
107
|
+
describe 'OpenAPI 2' do
|
108
|
+
before do
|
109
|
+
@schema = open_api_2_schema
|
110
|
+
@link = @schema.routes['GET'][0][1]
|
111
|
+
@request = Rack::Request.new({
|
112
|
+
"CONTENT_TYPE" => "application/json",
|
113
|
+
"rack.input" => StringIO.new("{}"),
|
114
|
+
"REQUEST_METHOD" => "POST"
|
115
|
+
})
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
it "passes through a valid request" do
|
120
|
+
headers = {
|
121
|
+
"AUTH-TOKEN" => "xxx"
|
122
|
+
}
|
123
|
+
call({}, headers)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "detects an missing header parameter in GET requests" do
|
127
|
+
@link = @schema.routes['GET'][0][1]
|
128
|
+
@request = Rack::Request.new({})
|
129
|
+
e = assert_raises(Committee::InvalidRequest) do
|
130
|
+
call({}, {})
|
131
|
+
end
|
132
|
+
message = %{Invalid request.\n\n#: failed schema : "AUTH-TOKEN" wasn't supplied.}
|
133
|
+
assert_equal message, e.message
|
134
|
+
end
|
98
135
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
136
|
+
it "allows skipping header schema check" do
|
137
|
+
@link = @schema.routes['GET'][0][1]
|
138
|
+
@request = Rack::Request.new({})
|
139
|
+
call({}, {}, { check_header: false })
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def call(data, headers={}, options={})
|
145
|
+
# hyper-schema link should be dropped into driver wrapper before it's used
|
146
|
+
Committee::RequestValidator.new(@link, options).call(@request, data, headers)
|
147
|
+
end
|
103
148
|
end
|
104
149
|
end
|
@@ -5,7 +5,7 @@ describe Committee::StringParamsCoercer do
|
|
5
5
|
@schema = JsonSchema.parse!(hyper_schema_data)
|
6
6
|
@schema.expand_references!
|
7
7
|
# GET /search/apps
|
8
|
-
@link = @
|
8
|
+
@link = @schema.properties["app"].links[5]
|
9
9
|
end
|
10
10
|
|
11
11
|
it "doesn't coerce params not in the schema" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: committee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandur
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-03-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json_schema
|
@@ -212,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
212
212
|
version: '0'
|
213
213
|
requirements: []
|
214
214
|
rubyforge_project:
|
215
|
-
rubygems_version: 2.
|
215
|
+
rubygems_version: 2.6.14
|
216
216
|
signing_key:
|
217
217
|
specification_version: 4
|
218
218
|
summary: A collection of Rack middleware to support JSON Schema.
|