committee 5.5.3 → 5.5.5
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/middleware/response_validation.rb +37 -11
- data/lib/committee/schema_validator/hyper_schema.rb +7 -4
- data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +2 -1
- data/lib/committee/schema_validator/open_api_3.rb +12 -10
- data/lib/committee/version.rb +1 -1
- data/test/middleware/response_validation_test.rb +68 -0
- data/test/test_helper.rb +8 -0
- metadata +3 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5ca2d98a65682acdd759d0fdbb8de493cec34b96889fec3cfd18bf4928b50cc
|
4
|
+
data.tar.gz: f2c50536c4bd4840dae6dc1a3da96c4a85dd2ec25ee69e55c944a90f3a514d00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3f35d67aab36148b9085b0a129ee315ea41e27ed6fde96c2038830c2046a3d1d5e212feeae849cd3d4571d69b01349911826711a699d326d6e799ca725a9391
|
7
|
+
data.tar.gz: 411bb7a8a21de88b9c978cb848480a2a40ebd9aab2e9819648d066d301db47420e9ccf9820c292e441c90b546d9c42ff42b0363c852e7db78e7eb902415adf6c
|
@@ -9,25 +9,38 @@ module Committee
|
|
9
9
|
super
|
10
10
|
@strict = options[:strict]
|
11
11
|
@validate_success_only = @schema.validator_option.validate_success_only
|
12
|
+
@streaming_content_parsers = options[:streaming_content_parsers] || {}
|
12
13
|
end
|
13
14
|
|
14
15
|
def handle(request)
|
15
16
|
status, headers, response = @app.call(request.env)
|
16
17
|
|
17
|
-
|
18
|
-
v = build_schema_validator(request)
|
19
|
-
v.response_validate(status, headers, response, @strict) if v.link_exist? && self.class.validate?(status, validate_success_only)
|
18
|
+
streaming_content_parser = retrieve_streaming_content_parser(headers)
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
if streaming_content_parser
|
21
|
+
response = Rack::BodyProxy.new(response) do
|
22
|
+
begin
|
23
|
+
validate(request, status, headers, response, streaming_content_parser)
|
24
|
+
rescue => e
|
25
|
+
handle_exception(e, request.env)
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
raise e if @raise
|
28
|
+
end
|
29
|
+
end
|
30
|
+
else
|
31
|
+
begin
|
32
|
+
validate(request, status, headers, response)
|
33
|
+
rescue Committee::InvalidResponse
|
34
|
+
handle_exception($!, request.env)
|
35
|
+
|
36
|
+
raise if @raise
|
37
|
+
return @error_class.new(500, :invalid_response, $!.message).render unless @ignore_error
|
38
|
+
rescue JSON::ParserError
|
39
|
+
handle_exception($!, request.env)
|
28
40
|
|
29
|
-
|
30
|
-
|
41
|
+
raise Committee::InvalidResponse if @raise
|
42
|
+
return @error_class.new(500, :invalid_response, "Response wasn't valid JSON.").render unless @ignore_error
|
43
|
+
end
|
31
44
|
end
|
32
45
|
|
33
46
|
[status, headers, response]
|
@@ -50,6 +63,19 @@ module Committee
|
|
50
63
|
|
51
64
|
private
|
52
65
|
|
66
|
+
def validate(request, status, headers, response, streaming_content_parser = nil)
|
67
|
+
v = build_schema_validator(request)
|
68
|
+
if v.link_exist? && self.class.validate?(status, validate_success_only)
|
69
|
+
v.response_validate(status, headers, response, @strict, streaming_content_parser)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def retrieve_streaming_content_parser(headers)
|
74
|
+
content_type_key = headers.keys.detect { |k| k.casecmp?('Content-Type') }
|
75
|
+
content_type = headers.fetch(content_type_key, nil)
|
76
|
+
@streaming_content_parsers[content_type]
|
77
|
+
end
|
78
|
+
|
53
79
|
def handle_exception(e, env)
|
54
80
|
@error_handler.call(e, env) if @error_handler
|
55
81
|
end
|
@@ -18,7 +18,7 @@ module Committee
|
|
18
18
|
parameter_coerce!(request, link, "rack.request.query_hash") if link_exist? && !request.GET.nil? && !link.schema.nil?
|
19
19
|
end
|
20
20
|
|
21
|
-
def response_validate(status, headers, response, _test_method = false)
|
21
|
+
def response_validate(status, headers, response, _test_method = false, custom_body_parser = nil)
|
22
22
|
return unless link_exist?
|
23
23
|
|
24
24
|
full_body = +""
|
@@ -26,8 +26,9 @@ module Committee
|
|
26
26
|
full_body << chunk
|
27
27
|
end
|
28
28
|
|
29
|
-
data =
|
30
|
-
|
29
|
+
data = if custom_body_parser
|
30
|
+
custom_body_parser.call(full_body)
|
31
|
+
elsif !full_body.empty?
|
31
32
|
parse_to_json = if validator_option.parse_response_by_content_type
|
32
33
|
content_type_key = headers.keys.detect { |k| k.casecmp?('Content-Type') }
|
33
34
|
headers.fetch(content_type_key, nil)&.start_with?('application/json')
|
@@ -35,7 +36,9 @@ module Committee
|
|
35
36
|
true
|
36
37
|
end
|
37
38
|
|
38
|
-
|
39
|
+
JSON.parse(full_body) if parse_to_json
|
40
|
+
else
|
41
|
+
{}
|
39
42
|
end
|
40
43
|
|
41
44
|
Committee::SchemaValidator::HyperSchema::ResponseValidator.new(link, validate_success_only: validator_option.validate_success_only, allow_blank_structures: validator_option.allow_blank_structures).call(status, headers, data)
|
@@ -110,7 +110,8 @@ module Committee
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def validate_post_request_params(path_params, query_params, body_params, headers, validator_option)
|
113
|
-
|
113
|
+
content_type_key = headers.keys.detect { |k| k.casecmp?('Content-Type') }
|
114
|
+
content_type = headers[content_type_key].to_s.split(';').first.to_s
|
114
115
|
|
115
116
|
# bad performance because when we coerce value, same check
|
116
117
|
validate_path_and_query_params(path_params, query_params, headers, validator_option)
|
@@ -20,7 +20,7 @@ module Committee
|
|
20
20
|
copy_coerced_data_to_params(request)
|
21
21
|
end
|
22
22
|
|
23
|
-
def response_validate(status, headers, response, test_method = false)
|
23
|
+
def response_validate(status, headers, response, test_method = false, custom_body_parser = nil)
|
24
24
|
full_body = +""
|
25
25
|
response.each do |chunk|
|
26
26
|
full_body << chunk
|
@@ -28,16 +28,18 @@ module Committee
|
|
28
28
|
|
29
29
|
parse_to_json = if validator_option.parse_response_by_content_type
|
30
30
|
content_type_key = headers.keys.detect { |k| k.casecmp?('Content-Type') }
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
data = if
|
31
|
+
headers.fetch(content_type_key, nil)&.start_with?('application/json')
|
32
|
+
else
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
data = if custom_body_parser
|
37
|
+
custom_body_parser.call(full_body)
|
38
|
+
elsif parse_to_json
|
37
39
|
full_body.empty? ? {} : JSON.parse(full_body)
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
else
|
41
|
+
full_body
|
42
|
+
end
|
41
43
|
|
42
44
|
# TODO: refactoring name
|
43
45
|
strict = test_method
|
data/lib/committee/version.rb
CHANGED
@@ -196,6 +196,74 @@ describe Committee::Middleware::ResponseValidation do
|
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
199
|
+
describe 'streaming response' do
|
200
|
+
describe "text/event-stream; e.g. server-sent events" do
|
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
|
+
}
|
206
|
+
status = 200
|
207
|
+
headers = { 'content-type' => 'text/event-stream' }
|
208
|
+
@app = Rack::Builder.new {
|
209
|
+
use Committee::Middleware::ResponseValidation, options
|
210
|
+
run lambda { |_|
|
211
|
+
[status, headers, ["hello"]]
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
get "/events/stream"
|
216
|
+
assert_equal 200, last_response.status
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe 'application/x-json-stream; customized streaming event' do
|
221
|
+
it "successfully validates the response as a special stream using a customized parser" do
|
222
|
+
error_handler_called = false
|
223
|
+
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
|
+
}
|
229
|
+
status = 200
|
230
|
+
headers = { 'content-type' => 'application/x-json-stream' }
|
231
|
+
@app = Rack::Builder.new {
|
232
|
+
use Committee::Middleware::ResponseValidation, options
|
233
|
+
run lambda { |_|
|
234
|
+
[status, headers, [JSON.dump({ "id" => 12345, "message" => "hello" })]]
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
get "/events/stream/json"
|
239
|
+
assert_equal 200, last_response.status
|
240
|
+
assert_equal false, error_handler_called
|
241
|
+
end
|
242
|
+
|
243
|
+
it "fails to validate the response as a special stream using a customized parser due to a schema mismatch" do
|
244
|
+
error_handler_called = false
|
245
|
+
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
|
+
}
|
251
|
+
status = 200
|
252
|
+
headers = { 'content-type' => 'application/x-json-stream' }
|
253
|
+
@app = Rack::Builder.new {
|
254
|
+
use Committee::Middleware::ResponseValidation, options
|
255
|
+
run lambda { |_|
|
256
|
+
[status, headers, [JSON.dump({ "message" => "hello" })]] # Missing 'id' field
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
get "/events/stream/json"
|
261
|
+
assert_equal 200, last_response.status
|
262
|
+
assert_equal true, error_handler_called
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
199
267
|
private
|
200
268
|
|
201
269
|
def new_rack_app(response, headers = {}, options = {})
|
data/test/test_helper.rb
CHANGED
@@ -61,6 +61,10 @@ def open_api_3_coverage_schema
|
|
61
61
|
@open_api_3_coverage_schema ||= Committee::Drivers.load_from_file(open_api_3_coverage_schema_path, parser_options: { strict_reference_validation: true })
|
62
62
|
end
|
63
63
|
|
64
|
+
def open_api_3_streaming_response_schema
|
65
|
+
@open_api_3_streaming_response_schema ||= Committee::Drivers.load_from_file(open_api_3_streaming_response_schema_path, parser_options: { strict_reference_validation: true })
|
66
|
+
end
|
67
|
+
|
64
68
|
# Don't cache this because we'll often manipulate the created hash in tests.
|
65
69
|
def hyper_schema_data
|
66
70
|
JSON.parse(File.read(hyper_schema_schema_path))
|
@@ -122,3 +126,7 @@ end
|
|
122
126
|
def open_api_3_invalid_reference_path
|
123
127
|
"./test/data/openapi3/invalid_reference.yaml"
|
124
128
|
end
|
129
|
+
|
130
|
+
def open_api_3_streaming_response_schema_path
|
131
|
+
"./test/data/openapi3/streaming_response.yaml"
|
132
|
+
end
|
metadata
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: committee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.5.
|
4
|
+
version: 5.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandur
|
8
8
|
- geemus (Wesley Beary)
|
9
9
|
- ota42y
|
10
|
-
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: json_schema
|
@@ -39,9 +38,6 @@ dependencies:
|
|
39
38
|
- - ">="
|
40
39
|
- !ruby/object:Gem::Version
|
41
40
|
version: '1.5'
|
42
|
-
- - "<"
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
version: '3.2'
|
45
41
|
type: :runtime
|
46
42
|
prerelease: false
|
47
43
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -49,9 +45,6 @@ dependencies:
|
|
49
45
|
- - ">="
|
50
46
|
- !ruby/object:Gem::Version
|
51
47
|
version: '1.5'
|
52
|
-
- - "<"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.2'
|
55
48
|
- !ruby/object:Gem::Dependency
|
56
49
|
name: openapi_parser
|
57
50
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,7 +157,6 @@ dependencies:
|
|
164
157
|
- - ">="
|
165
158
|
- !ruby/object:Gem::Version
|
166
159
|
version: '0'
|
167
|
-
description:
|
168
160
|
email:
|
169
161
|
- brandur@mutelight.org
|
170
162
|
- geemus+github@gmail.com
|
@@ -260,7 +252,6 @@ metadata:
|
|
260
252
|
changelog_uri: https://github.com/interagent/committee/blob/master/CHANGELOG.md
|
261
253
|
rubygems_mfa_required: 'true'
|
262
254
|
source_code_uri: https://github.com/interagent/committee
|
263
|
-
post_install_message:
|
264
255
|
rdoc_options: []
|
265
256
|
require_paths:
|
266
257
|
- lib
|
@@ -275,8 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
275
266
|
- !ruby/object:Gem::Version
|
276
267
|
version: '0'
|
277
268
|
requirements: []
|
278
|
-
rubygems_version: 3.
|
279
|
-
signing_key:
|
269
|
+
rubygems_version: 3.6.9
|
280
270
|
specification_version: 4
|
281
271
|
summary: A collection of Rack middleware to support JSON Schema.
|
282
272
|
test_files: []
|