committee 5.6.1 → 5.6.3
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_3/driver.rb +7 -1
- data/lib/committee/drivers.rb +2 -3
- data/lib/committee/errors.rb +11 -0
- data/lib/committee/middleware/base.rb +11 -5
- data/lib/committee/middleware/options/base.rb +107 -0
- data/lib/committee/middleware/options/request_validation.rb +80 -0
- data/lib/committee/middleware/options/response_validation.rb +46 -0
- data/lib/committee/middleware/options.rb +12 -0
- data/lib/committee/middleware/request_validation.rb +7 -1
- data/lib/committee/middleware/response_validation.rb +6 -2
- data/lib/committee/middleware.rb +1 -0
- data/lib/committee/schema_validator/hyper_schema/response_generator.rb +1 -3
- data/lib/committee/schema_validator/hyper_schema/response_validator.rb +14 -1
- data/lib/committee/schema_validator/hyper_schema/router.rb +1 -1
- data/lib/committee/schema_validator/hyper_schema.rb +3 -14
- data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +43 -13
- data/lib/committee/schema_validator/open_api_3/parameter_deserializer.rb +556 -0
- data/lib/committee/schema_validator/open_api_3/response_validator.rb +16 -2
- data/lib/committee/schema_validator/open_api_3.rb +19 -13
- data/lib/committee/schema_validator/option.rb +6 -17
- data/lib/committee/schema_validator.rb +12 -1
- data/lib/committee/test/except_parameter.rb +416 -0
- data/lib/committee/test/methods.rb +38 -2
- data/lib/committee/version.rb +1 -1
- data/lib/committee.rb +1 -1
- data/test/drivers/open_api_2/driver_test.rb +4 -16
- data/test/drivers/open_api_2/parameter_schema_builder_test.rb +4 -50
- data/test/drivers_test.rb +35 -21
- data/test/middleware/options/base_test.rb +120 -0
- data/test/middleware/options/request_validation_test.rb +177 -0
- data/test/middleware/options/response_validation_test.rb +121 -0
- data/test/middleware/request_validation_open_api_3_test.rb +200 -80
- data/test/middleware/request_validation_test.rb +13 -70
- data/test/middleware/response_validation_open_api_3_test.rb +40 -17
- data/test/middleware/response_validation_test.rb +3 -14
- data/test/request_unpacker_test.rb +2 -10
- data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +1 -37
- data/test/schema_validator/hyper_schema/request_validator_test.rb +6 -30
- data/test/schema_validator/hyper_schema/router_test.rb +5 -0
- data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +1 -37
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +58 -43
- data/test/schema_validator/open_api_3/parameter_deserializer_test.rb +457 -0
- data/test/schema_validator/open_api_3/request_validator_test.rb +1 -2
- data/test/schema_validator/open_api_3/response_validator_test.rb +3 -11
- data/test/schema_validator_test.rb +41 -0
- data/test/test/methods_test.rb +238 -105
- data/test/test/schema_coverage_test.rb +8 -155
- metadata +11 -1
|
@@ -84,42 +84,14 @@ describe Committee::Middleware::RequestValidation do
|
|
|
84
84
|
[200, {}, []]
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
@app = new_rack_app_with_lambda(check_parameter,
|
|
88
|
-
schema: open_api_3_schema,
|
|
89
|
-
coerce_date_times: true,
|
|
90
|
-
allow_get_body: true)
|
|
87
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, allow_get_body: true)
|
|
91
88
|
|
|
92
89
|
get "/string_params_coercer", { no_problem: true }, { input: params.to_json }
|
|
93
90
|
assert_equal 200, last_response.status
|
|
94
91
|
end
|
|
95
92
|
|
|
96
93
|
it "passes given a datetime and with coerce_date_times enabled on POST endpoint" do
|
|
97
|
-
params = {
|
|
98
|
-
"nested_array" => [
|
|
99
|
-
{
|
|
100
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
101
|
-
"nested_coercer_object" => {
|
|
102
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
103
|
-
},
|
|
104
|
-
"nested_no_coercer_object" => {
|
|
105
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
106
|
-
},
|
|
107
|
-
"nested_coercer_array" => [
|
|
108
|
-
{
|
|
109
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
110
|
-
}
|
|
111
|
-
],
|
|
112
|
-
"nested_no_coercer_array" => [
|
|
113
|
-
{
|
|
114
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
115
|
-
}
|
|
116
|
-
]
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
}
|
|
94
|
+
params = { "nested_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", }, "nested_no_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", }], "nested_no_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", }] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", }] }
|
|
123
95
|
|
|
124
96
|
check_parameter = lambda { |env|
|
|
125
97
|
nested_array = env['committee.params']["nested_array"]
|
|
@@ -166,11 +138,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
166
138
|
[200, {}, []]
|
|
167
139
|
}
|
|
168
140
|
|
|
169
|
-
@app = new_rack_app_with_lambda(check_parameter,
|
|
170
|
-
coerce_query_params: true,
|
|
171
|
-
coerce_recursive: true,
|
|
172
|
-
coerce_date_times: true,
|
|
173
|
-
schema: open_api_3_schema)
|
|
141
|
+
@app = new_rack_app_with_lambda(check_parameter, coerce_query_params: true, coerce_recursive: true, coerce_date_times: true, schema: open_api_3_schema)
|
|
174
142
|
|
|
175
143
|
get "/string_params_coercer", params
|
|
176
144
|
|
|
@@ -178,51 +146,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
178
146
|
end
|
|
179
147
|
|
|
180
148
|
it "passes given a nested datetime and with coerce_recursive=true and coerce_date_times=true on POST endpoint" do
|
|
181
|
-
params = {
|
|
182
|
-
"nested_array" => [
|
|
183
|
-
{
|
|
184
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
185
|
-
"per_page" => 1,
|
|
186
|
-
"nested_coercer_object" => {
|
|
187
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
188
|
-
"threshold" => 1.5
|
|
189
|
-
},
|
|
190
|
-
"nested_no_coercer_object" => {
|
|
191
|
-
"per_page" => 1,
|
|
192
|
-
"threshold" => 1.5
|
|
193
|
-
},
|
|
194
|
-
"nested_coercer_array" => [
|
|
195
|
-
{
|
|
196
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
197
|
-
"threshold" => 1.5
|
|
198
|
-
}
|
|
199
|
-
],
|
|
200
|
-
"nested_no_coercer_array" => [
|
|
201
|
-
{
|
|
202
|
-
"per_page" => 1,
|
|
203
|
-
"threshold" => 1.5
|
|
204
|
-
}
|
|
205
|
-
],
|
|
206
|
-
"integer_array" => [
|
|
207
|
-
1, 2, 3
|
|
208
|
-
],
|
|
209
|
-
"datetime_array" => [
|
|
210
|
-
"2016-04-01T16:00:00.000+09:00",
|
|
211
|
-
"2016-04-01T17:00:00.000+09:00",
|
|
212
|
-
"2016-04-01T18:00:00.000+09:00"
|
|
213
|
-
]
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
217
|
-
"per_page" => 1,
|
|
218
|
-
"threshold" => 1.5
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
"threshold" => 1.5,
|
|
222
|
-
"per_page" => 1
|
|
223
|
-
}
|
|
224
|
-
]
|
|
225
|
-
}
|
|
149
|
+
params = { "nested_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }, "nested_no_coercer_object" => { "per_page" => 1, "threshold" => 1.5 }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }], "nested_no_coercer_array" => [{ "per_page" => 1, "threshold" => 1.5 }], "integer_array" => [1, 2, 3], "datetime_array" => ["2016-04-01T16:00:00.000+09:00", "2016-04-01T17:00:00.000+09:00", "2016-04-01T18:00:00.000+09:00"] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "threshold" => 1.5 }, { "threshold" => 1.5, "per_page" => 1 }] }
|
|
226
150
|
|
|
227
151
|
check_parameter = lambda { |env|
|
|
228
152
|
hash = env['committee.params']
|
|
@@ -330,6 +254,14 @@ describe Committee::Middleware::RequestValidation do
|
|
|
330
254
|
assert_equal 200, last_response.status
|
|
331
255
|
end
|
|
332
256
|
|
|
257
|
+
it "ignores similar prefix paths outside the prefix in strict mode" do
|
|
258
|
+
@app = new_rack_app(prefix: "/v1", schema: open_api_3_schema, strict: true)
|
|
259
|
+
params = { "string_post_1" => 1 }
|
|
260
|
+
header "Content-Type", "application/json"
|
|
261
|
+
post "/v11/characters", JSON.generate(params)
|
|
262
|
+
assert_equal 200, last_response.status
|
|
263
|
+
end
|
|
264
|
+
|
|
333
265
|
it "don't check prefix with no option" do
|
|
334
266
|
@app = new_rack_app(schema: open_api_3_schema)
|
|
335
267
|
params = { "string_post_1" => 1 }
|
|
@@ -431,6 +363,26 @@ describe Committee::Middleware::RequestValidation do
|
|
|
431
363
|
get "/coerce_path_params/1"
|
|
432
364
|
end
|
|
433
365
|
|
|
366
|
+
it "coerces path params even when query param coercion is disabled" do
|
|
367
|
+
check_parameter_string = lambda { |env|
|
|
368
|
+
assert env['committee.path_hash']['integer'].is_a?(Integer)
|
|
369
|
+
assert env['committee.params']['integer'].is_a?(Integer)
|
|
370
|
+
[200, {}, []]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
@app = new_rack_app_with_lambda(check_parameter_string, schema: open_api_3_schema, coerce_path_params: true, coerce_query_params: false)
|
|
374
|
+
get "/coerce_path_params/1"
|
|
375
|
+
assert_equal 200, last_response.status
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it "does not coerce path params when path coercion is disabled even if query coercion is enabled" do
|
|
379
|
+
@app = new_rack_app(schema: open_api_3_schema, coerce_path_params: false, coerce_query_params: true)
|
|
380
|
+
get "/coerce_path_params/1"
|
|
381
|
+
|
|
382
|
+
assert_equal 400, last_response.status
|
|
383
|
+
assert_match(/integer/i, last_response.body)
|
|
384
|
+
end
|
|
385
|
+
|
|
434
386
|
describe "overwrite same parameter (old rule)" do
|
|
435
387
|
# (high priority) path_hash_key -> request_body_hash -> query_param
|
|
436
388
|
it "set query parameter to committee.params and query hash" do
|
|
@@ -627,6 +579,125 @@ describe Committee::Middleware::RequestValidation do
|
|
|
627
579
|
end
|
|
628
580
|
end
|
|
629
581
|
|
|
582
|
+
describe ':strict_query_params option' do
|
|
583
|
+
it 'allows unknown query params when strict_query_params is false' do
|
|
584
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: false)
|
|
585
|
+
get "/characters", { limit: 10, unknown_param: "value" }
|
|
586
|
+
assert_equal 200, last_response.status
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
it 'rejects unknown query params when strict_query_params is true' do
|
|
590
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
591
|
+
get "/characters", { limit: 10, unknown_param: "value" }
|
|
592
|
+
assert_equal 400, last_response.status
|
|
593
|
+
assert_match(/unknown_param/, last_response.body)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
it 'allows defined query params when strict_query_params is true' do
|
|
597
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
598
|
+
get "/characters", { limit: 10, school_name: "test" }
|
|
599
|
+
assert_equal 200, last_response.status
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
it 'handles empty query params when strict_query_params is true' do
|
|
603
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
604
|
+
get "/characters"
|
|
605
|
+
assert_equal 200, last_response.status
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it 'rejects multiple unknown query params' do
|
|
609
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
610
|
+
get "/characters", { limit: 10, unknown1: "a", unknown2: "b" }
|
|
611
|
+
assert_equal 400, last_response.status
|
|
612
|
+
assert_match(/unknown1/, last_response.body)
|
|
613
|
+
assert_match(/unknown2/, last_response.body)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
it 'allows partial query params (only some defined params)' do
|
|
617
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
618
|
+
get "/characters", { limit: 10 }
|
|
619
|
+
assert_equal 200, last_response.status
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
it 'works with POST requests that have query params' do
|
|
623
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
624
|
+
header "Content-Type", "application/json"
|
|
625
|
+
post "/additional_properties?first_name=test&unknown=value", JSON.generate(last_name: "test")
|
|
626
|
+
assert_equal 400, last_response.status
|
|
627
|
+
assert_match(/unknown/, last_response.body)
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
it 'allows defined query params in POST requests' do
|
|
631
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
632
|
+
header "Content-Type", "application/json"
|
|
633
|
+
post "/additional_properties?first_name=test", JSON.generate(last_name: "test")
|
|
634
|
+
assert_equal 200, last_response.status
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
it 'works with DELETE requests' do
|
|
638
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
639
|
+
delete "/characters?limit=10"
|
|
640
|
+
assert_equal 200, last_response.status
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
it 'rejects unknown query params in DELETE requests' do
|
|
644
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
645
|
+
delete "/characters?limit=10&unknown=value"
|
|
646
|
+
assert_equal 400, last_response.status
|
|
647
|
+
assert_match(/unknown/, last_response.body)
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
it 'works with HEAD requests' do
|
|
651
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
652
|
+
head "/characters?limit=10"
|
|
653
|
+
assert_equal 200, last_response.status
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it 'rejects unknown query params in HEAD requests' do
|
|
657
|
+
@app = new_rack_app(schema: open_api_3_schema, strict_query_params: true)
|
|
658
|
+
head "/characters?limit=10&unknown=value"
|
|
659
|
+
assert_equal 400, last_response.status
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
describe 'bracket-style query params' do
|
|
664
|
+
it 'validates query params declared with bracket notation names' do
|
|
665
|
+
check_parameter = lambda { |env|
|
|
666
|
+
assert_equal '/test', env['committee.query_hash']['filter[slug]']
|
|
667
|
+
refute env['committee.query_hash'].key?('filter')
|
|
668
|
+
[200, {}, []]
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: query_param_schema(bracket_notation_query_parameter))
|
|
672
|
+
|
|
673
|
+
get '/events?filter[slug]=%2Ftest'
|
|
674
|
+
|
|
675
|
+
assert_equal 200, last_response.status
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
it 'rejects unknown nested query params with strict_query_params' do
|
|
679
|
+
@app = new_rack_app(schema: query_param_schema(bracket_notation_query_parameter), strict_query_params: true)
|
|
680
|
+
|
|
681
|
+
get '/events?filter[slug]=%2Ftest&filter[status]=active'
|
|
682
|
+
|
|
683
|
+
assert_equal 400, last_response.status
|
|
684
|
+
assert_match(/filter\[status\]/, last_response.body)
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
it 'continues to support deepObject query params from Rack nested hashes' do
|
|
688
|
+
check_parameter = lambda { |env|
|
|
689
|
+
assert_equal '/test', env['committee.query_hash']['filter']['slug']
|
|
690
|
+
[200, {}, []]
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: query_param_schema(deep_object_query_parameter))
|
|
694
|
+
|
|
695
|
+
get '/events?filter[slug]=%2Ftest'
|
|
696
|
+
|
|
697
|
+
assert_equal 200, last_response.status
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
|
|
630
701
|
private
|
|
631
702
|
|
|
632
703
|
def new_rack_app(options = {})
|
|
@@ -641,4 +712,53 @@ describe Committee::Middleware::RequestValidation do
|
|
|
641
712
|
run check_lambda
|
|
642
713
|
}
|
|
643
714
|
end
|
|
715
|
+
|
|
716
|
+
def query_param_schema(parameter)
|
|
717
|
+
Committee::Drivers.load_from_data(query_param_document(parameter), nil, parser_options: { strict_reference_validation: true })
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def bracket_notation_query_parameter
|
|
721
|
+
{
|
|
722
|
+
'name' => 'filter[slug]',
|
|
723
|
+
'in' => 'query',
|
|
724
|
+
'required' => true,
|
|
725
|
+
'schema' => { 'type' => 'string' },
|
|
726
|
+
}
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
def deep_object_query_parameter
|
|
730
|
+
{
|
|
731
|
+
'name' => 'filter',
|
|
732
|
+
'in' => 'query',
|
|
733
|
+
'required' => true,
|
|
734
|
+
'style' => 'deepObject',
|
|
735
|
+
'explode' => true,
|
|
736
|
+
'schema' => {
|
|
737
|
+
'type' => 'object',
|
|
738
|
+
'required' => ['slug'],
|
|
739
|
+
'properties' => {
|
|
740
|
+
'slug' => { 'type' => 'string' },
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
}
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
def query_param_document(parameter)
|
|
747
|
+
{
|
|
748
|
+
'openapi' => '3.0.3',
|
|
749
|
+
'info' => { 'title' => 'test', 'version' => '1.0.0' },
|
|
750
|
+
'paths' => {
|
|
751
|
+
'/events' => {
|
|
752
|
+
'get' => {
|
|
753
|
+
'parameters' => [parameter],
|
|
754
|
+
'responses' => {
|
|
755
|
+
'200' => {
|
|
756
|
+
'description' => 'ok',
|
|
757
|
+
},
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
}
|
|
763
|
+
end
|
|
644
764
|
end
|
|
@@ -9,49 +9,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
9
9
|
@app
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
ARRAY_PROPERTY = [
|
|
13
|
-
{
|
|
14
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
15
|
-
"per_page" => 1,
|
|
16
|
-
"nested_coercer_object" => {
|
|
17
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
18
|
-
"threshold" => 1.5
|
|
19
|
-
},
|
|
20
|
-
"nested_no_coercer_object" => {
|
|
21
|
-
"per_page" => 1,
|
|
22
|
-
"threshold" => 1.5
|
|
23
|
-
},
|
|
24
|
-
"nested_coercer_array" => [
|
|
25
|
-
{
|
|
26
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
27
|
-
"threshold" => 1.5
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
"nested_no_coercer_array" => [
|
|
31
|
-
{
|
|
32
|
-
"per_page" => 1,
|
|
33
|
-
"threshold" => 1.5
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
"integer_array" => [
|
|
37
|
-
1, 2, 3
|
|
38
|
-
],
|
|
39
|
-
"datetime_array" => [
|
|
40
|
-
"2016-04-01T16:00:00.000+09:00",
|
|
41
|
-
"2016-04-01T17:00:00.000+09:00",
|
|
42
|
-
"2016-04-01T18:00:00.000+09:00"
|
|
43
|
-
]
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
47
|
-
"per_page" => 1,
|
|
48
|
-
"threshold" => 1.5
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"threshold" => 1.5,
|
|
52
|
-
"per_page" => 1
|
|
53
|
-
}
|
|
54
|
-
]
|
|
12
|
+
ARRAY_PROPERTY = [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }, "nested_no_coercer_object" => { "per_page" => 1, "threshold" => 1.5 }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }], "nested_no_coercer_array" => [{ "per_page" => 1, "threshold" => 1.5 }], "integer_array" => [1, 2, 3], "datetime_array" => ["2016-04-01T16:00:00.000+09:00", "2016-04-01T17:00:00.000+09:00", "2016-04-01T18:00:00.000+09:00"] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "threshold" => 1.5 }, { "threshold" => 1.5, "per_page" => 1 }]
|
|
55
13
|
|
|
56
14
|
it "passes through a valid request" do
|
|
57
15
|
@app = new_rack_app(schema: hyper_schema)
|
|
@@ -98,11 +56,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
98
56
|
|
|
99
57
|
it "passes a nested object with recursive option" do
|
|
100
58
|
key_name = "update_time"
|
|
101
|
-
params = {
|
|
102
|
-
key_name => "2016-04-01T16:00:00.000+09:00",
|
|
103
|
-
"query" => "cloudnasium",
|
|
104
|
-
"array_property" => ARRAY_PROPERTY
|
|
105
|
-
}
|
|
59
|
+
params = { key_name => "2016-04-01T16:00:00.000+09:00", "query" => "cloudnasium", "array_property" => ARRAY_PROPERTY }
|
|
106
60
|
|
|
107
61
|
check_parameter = lambda { |env|
|
|
108
62
|
hash = env['rack.request.query_hash']
|
|
@@ -116,11 +70,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
116
70
|
[200, {}, []]
|
|
117
71
|
}
|
|
118
72
|
|
|
119
|
-
@app = new_rack_app_with_lambda(check_parameter,
|
|
120
|
-
coerce_query_params: true,
|
|
121
|
-
coerce_recursive: true,
|
|
122
|
-
coerce_date_times: true,
|
|
123
|
-
schema: hyper_schema)
|
|
73
|
+
@app = new_rack_app_with_lambda(check_parameter, coerce_query_params: true, coerce_recursive: true, coerce_date_times: true, schema: hyper_schema)
|
|
124
74
|
|
|
125
75
|
get "/search/apps", params
|
|
126
76
|
assert_equal 200, last_response.status
|
|
@@ -128,11 +78,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
128
78
|
|
|
129
79
|
it "passes a nested object with coerce_recursive false" do
|
|
130
80
|
key_name = "update_time"
|
|
131
|
-
params = {
|
|
132
|
-
key_name => "2016-04-01T16:00:00.000+09:00",
|
|
133
|
-
"query" => "cloudnasium",
|
|
134
|
-
"array_property" => ARRAY_PROPERTY
|
|
135
|
-
}
|
|
81
|
+
params = { key_name => "2016-04-01T16:00:00.000+09:00", "query" => "cloudnasium", "array_property" => ARRAY_PROPERTY }
|
|
136
82
|
|
|
137
83
|
@app = new_rack_app(coerce_query_params: true, coerce_date_times: true, coerce_recursive: false, schema: hyper_schema)
|
|
138
84
|
|
|
@@ -142,11 +88,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
142
88
|
|
|
143
89
|
it "passes a nested object with coerce_recursive default(true)" do
|
|
144
90
|
key_name = "update_time"
|
|
145
|
-
params = {
|
|
146
|
-
key_name => "2016-04-01T16:00:00.000+09:00",
|
|
147
|
-
"query" => "cloudnasium",
|
|
148
|
-
"array_property" => ARRAY_PROPERTY
|
|
149
|
-
}
|
|
91
|
+
params = { key_name => "2016-04-01T16:00:00.000+09:00", "query" => "cloudnasium", "array_property" => ARRAY_PROPERTY }
|
|
150
92
|
|
|
151
93
|
@app = new_rack_app(coerce_query_params: true, coerce_date_times: true, schema: hyper_schema)
|
|
152
94
|
|
|
@@ -321,6 +263,13 @@ describe Committee::Middleware::RequestValidation do
|
|
|
321
263
|
assert_equal 200, last_response.status
|
|
322
264
|
end
|
|
323
265
|
|
|
266
|
+
it "ignores similar prefix paths outside the prefix in strict mode" do
|
|
267
|
+
@app = new_rack_app(prefix: "/v1", schema: hyper_schema, strict: true)
|
|
268
|
+
header "Content-Type", "application/json"
|
|
269
|
+
post "/v11/apps", JSON.generate({ "name" => 1 })
|
|
270
|
+
assert_equal 200, last_response.status
|
|
271
|
+
end
|
|
272
|
+
|
|
324
273
|
it "routes to paths not in schema" do
|
|
325
274
|
@app = new_rack_app(schema: hyper_schema)
|
|
326
275
|
get "/not-a-resource"
|
|
@@ -429,13 +378,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
429
378
|
end
|
|
430
379
|
|
|
431
380
|
it "not exist path and options" do
|
|
432
|
-
options = {
|
|
433
|
-
coerce_form_params: true,
|
|
434
|
-
coerce_date_times: true,
|
|
435
|
-
coerce_query_params: true,
|
|
436
|
-
coerce_path_params: true,
|
|
437
|
-
coerce_recursive: true
|
|
438
|
-
}
|
|
381
|
+
options = { coerce_form_params: true, coerce_date_times: true, coerce_query_params: true, coerce_path_params: true, coerce_recursive: true }
|
|
439
382
|
|
|
440
383
|
@app = new_rack_app({ schema: hyper_schema }.merge(options))
|
|
441
384
|
header "Content-Type", "application/x-www-form-urlencoded"
|
|
@@ -33,6 +33,13 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
33
33
|
assert_equal 200, last_response.status
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
it "passes through a valid response with a +json content-type" do
|
|
37
|
+
@app = new_response_rack(JSON.generate(CHARACTERS_RESPONSE), { "Content-Type" => "application/problem+json; charset=utf-8" }, schema: open_api_3_schema, parse_response_by_content_type: true,)
|
|
38
|
+
|
|
39
|
+
get "/characters"
|
|
40
|
+
assert_equal 200, last_response.status
|
|
41
|
+
end
|
|
42
|
+
|
|
36
43
|
it "passes through a invalid json" do
|
|
37
44
|
@app = new_response_rack("not_json", {}, schema: open_api_3_schema)
|
|
38
45
|
|
|
@@ -187,12 +194,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
187
194
|
|
|
188
195
|
describe 'validate error option' do
|
|
189
196
|
it "detects an invalid response status code" do
|
|
190
|
-
@app = new_response_rack({ integer: '1' }.to_json,
|
|
191
|
-
{},
|
|
192
|
-
app_status: 400,
|
|
193
|
-
schema: open_api_3_schema,
|
|
194
|
-
raise: true,
|
|
195
|
-
validate_success_only: false)
|
|
197
|
+
@app = new_response_rack({ integer: '1' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false)
|
|
196
198
|
|
|
197
199
|
e = assert_raises(Committee::InvalidResponse) do
|
|
198
200
|
get "/characters"
|
|
@@ -202,12 +204,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
202
204
|
end
|
|
203
205
|
|
|
204
206
|
it "detects an invalid response status code with validate_success_only=true" do
|
|
205
|
-
@app = new_response_rack({ string_1: :honoka }.to_json,
|
|
206
|
-
{},
|
|
207
|
-
app_status: 400,
|
|
208
|
-
schema: open_api_3_schema,
|
|
209
|
-
raise: true,
|
|
210
|
-
validate_success_only: true)
|
|
207
|
+
@app = new_response_rack({ string_1: :honoka }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: true)
|
|
211
208
|
|
|
212
209
|
get "/characters"
|
|
213
210
|
|
|
@@ -235,6 +232,36 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
235
232
|
end
|
|
236
233
|
end
|
|
237
234
|
|
|
235
|
+
describe 'response type validation' do
|
|
236
|
+
it "detects string value for number field when coerce_response_values is false" do
|
|
237
|
+
@app = new_response_rack({ integer: '726' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false, coerce_response_values: false)
|
|
238
|
+
|
|
239
|
+
e = assert_raises(Committee::InvalidResponse) do
|
|
240
|
+
get "/characters"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
assert_match(/expected integer, but received String/i, e.message)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
it "passes string value for number field when coerce_response_values is true" do
|
|
247
|
+
@app = new_response_rack({ integer: '726' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false, coerce_response_values: true)
|
|
248
|
+
|
|
249
|
+
get "/characters"
|
|
250
|
+
|
|
251
|
+
assert_equal 400, last_response.status
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it "detects string value for number field by default (coerce_response_values defaults to false)" do
|
|
255
|
+
@app = new_response_rack({ integer: '726' }.to_json, {}, app_status: 400, schema: open_api_3_schema, raise: true, validate_success_only: false)
|
|
256
|
+
|
|
257
|
+
e = assert_raises(Committee::InvalidResponse) do
|
|
258
|
+
get "/characters"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
assert_match(/expected integer, but received String/i, e.message)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
238
265
|
it 'does not suppress application error' do
|
|
239
266
|
@app = Rack::Builder.new {
|
|
240
267
|
use Committee::Middleware::ResponseValidation, { schema: open_api_3_schema, raise: true }
|
|
@@ -269,11 +296,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
269
296
|
end
|
|
270
297
|
|
|
271
298
|
it "strict and invalid content type with raise" do
|
|
272
|
-
@app = new_response_rack("abc",
|
|
273
|
-
{},
|
|
274
|
-
{ schema: open_api_3_schema, strict: true, raise: true },
|
|
275
|
-
{ content_type: 'application/text' }
|
|
276
|
-
)
|
|
299
|
+
@app = new_response_rack("abc", {}, { schema: open_api_3_schema, strict: true, raise: true }, { content_type: 'application/text' })
|
|
277
300
|
|
|
278
301
|
assert_raises(Committee::InvalidResponse) do
|
|
279
302
|
get "/characters"
|
|
@@ -199,10 +199,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
199
199
|
describe 'streaming response' do
|
|
200
200
|
describe "text/event-stream; e.g. server-sent events" do
|
|
201
201
|
it 'validates the response stream as a string' do
|
|
202
|
-
options = {
|
|
203
|
-
schema: open_api_3_streaming_response_schema,
|
|
204
|
-
streaming_content_parsers: { 'text/event-stream' => ->(body) { body } },
|
|
205
|
-
}
|
|
202
|
+
options = { schema: open_api_3_streaming_response_schema, streaming_content_parsers: { 'text/event-stream' => ->(body) { body } }, }
|
|
206
203
|
status = 200
|
|
207
204
|
headers = { 'content-type' => 'text/event-stream' }
|
|
208
205
|
@app = Rack::Builder.new {
|
|
@@ -221,11 +218,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
221
218
|
it "successfully validates the response as a special stream using a customized parser" do
|
|
222
219
|
error_handler_called = false
|
|
223
220
|
error_handler = ->(_e, _env) { error_handler_called = true }
|
|
224
|
-
options = {
|
|
225
|
-
schema: open_api_3_streaming_response_schema,
|
|
226
|
-
streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } },
|
|
227
|
-
error_handler: error_handler,
|
|
228
|
-
}
|
|
221
|
+
options = { schema: open_api_3_streaming_response_schema, streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } }, error_handler: error_handler, }
|
|
229
222
|
status = 200
|
|
230
223
|
headers = { 'content-type' => 'application/x-json-stream' }
|
|
231
224
|
@app = Rack::Builder.new {
|
|
@@ -243,11 +236,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
243
236
|
it "fails to validate the response as a special stream using a customized parser due to a schema mismatch" do
|
|
244
237
|
error_handler_called = false
|
|
245
238
|
error_handler = ->(_e, _env) { error_handler_called = true }
|
|
246
|
-
options = {
|
|
247
|
-
schema: open_api_3_streaming_response_schema,
|
|
248
|
-
streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } },
|
|
249
|
-
error_handler: error_handler,
|
|
250
|
-
}
|
|
239
|
+
options = { schema: open_api_3_streaming_response_schema, streaming_content_parsers: { 'application/x-json-stream' => ->(body) { JSON.parse!(body) } }, error_handler: error_handler, }
|
|
251
240
|
status = 200
|
|
252
241
|
headers = { 'content-type' => 'application/x-json-stream' }
|
|
253
242
|
@app = Rack::Builder.new {
|
|
@@ -137,22 +137,14 @@ describe Committee::RequestUnpacker do
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
it "includes request body when`use_get_body` is true" do
|
|
140
|
-
env = {
|
|
141
|
-
"rack.input" => StringIO.new('{"x":1, "y":2}'),
|
|
142
|
-
"REQUEST_METHOD" => "GET",
|
|
143
|
-
"QUERY_STRING" => "data=value&x=aaa",
|
|
144
|
-
}
|
|
140
|
+
env = { "rack.input" => StringIO.new('{"x":1, "y":2}'), "REQUEST_METHOD" => "GET", "QUERY_STRING" => "data=value&x=aaa", }
|
|
145
141
|
request = Rack::Request.new(env)
|
|
146
142
|
unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, allow_get_body: true })
|
|
147
143
|
assert_equal([{ 'x' => 1, 'y' => 2 }, false], unpacker.unpack_request_params(request))
|
|
148
144
|
end
|
|
149
145
|
|
|
150
146
|
it "doesn't include request body when `use_get_body` is false" do
|
|
151
|
-
env = {
|
|
152
|
-
"rack.input" => StringIO.new('{"x":1, "y":2}'),
|
|
153
|
-
"REQUEST_METHOD" => "GET",
|
|
154
|
-
"QUERY_STRING" => "data=value&x=aaa",
|
|
155
|
-
}
|
|
147
|
+
env = { "rack.input" => StringIO.new('{"x":1, "y":2}'), "REQUEST_METHOD" => "GET", "QUERY_STRING" => "data=value&x=aaa", }
|
|
156
148
|
request = Rack::Request.new(env)
|
|
157
149
|
unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, use_get_body: false })
|
|
158
150
|
assert_equal({ 'data' => 'value', 'x' => 'aaa' }, unpacker.unpack_query_params(request))
|
|
@@ -35,43 +35,7 @@ describe Committee::SchemaValidator::HyperSchema::ParameterCoercer do
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
it "pass array property" do
|
|
38
|
-
params = {
|
|
39
|
-
"array_property" => [
|
|
40
|
-
{
|
|
41
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
42
|
-
"per_page" => 1,
|
|
43
|
-
"nested_coercer_object" => {
|
|
44
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
45
|
-
"threshold" => 1.5
|
|
46
|
-
},
|
|
47
|
-
"nested_no_coercer_object" => {
|
|
48
|
-
"per_page" => 1,
|
|
49
|
-
"threshold" => 1.5
|
|
50
|
-
},
|
|
51
|
-
"nested_coercer_array" => [
|
|
52
|
-
{
|
|
53
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
54
|
-
"threshold" => 1.5
|
|
55
|
-
}
|
|
56
|
-
],
|
|
57
|
-
"nested_no_coercer_array" => [
|
|
58
|
-
{
|
|
59
|
-
"per_page" => 1,
|
|
60
|
-
"threshold" => 1.5
|
|
61
|
-
}
|
|
62
|
-
]
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
"update_time" => "2016-04-01T16:00:00.000+09:00",
|
|
66
|
-
"per_page" => 1,
|
|
67
|
-
"threshold" => 1.5
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"threshold" => 1.5,
|
|
71
|
-
"per_page" => 1
|
|
72
|
-
}
|
|
73
|
-
],
|
|
74
|
-
}
|
|
38
|
+
params = { "array_property" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "nested_coercer_object" => { "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }, "nested_no_coercer_object" => { "per_page" => 1, "threshold" => 1.5 }, "nested_coercer_array" => [{ "update_time" => "2016-04-01T16:00:00.000+09:00", "threshold" => 1.5 }], "nested_no_coercer_array" => [{ "per_page" => 1, "threshold" => 1.5 }] }, { "update_time" => "2016-04-01T16:00:00.000+09:00", "per_page" => 1, "threshold" => 1.5 }, { "threshold" => 1.5, "per_page" => 1 }], }
|
|
75
39
|
call(params, coerce_date_times: true, coerce_recursive: true)
|
|
76
40
|
|
|
77
41
|
first_data = params["array_property"][0]
|