committee 2.0.1 → 2.1.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
- 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.
|