committee 5.5.0 → 5.5.2
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/bin/committee_stub.rb +1 -6
- data/lib/committee/drivers/open_api_2/driver.rb +21 -33
- data/lib/committee/drivers/open_api_2/link.rb +8 -1
- data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +1 -2
- data/lib/committee/drivers/open_api_2/schema_builder.rb +2 -5
- data/lib/committee/drivers.rb +1 -1
- data/lib/committee/errors.rb +2 -2
- data/lib/committee/middleware/base.rb +2 -2
- data/lib/committee/middleware/request_validation.rb +1 -1
- data/lib/committee/middleware/stub.rb +1 -1
- data/lib/committee/request_unpacker.rb +17 -9
- data/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb +41 -41
- data/lib/committee/schema_validator/hyper_schema/request_validator.rb +1 -2
- data/lib/committee/schema_validator/hyper_schema/response_validator.rb +15 -7
- data/lib/committee/schema_validator/hyper_schema/string_params_coercer.rb +60 -60
- data/lib/committee/schema_validator/hyper_schema.rb +65 -59
- data/lib/committee/schema_validator/open_api_3/request_validator.rb +1 -1
- data/lib/committee/schema_validator/open_api_3.rb +11 -5
- data/lib/committee/schema_validator/option.rb +3 -6
- data/lib/committee/test/schema_coverage.rb +1 -7
- data/lib/committee/utils.rb +20 -20
- data/lib/committee/version.rb +1 -1
- data/test/bin/committee_stub_test.rb +1 -5
- data/test/committee_test.rb +0 -1
- data/test/drivers/hyper_schema/driver_test.rb +0 -1
- data/test/drivers/open_api_2/driver_test.rb +1 -3
- data/test/drivers/open_api_2/header_schema_builder_test.rb +1 -9
- data/test/drivers/open_api_2/link_test.rb +1 -2
- data/test/drivers/open_api_2/parameter_schema_builder_test.rb +6 -45
- data/test/drivers/open_api_3/driver_test.rb +5 -5
- data/test/drivers_test.rb +19 -28
- data/test/middleware/base_test.rb +13 -36
- data/test/middleware/request_validation_open_api_3_test.rb +24 -47
- data/test/middleware/request_validation_test.rb +19 -53
- data/test/middleware/response_validation_open_api_3_test.rb +33 -25
- data/test/middleware/response_validation_test.rb +24 -15
- data/test/middleware/stub_test.rb +5 -12
- data/test/request_unpacker_test.rb +26 -66
- data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +3 -3
- data/test/schema_validator/hyper_schema/request_validator_test.rb +7 -17
- data/test/schema_validator/hyper_schema/response_generator_test.rb +24 -18
- data/test/schema_validator/hyper_schema/response_validator_test.rb +3 -7
- data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +2 -2
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +16 -17
- data/test/schema_validator/open_api_3/request_validator_test.rb +8 -19
- data/test/schema_validator/open_api_3/response_validator_test.rb +2 -4
- data/test/test/methods_new_version_test.rb +2 -2
- data/test/test/methods_test.rb +5 -5
- data/test/test/schema_coverage_test.rb +4 -17
- data/test/test_helper.rb +6 -13
- data/test/validation_error_test.rb +1 -5
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c6653c3224887d3adf7300b4c4e2f047a839ab31ba1890b87b8daad828e939d
|
4
|
+
data.tar.gz: 7c00b24dff0a892dfe82b28304547e8ac47729f1ac87fcdf81f47575a7757bad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 876a44d4c13020892a3e6b10ee1008fc0b5ee0d7e294acbca8922c5ec4cf7c78fe6bee6374388bc9468a4511b11eb68cf6e3c49bf360e77f158942953e113bcb
|
7
|
+
data.tar.gz: 17f3777d9d610fe64f86670c7b849a8dae18346ae5d59639b3bdd5cff2958649b1a6da78c5abe4d60e01dfd5a506fc2420f1c024de40d5f982bd2f353bf34d01
|
@@ -30,12 +30,7 @@ module Committee
|
|
30
30
|
|
31
31
|
# Gets an option parser for command line arguments.
|
32
32
|
def get_options_parser
|
33
|
-
options = {
|
34
|
-
driver: nil,
|
35
|
-
help: false,
|
36
|
-
port: 9292,
|
37
|
-
tolerant: false,
|
38
|
-
}
|
33
|
+
options = { driver: nil, help: false, port: 9292, tolerant: false, }
|
39
34
|
|
40
35
|
parser = OptionParser.new do |opts|
|
41
36
|
opts.banner = "Usage: rackup [options] [JSON Schema file]"
|
@@ -81,28 +81,21 @@ module Committee
|
|
81
81
|
|
82
82
|
# These are fields that the OpenAPI 2 spec considers mandatory to be
|
83
83
|
# included in the document's top level.
|
84
|
-
REQUIRED_FIELDS = [
|
85
|
-
:consumes,
|
86
|
-
:definitions,
|
87
|
-
:paths,
|
88
|
-
:produces,
|
89
|
-
:swagger,
|
90
|
-
].map(&:to_s).freeze
|
84
|
+
REQUIRED_FIELDS = [:consumes, :definitions, :paths, :produces, :swagger,].map(&:to_s).freeze
|
91
85
|
|
92
86
|
def find_best_fit_response(link_data)
|
93
87
|
if response_data = link_data["responses"]["200"] || response_data = link_data["responses"][200]
|
94
|
-
|
88
|
+
200
|
95
89
|
elsif response_data = link_data["responses"]["201"] || response_data = link_data["responses"][201]
|
96
|
-
|
90
|
+
201
|
97
91
|
else
|
98
92
|
# Sort responses so that we can try to prefer any 3-digit status code.
|
99
93
|
# If there are none, we'll just take anything from the list.
|
100
|
-
ordered_responses = link_data["responses"].
|
101
|
-
select { |k, v| k.to_s =~ /[0-9]{3}/ }
|
94
|
+
ordered_responses = link_data["responses"].select { |k, v| k.to_s =~ /[0-9]{3}/ }
|
102
95
|
if first = ordered_responses.first
|
103
|
-
|
96
|
+
first[0].to_i
|
104
97
|
else
|
105
|
-
|
98
|
+
nil
|
106
99
|
end
|
107
100
|
end
|
108
101
|
end
|
@@ -117,9 +110,7 @@ module Committee
|
|
117
110
|
# that all references to it will still have correct paths (i.e. we can
|
118
111
|
# still find a resource at '#/definitions/resource' instead of
|
119
112
|
# '#/resource').
|
120
|
-
schema = JsonSchema.parse!({
|
121
|
-
"definitions" => data['definitions'],
|
122
|
-
})
|
113
|
+
schema = JsonSchema.parse!({ "definitions" => data['definitions'], })
|
123
114
|
schema.expand_references!
|
124
115
|
schema.uri = DEFINITIONS_PSEUDO_URI
|
125
116
|
|
@@ -174,19 +165,16 @@ module Committee
|
|
174
165
|
schemas_data["properties"][href]["properties"][method] = schema_data
|
175
166
|
end
|
176
167
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
# A link need not necessarily specify a target schema.
|
184
|
-
if response_data["schema"]
|
185
|
-
target_schemas_data["properties"][href]["properties"][method] =
|
186
|
-
response_data["schema"]
|
187
|
-
end
|
168
|
+
target_schemas_data["properties"][href]["properties"][method] ||= { "properties" => {} }
|
169
|
+
link_data["responses"].each do |key, response_data|
|
170
|
+
status = key.to_i
|
171
|
+
next unless response_data["schema"]
|
172
|
+
|
173
|
+
target_schemas_data["properties"][href]["properties"][method]["properties"][status] = response_data["schema"]
|
188
174
|
end
|
189
175
|
|
176
|
+
link.status_success = find_best_fit_response(link_data)
|
177
|
+
|
190
178
|
rx = %r{^#{href_to_regex(link.href)}$}
|
191
179
|
Committee.log_debug "Created route: #{link.method} #{link.href} (regex #{rx})"
|
192
180
|
|
@@ -199,10 +187,8 @@ module Committee
|
|
199
187
|
# #parse_definitions!, but what we're doing here is prefixing references
|
200
188
|
# with a specialized internal URI so that they can reference definitions
|
201
189
|
# from another document in the store.
|
202
|
-
schemas =
|
203
|
-
|
204
|
-
target_schemas =
|
205
|
-
rewrite_references_and_parse(target_schemas_data, store)
|
190
|
+
schemas = rewrite_references_and_parse(schemas_data, store)
|
191
|
+
target_schemas = rewrite_references_and_parse(target_schemas_data, store)
|
206
192
|
|
207
193
|
# As noted above, now that we've parsed our aggregate response schema, go
|
208
194
|
# back through each link and them their response schema.
|
@@ -218,8 +204,10 @@ module Committee
|
|
218
204
|
end
|
219
205
|
|
220
206
|
# response
|
221
|
-
link.
|
222
|
-
|
207
|
+
link.target_schemas = {}
|
208
|
+
target_schemas.properties[link.href].properties[method].properties.each do |status, schema|
|
209
|
+
link.target_schemas[status] = schema
|
210
|
+
end
|
223
211
|
end
|
224
212
|
end
|
225
213
|
|
@@ -23,13 +23,20 @@ module Committee
|
|
23
23
|
|
24
24
|
# The link's output schema. i.e. How we validate an endpoint's response
|
25
25
|
# data.
|
26
|
-
attr_accessor :
|
26
|
+
attr_accessor :target_schemas
|
27
27
|
|
28
28
|
attr_accessor :header_schema
|
29
29
|
|
30
30
|
def rel
|
31
31
|
raise "Committee: rel not implemented for OpenAPI"
|
32
32
|
end
|
33
|
+
|
34
|
+
def target_schema
|
35
|
+
target_schemas[status_success] ||
|
36
|
+
target_schemas[200] ||
|
37
|
+
target_schemas[201] ||
|
38
|
+
target_schemas.values.first
|
39
|
+
end
|
33
40
|
end
|
34
41
|
end
|
35
42
|
end
|
@@ -17,8 +17,7 @@ module Committee
|
|
17
17
|
check_required_fields!(body_param)
|
18
18
|
|
19
19
|
if link_data["parameters"].detect { |p| p["in"] == "form" } != nil
|
20
|
-
raise ArgumentError, "Committee: can't mix body parameter "
|
21
|
-
"with form parameters."
|
20
|
+
raise ArgumentError, "Committee: can't mix body parameter with form parameters."
|
22
21
|
end
|
23
22
|
|
24
23
|
schema_data = body_param["schema"]
|
@@ -10,17 +10,14 @@ module Committee
|
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
|
-
LINK_REQUIRED_FIELDS = [
|
14
|
-
:name
|
15
|
-
].map(&:to_s).freeze
|
13
|
+
LINK_REQUIRED_FIELDS = [:name].map(&:to_s).freeze
|
16
14
|
|
17
15
|
attr_accessor :link_data
|
18
16
|
|
19
17
|
def check_required_fields!(param_data)
|
20
18
|
LINK_REQUIRED_FIELDS.each do |field|
|
21
19
|
if !param_data[field]
|
22
|
-
raise ArgumentError,
|
23
|
-
"Committee: no #{field} section in link data."
|
20
|
+
raise ArgumentError, "Committee: no #{field} section in link data."
|
24
21
|
end
|
25
22
|
end
|
26
23
|
end
|
data/lib/committee/drivers.rb
CHANGED
@@ -65,7 +65,7 @@ module Committee
|
|
65
65
|
'from next version. Pass config `strict_reference_validation: true` (or false, if you must) ' +
|
66
66
|
'to quiet this warning.')
|
67
67
|
opts[:strict_reference_validation] ||= false
|
68
|
-
|
68
|
+
|
69
69
|
openapi = OpenAPIParser.parse_with_filepath(hash, schema_path, opts)
|
70
70
|
return Committee::Drivers::OpenAPI3::Driver.new.parse(openapi)
|
71
71
|
end
|
data/lib/committee/errors.rb
CHANGED
@@ -10,7 +10,7 @@ module Committee
|
|
10
10
|
class InvalidRequest < Error
|
11
11
|
attr_reader :original_error
|
12
12
|
|
13
|
-
def initialize(error_message=nil, original_error: nil)
|
13
|
+
def initialize(error_message = nil, original_error: nil)
|
14
14
|
@original_error = original_error
|
15
15
|
super(error_message)
|
16
16
|
end
|
@@ -19,7 +19,7 @@ module Committee
|
|
19
19
|
class InvalidResponse < Error
|
20
20
|
attr_reader :original_error
|
21
21
|
|
22
|
-
def initialize(error_message=nil, original_error: nil)
|
22
|
+
def initialize(error_message = nil, original_error: nil)
|
23
23
|
@original_error = original_error
|
24
24
|
super(error_message)
|
25
25
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Committee
|
4
4
|
module Middleware
|
5
5
|
class Base
|
6
|
-
def initialize(app, options={})
|
6
|
+
def initialize(app, options = {})
|
7
7
|
@app = app
|
8
8
|
|
9
9
|
@error_class = options.fetch(:error_class, Committee::ValidationError)
|
@@ -14,7 +14,7 @@ module Committee
|
|
14
14
|
@schema = self.class.get_schema(options)
|
15
15
|
|
16
16
|
@router = @schema.build_router(options)
|
17
|
-
@accept_request_filter = options[:accept_request_filter] || ->
|
17
|
+
@accept_request_filter = options[:accept_request_filter] || ->(_) { true }
|
18
18
|
end
|
19
19
|
|
20
20
|
def call(env)
|
@@ -20,11 +20,12 @@ module Committee
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def initialize(options={})
|
24
|
-
@allow_form_params
|
25
|
-
@allow_get_body
|
26
|
-
@allow_query_params
|
27
|
-
@
|
23
|
+
def initialize(options = {})
|
24
|
+
@allow_form_params = options[:allow_form_params]
|
25
|
+
@allow_get_body = options[:allow_get_body]
|
26
|
+
@allow_query_params = options[:allow_query_params]
|
27
|
+
@allow_non_get_query_params = options[:allow_non_get_query_params]
|
28
|
+
@optimistic_json = options[:optimistic_json]
|
28
29
|
end
|
29
30
|
|
30
31
|
# return params and is_form_params
|
@@ -32,7 +33,7 @@ module Committee
|
|
32
33
|
# if Content-Type is empty or JSON, and there was a request body, try to
|
33
34
|
# interpret it as JSON
|
34
35
|
params = if !request.media_type || request.media_type =~ %r{application/(?:.*\+)?json}
|
35
|
-
|
36
|
+
parse_json(request)
|
36
37
|
elsif @optimistic_json
|
37
38
|
begin
|
38
39
|
parse_json(request)
|
@@ -57,7 +58,15 @@ module Committee
|
|
57
58
|
end
|
58
59
|
|
59
60
|
def unpack_query_params(request)
|
60
|
-
@allow_query_params
|
61
|
+
unless @allow_query_params
|
62
|
+
return {}
|
63
|
+
end
|
64
|
+
|
65
|
+
if @allow_non_get_query_params
|
66
|
+
self.class.indifferent_params(request.params)
|
67
|
+
else
|
68
|
+
self.class.indifferent_params(request.GET)
|
69
|
+
end
|
61
70
|
end
|
62
71
|
|
63
72
|
def unpack_headers(request)
|
@@ -87,8 +96,7 @@ module Committee
|
|
87
96
|
# We want a hash specifically. '42', 42, and [42] will all be
|
88
97
|
# decoded properly, but we can't use them here.
|
89
98
|
if !hash.is_a?(Hash)
|
90
|
-
raise BadRequest,
|
91
|
-
"Invalid JSON input. Require object with parameters as keys."
|
99
|
+
raise BadRequest, "Invalid JSON input. Require object with parameters as keys."
|
92
100
|
end
|
93
101
|
self.class.indifferent_params(hash)
|
94
102
|
end
|
@@ -18,61 +18,61 @@ module Committee
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
def coerce_object!(hash, schema)
|
22
|
+
return false unless schema.respond_to?(:properties)
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
24
|
+
is_coerced = false
|
25
|
+
schema.properties.each do |k, s|
|
26
|
+
original_val = hash[k]
|
27
|
+
unless original_val.nil?
|
28
|
+
new_value, is_changed = coerce_value!(original_val, s)
|
29
|
+
if is_changed
|
30
|
+
hash[k] = new_value
|
31
|
+
is_coerced = true
|
33
32
|
end
|
34
33
|
end
|
35
|
-
|
36
|
-
is_coerced
|
37
34
|
end
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
if @coerce_date_times && to_type == "string" && s.format == "date-time"
|
42
|
-
coerced_val = parse_date_time(original_val)
|
43
|
-
return coerced_val, true if coerced_val
|
44
|
-
end
|
45
|
-
|
46
|
-
return original_val, true if @coerce_recursive && (to_type == "array") && coerce_array_data!(original_val, s)
|
36
|
+
is_coerced
|
37
|
+
end
|
47
38
|
|
48
|
-
|
39
|
+
def coerce_value!(original_val, s)
|
40
|
+
s.type.each do |to_type|
|
41
|
+
if @coerce_date_times && to_type == "string" && s.format == "date-time"
|
42
|
+
coerced_val = parse_date_time(original_val)
|
43
|
+
return coerced_val, true if coerced_val
|
49
44
|
end
|
50
|
-
|
45
|
+
|
46
|
+
return original_val, true if @coerce_recursive && (to_type == "array") && coerce_array_data!(original_val, s)
|
47
|
+
|
48
|
+
return original_val, true if @coerce_recursive && (to_type == "object") && coerce_object!(original_val, s)
|
51
49
|
end
|
50
|
+
return nil, false
|
51
|
+
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
def coerce_array_data!(original_val, schema)
|
54
|
+
return false unless schema.respond_to?(:items)
|
55
|
+
return false unless original_val.is_a?(Array)
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
57
|
+
is_coerced = false
|
58
|
+
original_val.each_with_index do |d, index|
|
59
|
+
new_value, is_changed = coerce_value!(d, schema.items)
|
60
|
+
if is_changed
|
61
|
+
original_val[index] = new_value
|
62
|
+
is_coerced = true
|
64
63
|
end
|
65
|
-
|
66
|
-
is_coerced
|
67
64
|
end
|
68
65
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
66
|
+
is_coerced
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_date_time(original_val)
|
70
|
+
begin
|
71
|
+
DateTime.parse(original_val)
|
72
|
+
rescue ArgumentError => e
|
73
|
+
raise ::Committee::InvalidResponse unless e.message =~ /invalid date/
|
75
74
|
end
|
75
|
+
end
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -35,8 +35,7 @@ module Committee
|
|
35
35
|
content_type = ::Committee::SchemaValidator.request_media_type(request)
|
36
36
|
if content_type && @link.enc_type && !empty_request?(request)
|
37
37
|
unless Rack::Mime.match?(content_type, @link.enc_type)
|
38
|
-
raise Committee::InvalidRequest,
|
39
|
-
%{"Content-Type" request header must be set to "#{@link.enc_type}".}
|
38
|
+
raise Committee::InvalidRequest, %{"Content-Type" request header must be set to "#{@link.enc_type}".}
|
40
39
|
end
|
41
40
|
end
|
42
41
|
end
|
@@ -11,7 +11,14 @@ module Committee
|
|
11
11
|
@validate_success_only = options[:validate_success_only]
|
12
12
|
@allow_blank_structures = options[:allow_blank_structures]
|
13
13
|
|
14
|
-
@
|
14
|
+
@validators = {}
|
15
|
+
if link.is_a? Drivers::OpenAPI2::Link
|
16
|
+
link.target_schemas.each do |status, schema|
|
17
|
+
@validators[status] = JsonSchema::Validator.new(target_schema(link))
|
18
|
+
end
|
19
|
+
else
|
20
|
+
@validators[link.status_success] = JsonSchema::Validator.new(target_schema(link))
|
21
|
+
end
|
15
22
|
end
|
16
23
|
|
17
24
|
def call(status, headers, data)
|
@@ -45,9 +52,12 @@ module Committee
|
|
45
52
|
end
|
46
53
|
|
47
54
|
begin
|
48
|
-
if Committee::Middleware::ResponseValidation.validate?(status, validate_success_only)
|
49
|
-
|
50
|
-
|
55
|
+
if Committee::Middleware::ResponseValidation.validate?(status, validate_success_only)
|
56
|
+
raise InvalidResponse, "Invalid response.#{@link.href} status code #{status} definition does not exist" if @validators[status].nil?
|
57
|
+
if !@validators[status].validate(data)
|
58
|
+
errors = JsonSchema::SchemaError.aggregate(@validators[status].errors).join("\n")
|
59
|
+
raise InvalidResponse, "Invalid response.\n\n#{errors}"
|
60
|
+
end
|
51
61
|
end
|
52
62
|
rescue => e
|
53
63
|
raise InvalidResponse, "Invalid response.\n\nschema is undefined" if /undefined method .all_of. for nil/ =~ e.message
|
@@ -66,12 +76,10 @@ module Committee
|
|
66
76
|
end
|
67
77
|
end
|
68
78
|
|
69
|
-
|
70
79
|
def check_content_type!(response)
|
71
80
|
if @link.media_type
|
72
81
|
unless Rack::Mime.match?(response_media_type(response), @link.media_type)
|
73
|
-
raise Committee::InvalidResponse,
|
74
|
-
%{"Content-Type" response header must be set to "#{@link.media_type}".}
|
82
|
+
raise Committee::InvalidResponse, %{"Content-Type" response header must be set to "#{@link.media_type}".}
|
75
83
|
end
|
76
84
|
end
|
77
85
|
end
|
@@ -27,78 +27,78 @@ module Committee
|
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
def coerce_object!(hash, schema)
|
31
|
+
return false unless schema.respond_to?(:properties)
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
33
|
+
is_coerced = false
|
34
|
+
schema.properties.each do |k, s|
|
35
|
+
original_val = hash[k]
|
36
|
+
unless original_val.nil?
|
37
|
+
new_value, is_changed = coerce_value!(original_val, s)
|
38
|
+
if is_changed
|
39
|
+
hash[k] = new_value
|
40
|
+
is_coerced = true
|
42
41
|
end
|
43
42
|
end
|
44
|
-
|
45
|
-
is_coerced
|
46
43
|
end
|
47
44
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
45
|
+
is_coerced
|
46
|
+
end
|
47
|
+
|
48
|
+
def coerce_value!(original_val, s)
|
49
|
+
unless original_val.nil?
|
50
|
+
s.type.each do |to_type|
|
51
|
+
case to_type
|
52
|
+
when "null"
|
53
|
+
return nil, true if original_val.empty?
|
54
|
+
when "integer"
|
55
|
+
begin
|
56
|
+
return Integer(original_val), true
|
57
|
+
rescue ArgumentError => e
|
58
|
+
raise e unless e.message =~ /invalid value for Integer/
|
59
|
+
end
|
60
|
+
when "number"
|
61
|
+
begin
|
62
|
+
return Float(original_val), true
|
63
|
+
rescue ArgumentError => e
|
64
|
+
raise e unless e.message =~ /invalid value for Float/
|
65
|
+
end
|
66
|
+
when "boolean"
|
67
|
+
if original_val == "true" || original_val == "1"
|
68
|
+
return true, true
|
69
|
+
end
|
70
|
+
if original_val == "false" || original_val == "0"
|
71
|
+
return false, true
|
72
|
+
end
|
73
|
+
when "array"
|
74
|
+
if @coerce_recursive && coerce_array_data!(original_val, s)
|
75
|
+
return original_val, true # change original value
|
76
|
+
end
|
77
|
+
when "object"
|
78
|
+
if @coerce_recursive && coerce_object!(original_val, s)
|
79
|
+
return original_val, true # change original value
|
80
|
+
end
|
82
81
|
end
|
83
82
|
end
|
84
|
-
return nil, false
|
85
83
|
end
|
84
|
+
return nil, false
|
85
|
+
end
|
86
86
|
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
def coerce_array_data!(original_val, schema)
|
88
|
+
return false unless schema.respond_to?(:items)
|
89
|
+
return false unless original_val.is_a?(Array)
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
91
|
+
is_coerced = false
|
92
|
+
original_val.each_with_index do |d, index|
|
93
|
+
new_value, is_changed = coerce_value!(d, schema.items)
|
94
|
+
if is_changed
|
95
|
+
original_val[index] = new_value
|
96
|
+
is_coerced = true
|
98
97
|
end
|
99
|
-
|
100
|
-
is_coerced
|
101
98
|
end
|
99
|
+
|
100
|
+
is_coerced
|
101
|
+
end
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|