openapi_parser_firetail 1.0.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 +7 -0
- data/.github/workflows/ci.yaml +25 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.rubocop_ignore.yml +6 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +132 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +112 -0
- data/Rakefile +10 -0
- data/Steepfile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/openapi_parser/concern.rb +5 -0
- data/lib/openapi_parser/concerns/expandable.rb +87 -0
- data/lib/openapi_parser/concerns/findable.rb +54 -0
- data/lib/openapi_parser/concerns/media_type_selectable.rb +29 -0
- data/lib/openapi_parser/concerns/parameter_validatable.rb +62 -0
- data/lib/openapi_parser/concerns/parser/core.rb +21 -0
- data/lib/openapi_parser/concerns/parser/hash.rb +10 -0
- data/lib/openapi_parser/concerns/parser/hash_body.rb +12 -0
- data/lib/openapi_parser/concerns/parser/list.rb +10 -0
- data/lib/openapi_parser/concerns/parser/object.rb +14 -0
- data/lib/openapi_parser/concerns/parser/value.rb +14 -0
- data/lib/openapi_parser/concerns/parser.rb +45 -0
- data/lib/openapi_parser/concerns/schema_loader/base.rb +28 -0
- data/lib/openapi_parser/concerns/schema_loader/creator.rb +48 -0
- data/lib/openapi_parser/concerns/schema_loader/hash_body_loader.rb +37 -0
- data/lib/openapi_parser/concerns/schema_loader/hash_objects_loader.rb +29 -0
- data/lib/openapi_parser/concerns/schema_loader/list_loader.rb +28 -0
- data/lib/openapi_parser/concerns/schema_loader/objects_loader.rb +21 -0
- data/lib/openapi_parser/concerns/schema_loader/values_loader.rb +10 -0
- data/lib/openapi_parser/concerns/schema_loader.rb +58 -0
- data/lib/openapi_parser/config.rb +55 -0
- data/lib/openapi_parser/errors.rb +281 -0
- data/lib/openapi_parser/parameter_validator.rb +33 -0
- data/lib/openapi_parser/path_item_finder.rb +161 -0
- data/lib/openapi_parser/reference_expander.rb +9 -0
- data/lib/openapi_parser/request_operation.rb +90 -0
- data/lib/openapi_parser/schema_validator/all_of_validator.rb +40 -0
- data/lib/openapi_parser/schema_validator/any_of_validator.rb +18 -0
- data/lib/openapi_parser/schema_validator/array_validator.rb +32 -0
- data/lib/openapi_parser/schema_validator/base.rb +39 -0
- data/lib/openapi_parser/schema_validator/boolean_validator.rb +29 -0
- data/lib/openapi_parser/schema_validator/enumable.rb +13 -0
- data/lib/openapi_parser/schema_validator/float_validator.rb +34 -0
- data/lib/openapi_parser/schema_validator/integer_validator.rb +32 -0
- data/lib/openapi_parser/schema_validator/minimum_maximum.rb +38 -0
- data/lib/openapi_parser/schema_validator/nil_validator.rb +11 -0
- data/lib/openapi_parser/schema_validator/object_validator.rb +56 -0
- data/lib/openapi_parser/schema_validator/one_of_validator.rb +22 -0
- data/lib/openapi_parser/schema_validator/options.rb +29 -0
- data/lib/openapi_parser/schema_validator/string_validator.rb +108 -0
- data/lib/openapi_parser/schema_validator/unspecified_type_validator.rb +8 -0
- data/lib/openapi_parser/schema_validator.rb +164 -0
- data/lib/openapi_parser/schemas/base.rb +28 -0
- data/lib/openapi_parser/schemas/classes.rb +22 -0
- data/lib/openapi_parser/schemas/components.rb +32 -0
- data/lib/openapi_parser/schemas/discriminator.rb +11 -0
- data/lib/openapi_parser/schemas/header.rb +18 -0
- data/lib/openapi_parser/schemas/info.rb +6 -0
- data/lib/openapi_parser/schemas/media_type.rb +18 -0
- data/lib/openapi_parser/schemas/openapi.rb +63 -0
- data/lib/openapi_parser/schemas/operation.rb +50 -0
- data/lib/openapi_parser/schemas/parameter.rb +20 -0
- data/lib/openapi_parser/schemas/path_item.rb +22 -0
- data/lib/openapi_parser/schemas/paths.rb +7 -0
- data/lib/openapi_parser/schemas/reference.rb +7 -0
- data/lib/openapi_parser/schemas/request_body.rb +34 -0
- data/lib/openapi_parser/schemas/response.rb +54 -0
- data/lib/openapi_parser/schemas/responses.rb +56 -0
- data/lib/openapi_parser/schemas/schema.rb +117 -0
- data/lib/openapi_parser/schemas/security.rb +7 -0
- data/lib/openapi_parser/schemas/security_schemes.rb +20 -0
- data/lib/openapi_parser/schemas.rb +20 -0
- data/lib/openapi_parser/version.rb +3 -0
- data/lib/openapi_parser.rb +108 -0
- data/openapi_parser.gemspec +43 -0
- data/sig/openapi_parser/config.rbs +19 -0
- data/sig/openapi_parser/errors.rbs +22 -0
- data/sig/openapi_parser/reference_expander.rbs +3 -0
- data/sig/openapi_parser/schema_validator.rbs +46 -0
- data/sig/openapi_parser/schema_validators/base.rbs +18 -0
- data/sig/openapi_parser/schema_validators/options.rbs +17 -0
- data/sig/openapi_parser/schemas/base.rbs +17 -0
- data/sig/openapi_parser/version.rbs +3 -0
- data/sig/openapi_parser.rbs +19 -0
- data/sig/types.rbs +13 -0
- data/sig/wip_types.rbs +64 -0
- metadata +288 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class Base
|
|
3
|
+
def initialize(validatable, coerce_value)
|
|
4
|
+
@validatable = validatable
|
|
5
|
+
@coerce_value = coerce_value
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
attr_reader :validatable
|
|
9
|
+
|
|
10
|
+
# need override
|
|
11
|
+
def coerce_and_validate(_value, _schema, **_keyword_args)
|
|
12
|
+
raise 'need implement'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def validate_discriminator_schema(discriminator, value, parent_discriminator_schemas: [])
|
|
16
|
+
property_name = discriminator.property_name
|
|
17
|
+
if property_name.nil? || !value.key?(property_name)
|
|
18
|
+
return [nil, OpenAPIParser::NotExistDiscriminatorPropertyName.new(discriminator.property_name, value, discriminator.object_reference)]
|
|
19
|
+
end
|
|
20
|
+
mapping_key = value[property_name]
|
|
21
|
+
|
|
22
|
+
# it's allowed to have discriminator without mapping, then we need to lookup discriminator.property_name
|
|
23
|
+
# but the format is not the full path, just model name in the components
|
|
24
|
+
mapping_target = discriminator.mapping&.[](mapping_key) || "#/components/schemas/#{mapping_key}"
|
|
25
|
+
|
|
26
|
+
# Find object does O(n) search at worst, then caches the result, so this is ok for repeated search
|
|
27
|
+
resolved_schema = discriminator.root.find_object(mapping_target)
|
|
28
|
+
|
|
29
|
+
unless resolved_schema
|
|
30
|
+
return [nil, OpenAPIParser::NotExistDiscriminatorMappedSchema.new(mapping_target, discriminator.object_reference)]
|
|
31
|
+
end
|
|
32
|
+
validatable.validate_schema(
|
|
33
|
+
value,
|
|
34
|
+
resolved_schema,
|
|
35
|
+
**{discriminator_property_name: discriminator.property_name, parent_discriminator_schemas: parent_discriminator_schemas}
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class BooleanValidator < Base
|
|
3
|
+
include ::OpenAPIParser::SchemaValidator::Enumable
|
|
4
|
+
|
|
5
|
+
TRUE_VALUES = ['true', '1'].freeze
|
|
6
|
+
FALSE_VALUES = ['false', '0'].freeze
|
|
7
|
+
|
|
8
|
+
def coerce_and_validate(value, schema, **_keyword_args)
|
|
9
|
+
value = coerce(value) if @coerce_value
|
|
10
|
+
|
|
11
|
+
return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(TrueClass) || value.kind_of?(FalseClass)
|
|
12
|
+
|
|
13
|
+
value, err = check_enum_include(value, schema)
|
|
14
|
+
return [nil, err] if err
|
|
15
|
+
|
|
16
|
+
[value, nil]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def coerce(value)
|
|
22
|
+
return true if TRUE_VALUES.include?(value)
|
|
23
|
+
|
|
24
|
+
return false if FALSE_VALUES.include?(value)
|
|
25
|
+
|
|
26
|
+
value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
module Enumable
|
|
3
|
+
# check enum value by schema
|
|
4
|
+
# @param [Object] value
|
|
5
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
6
|
+
def check_enum_include(value, schema)
|
|
7
|
+
return [value, nil] unless schema.enum
|
|
8
|
+
return [value, nil] if schema.enum.include?(value)
|
|
9
|
+
|
|
10
|
+
[nil, OpenAPIParser::NotEnumInclude.new(value, schema.object_reference)]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class FloatValidator < Base
|
|
3
|
+
include ::OpenAPIParser::SchemaValidator::Enumable
|
|
4
|
+
include ::OpenAPIParser::SchemaValidator::MinimumMaximum
|
|
5
|
+
|
|
6
|
+
# validate float value by schema
|
|
7
|
+
# @param [Object] value
|
|
8
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
9
|
+
def coerce_and_validate(value, schema, **_keyword_args)
|
|
10
|
+
value = coerce(value) if @coerce_value
|
|
11
|
+
|
|
12
|
+
return validatable.validate_integer(value, schema) if value.kind_of?(Integer)
|
|
13
|
+
|
|
14
|
+
coercer_and_validate_numeric(value, schema)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def coercer_and_validate_numeric(value, schema)
|
|
20
|
+
return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(Numeric)
|
|
21
|
+
|
|
22
|
+
value, err = check_enum_include(value, schema)
|
|
23
|
+
return [nil, err] if err
|
|
24
|
+
|
|
25
|
+
check_minimum_maximum(value, schema)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def coerce(value)
|
|
29
|
+
Float(value)
|
|
30
|
+
rescue ArgumentError, TypeError
|
|
31
|
+
value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class IntegerValidator < Base
|
|
3
|
+
include ::OpenAPIParser::SchemaValidator::Enumable
|
|
4
|
+
include ::OpenAPIParser::SchemaValidator::MinimumMaximum
|
|
5
|
+
|
|
6
|
+
# validate integer value by schema
|
|
7
|
+
# @param [Object] value
|
|
8
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
9
|
+
def coerce_and_validate(value, schema, **_keyword_args)
|
|
10
|
+
value = coerce(value) if @coerce_value
|
|
11
|
+
|
|
12
|
+
return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(Integer)
|
|
13
|
+
|
|
14
|
+
value, err = check_enum_include(value, schema)
|
|
15
|
+
return [nil, err] if err
|
|
16
|
+
|
|
17
|
+
check_minimum_maximum(value, schema)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def coerce(value)
|
|
23
|
+
return value if value.kind_of?(Integer)
|
|
24
|
+
|
|
25
|
+
begin
|
|
26
|
+
Integer(value)
|
|
27
|
+
rescue ArgumentError, TypeError
|
|
28
|
+
value
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
module MinimumMaximum
|
|
3
|
+
# check minimum and maximum value by schema
|
|
4
|
+
# @param [Object] value
|
|
5
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
6
|
+
def check_minimum_maximum(value, schema)
|
|
7
|
+
include_min_max = schema.minimum || schema.maximum
|
|
8
|
+
return [value, nil] unless include_min_max
|
|
9
|
+
|
|
10
|
+
validate(value, schema)
|
|
11
|
+
[value, nil]
|
|
12
|
+
rescue OpenAPIParser::OpenAPIError => e
|
|
13
|
+
return [nil, e]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def validate(value, schema)
|
|
19
|
+
reference = schema.object_reference
|
|
20
|
+
|
|
21
|
+
if schema.minimum
|
|
22
|
+
if schema.exclusiveMinimum && value <= schema.minimum
|
|
23
|
+
raise OpenAPIParser::LessThanExclusiveMinimum.new(value, reference)
|
|
24
|
+
elsif value < schema.minimum
|
|
25
|
+
raise OpenAPIParser::LessThanMinimum.new(value, reference)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if schema.maximum
|
|
30
|
+
if schema.exclusiveMaximum && value >= schema.maximum
|
|
31
|
+
raise OpenAPIParser::MoreThanExclusiveMaximum.new(value, reference)
|
|
32
|
+
elsif value > schema.maximum
|
|
33
|
+
raise OpenAPIParser::MoreThanMaximum.new(value, reference)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class NilValidator < Base
|
|
3
|
+
# @param [Object] value
|
|
4
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
5
|
+
def coerce_and_validate(value, schema, **_keyword_args)
|
|
6
|
+
return [value, nil] if schema.nullable
|
|
7
|
+
|
|
8
|
+
[nil, OpenAPIParser::NotNullError.new(schema.object_reference)]
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class ObjectValidator < Base
|
|
3
|
+
# @param [Hash] value
|
|
4
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
5
|
+
# @param [Boolean] parent_all_of true if component is nested under allOf
|
|
6
|
+
# @param [String, nil] discriminator_property_name discriminator.property_name to ignore checking additional_properties
|
|
7
|
+
def coerce_and_validate(value, schema, parent_all_of: false, parent_discriminator_schemas: [], discriminator_property_name: nil)
|
|
8
|
+
return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(Hash)
|
|
9
|
+
|
|
10
|
+
properties = schema.properties || {}
|
|
11
|
+
|
|
12
|
+
required_set = schema.required ? schema.required.to_set : Set.new
|
|
13
|
+
remaining_keys = value.keys
|
|
14
|
+
|
|
15
|
+
if schema.discriminator && !parent_discriminator_schemas.include?(schema)
|
|
16
|
+
return validate_discriminator_schema(
|
|
17
|
+
schema.discriminator,
|
|
18
|
+
value,
|
|
19
|
+
parent_discriminator_schemas: parent_discriminator_schemas + [schema]
|
|
20
|
+
)
|
|
21
|
+
else
|
|
22
|
+
remaining_keys.delete('discriminator')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
coerced_values = value.map do |name, v|
|
|
26
|
+
s = properties[name]
|
|
27
|
+
coerced, err = if s
|
|
28
|
+
remaining_keys.delete(name)
|
|
29
|
+
validatable.validate_schema(v, s)
|
|
30
|
+
else
|
|
31
|
+
# TODO: we need to perform a validation based on schema.additional_properties here, if
|
|
32
|
+
# additionalProperties are defined
|
|
33
|
+
[v, nil]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
return [nil, err] if err
|
|
37
|
+
|
|
38
|
+
required_set.delete(name)
|
|
39
|
+
[name, coerced]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
remaining_keys.delete(discriminator_property_name) if discriminator_property_name
|
|
43
|
+
|
|
44
|
+
if !remaining_keys.empty? && !parent_all_of && !schema.additional_properties
|
|
45
|
+
# If object is nested in all of, the validation is already done in allOf validator. Or if
|
|
46
|
+
# additionalProperties are defined, we will validate using that
|
|
47
|
+
return [nil, OpenAPIParser::NotExistPropertyDefinition.new(remaining_keys, schema.object_reference)]
|
|
48
|
+
end
|
|
49
|
+
return [nil, OpenAPIParser::NotExistRequiredKey.new(required_set.to_a, schema.object_reference)] unless required_set.empty?
|
|
50
|
+
|
|
51
|
+
value.merge!(coerced_values.to_h) if @coerce_value
|
|
52
|
+
|
|
53
|
+
[value, nil]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class OneOfValidator < Base
|
|
3
|
+
# @param [Object] value
|
|
4
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
5
|
+
def coerce_and_validate(value, schema, **_keyword_args)
|
|
6
|
+
if schema.discriminator
|
|
7
|
+
return validate_discriminator_schema(schema.discriminator, value)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# if multiple schemas are satisfied, it's not valid
|
|
11
|
+
result = schema.one_of.one? do |s|
|
|
12
|
+
_coerced, err = validatable.validate_schema(value, s)
|
|
13
|
+
err.nil?
|
|
14
|
+
end
|
|
15
|
+
if result
|
|
16
|
+
[value, nil]
|
|
17
|
+
else
|
|
18
|
+
[nil, OpenAPIParser::NotOneOf.new(value, schema.object_reference)]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class Options
|
|
3
|
+
# @!attribute [r] coerce_value
|
|
4
|
+
# @return [Boolean] coerce value option on/off
|
|
5
|
+
# @!attribute [r] datetime_coerce_class
|
|
6
|
+
# @return [Object, nil] coerce datetime string by this Object class
|
|
7
|
+
# @!attribute [r] validate_header
|
|
8
|
+
# @return [Boolean] validate header or not
|
|
9
|
+
attr_reader :coerce_value, :datetime_coerce_class, :validate_header
|
|
10
|
+
|
|
11
|
+
def initialize(coerce_value: nil, datetime_coerce_class: nil, validate_header: true)
|
|
12
|
+
@coerce_value = coerce_value
|
|
13
|
+
@datetime_coerce_class = datetime_coerce_class
|
|
14
|
+
@validate_header = validate_header
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# response body validation option
|
|
19
|
+
class ResponseValidateOptions
|
|
20
|
+
# @!attribute [r] strict
|
|
21
|
+
# @return [Boolean] validate by strict (when not exist definition, raise error)
|
|
22
|
+
attr_reader :strict, :validate_header
|
|
23
|
+
|
|
24
|
+
def initialize(strict: false, validate_header: true)
|
|
25
|
+
@strict = strict
|
|
26
|
+
@validate_header = validate_header
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
class OpenAPIParser::SchemaValidator
|
|
2
|
+
class StringValidator < Base
|
|
3
|
+
include ::OpenAPIParser::SchemaValidator::Enumable
|
|
4
|
+
|
|
5
|
+
def initialize(validator, coerce_value, datetime_coerce_class)
|
|
6
|
+
super(validator, coerce_value)
|
|
7
|
+
@datetime_coerce_class = datetime_coerce_class
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def coerce_and_validate(value, schema, **_keyword_args)
|
|
11
|
+
return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(String)
|
|
12
|
+
|
|
13
|
+
value, err = check_enum_include(value, schema)
|
|
14
|
+
return [nil, err] if err
|
|
15
|
+
|
|
16
|
+
value, err = pattern_validate(value, schema)
|
|
17
|
+
return [nil, err] if err
|
|
18
|
+
|
|
19
|
+
value, err = validate_max_min_length(value, schema)
|
|
20
|
+
return [nil, err] if err
|
|
21
|
+
|
|
22
|
+
value, err = validate_email_format(value, schema)
|
|
23
|
+
return [nil, err] if err
|
|
24
|
+
|
|
25
|
+
value, err = validate_uuid_format(value, schema)
|
|
26
|
+
return [nil, err] if err
|
|
27
|
+
|
|
28
|
+
value, err = validate_date_format(value, schema)
|
|
29
|
+
return [nil, err] if err
|
|
30
|
+
|
|
31
|
+
value, err = validate_datetime_format(value, schema)
|
|
32
|
+
return [nil, err] if err
|
|
33
|
+
|
|
34
|
+
[value, nil]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
40
|
+
def pattern_validate(value, schema)
|
|
41
|
+
# pattern support string only so put this
|
|
42
|
+
return [value, nil] unless schema.pattern
|
|
43
|
+
return [value, nil] if value =~ /#{schema.pattern}/
|
|
44
|
+
|
|
45
|
+
[nil, OpenAPIParser::InvalidPattern.new(value, schema.pattern, schema.object_reference, schema.example)]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def validate_max_min_length(value, schema)
|
|
49
|
+
return [nil, OpenAPIParser::MoreThanMaxLength.new(value, schema.object_reference)] if schema.maxLength && value.size > schema.maxLength
|
|
50
|
+
return [nil, OpenAPIParser::LessThanMinLength.new(value, schema.object_reference)] if schema.minLength && value.size < schema.minLength
|
|
51
|
+
|
|
52
|
+
[value, nil]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_email_format(value, schema)
|
|
56
|
+
return [value, nil] unless schema.format == 'email'
|
|
57
|
+
|
|
58
|
+
# match? method is good performance.
|
|
59
|
+
# So when we drop ruby 2.3 support we use match? method because this method add ruby 2.4
|
|
60
|
+
#return [value, nil] if value.match?(URI::MailTo::EMAIL_REGEXP)
|
|
61
|
+
return [value, nil] if value.match(URI::MailTo::EMAIL_REGEXP)
|
|
62
|
+
|
|
63
|
+
return [nil, OpenAPIParser::InvalidEmailFormat.new(value, schema.object_reference)]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def validate_uuid_format(value, schema)
|
|
67
|
+
return [value, nil] unless schema.format == 'uuid'
|
|
68
|
+
|
|
69
|
+
return [value, nil] if value.match(/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/)
|
|
70
|
+
|
|
71
|
+
return [nil, OpenAPIParser::InvalidUUIDFormat.new(value, schema.object_reference)]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def validate_date_format(value, schema)
|
|
75
|
+
return [value, nil] unless schema.format == 'date'
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
Date.strptime(value, "%Y-%m-%d")
|
|
79
|
+
rescue ArgumentError
|
|
80
|
+
return [nil, OpenAPIParser::InvalidDateFormat.new(value, schema.object_reference)]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return [value, nil]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def validate_datetime_format(value, schema)
|
|
87
|
+
return [value, nil] unless schema.format == 'date-time'
|
|
88
|
+
|
|
89
|
+
begin
|
|
90
|
+
if @datetime_coerce_class.nil?
|
|
91
|
+
# validate only
|
|
92
|
+
DateTime.rfc3339(value)
|
|
93
|
+
[value, nil]
|
|
94
|
+
else
|
|
95
|
+
# validate and coerce
|
|
96
|
+
if @datetime_coerce_class == Time
|
|
97
|
+
[DateTime.rfc3339(value).to_time, nil]
|
|
98
|
+
else
|
|
99
|
+
[@datetime_coerce_class.rfc3339(value), nil]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
rescue ArgumentError
|
|
103
|
+
# when rfc3339(value) failed
|
|
104
|
+
[nil, OpenAPIParser::InvalidDateTimeFormat.new(value, schema.object_reference)]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
require_relative 'schema_validator/options'
|
|
2
|
+
require_relative 'schema_validator/enumable'
|
|
3
|
+
require_relative 'schema_validator/minimum_maximum'
|
|
4
|
+
require_relative 'schema_validator/base'
|
|
5
|
+
require_relative 'schema_validator/string_validator'
|
|
6
|
+
require_relative 'schema_validator/integer_validator'
|
|
7
|
+
require_relative 'schema_validator/float_validator'
|
|
8
|
+
require_relative 'schema_validator/boolean_validator'
|
|
9
|
+
require_relative 'schema_validator/object_validator'
|
|
10
|
+
require_relative 'schema_validator/array_validator'
|
|
11
|
+
require_relative 'schema_validator/any_of_validator'
|
|
12
|
+
require_relative 'schema_validator/all_of_validator'
|
|
13
|
+
require_relative 'schema_validator/one_of_validator'
|
|
14
|
+
require_relative 'schema_validator/nil_validator'
|
|
15
|
+
require_relative 'schema_validator/unspecified_type_validator'
|
|
16
|
+
|
|
17
|
+
class OpenAPIParser::SchemaValidator
|
|
18
|
+
# validate value by schema
|
|
19
|
+
# this module for SchemaValidators::Base
|
|
20
|
+
# @param [Object] value
|
|
21
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
22
|
+
module Validatable
|
|
23
|
+
def validate_schema(value, schema, **keyword_args)
|
|
24
|
+
raise 'implement'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# validate integer value by schema
|
|
28
|
+
# this method use from float_validator because number allow float and integer
|
|
29
|
+
# @param [Object] _value
|
|
30
|
+
# @param [OpenAPIParser::Schemas::Schema] _schema
|
|
31
|
+
def validate_integer(_value, _schema)
|
|
32
|
+
raise 'implement'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
include Validatable
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# validate schema data
|
|
40
|
+
# @param [Hash] value
|
|
41
|
+
# @param [OpenAPIParser::Schemas:v:Schema]
|
|
42
|
+
# @param [OpenAPIParser::SchemaValidator::Options] options
|
|
43
|
+
# @return [Object] coerced or original params
|
|
44
|
+
def validate(value, schema, options)
|
|
45
|
+
new(value, schema, options).validate_data
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param [Hash] value
|
|
50
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
51
|
+
# @param [OpenAPIParser::SchemaValidator::Options] options
|
|
52
|
+
def initialize(value, schema, options)
|
|
53
|
+
@value = value
|
|
54
|
+
@schema = schema
|
|
55
|
+
@coerce_value = options.coerce_value
|
|
56
|
+
@datetime_coerce_class = options.datetime_coerce_class
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# execute validate data
|
|
60
|
+
# @return [Object] coerced or original params
|
|
61
|
+
def validate_data
|
|
62
|
+
coerced, err = validate_schema(@value, @schema)
|
|
63
|
+
raise err if err
|
|
64
|
+
|
|
65
|
+
coerced
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# validate value eby schema
|
|
69
|
+
# @param [Object] value
|
|
70
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
71
|
+
def validate_schema(value, schema, **keyword_args)
|
|
72
|
+
return [value, nil] unless schema
|
|
73
|
+
|
|
74
|
+
if (v = validator(value, schema))
|
|
75
|
+
if keyword_args.empty?
|
|
76
|
+
return v.coerce_and_validate(value, schema)
|
|
77
|
+
else
|
|
78
|
+
return v.coerce_and_validate(value, schema, **keyword_args)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# unknown return error
|
|
83
|
+
OpenAPIParser::ValidateError.build_error_result(value, schema)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# validate integer value by schema
|
|
87
|
+
# this method use from float_validator because number allow float and integer
|
|
88
|
+
# @param [Object] value
|
|
89
|
+
# @param [OpenAPIParser::Schemas::Schema] schema
|
|
90
|
+
def validate_integer(value, schema)
|
|
91
|
+
integer_validator.coerce_and_validate(value, schema)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
# @return [OpenAPIParser::SchemaValidator::Base, nil]
|
|
97
|
+
def validator(value, schema)
|
|
98
|
+
return any_of_validator if schema.any_of
|
|
99
|
+
return all_of_validator if schema.all_of
|
|
100
|
+
return one_of_validator if schema.one_of
|
|
101
|
+
return nil_validator if value.nil?
|
|
102
|
+
|
|
103
|
+
case schema.type
|
|
104
|
+
when 'string'
|
|
105
|
+
string_validator
|
|
106
|
+
when 'integer'
|
|
107
|
+
integer_validator
|
|
108
|
+
when 'boolean'
|
|
109
|
+
boolean_validator
|
|
110
|
+
when 'number'
|
|
111
|
+
float_validator
|
|
112
|
+
when 'object'
|
|
113
|
+
object_validator
|
|
114
|
+
when 'array'
|
|
115
|
+
array_validator
|
|
116
|
+
else
|
|
117
|
+
unspecified_type_validator
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def string_validator
|
|
122
|
+
@string_validator ||= OpenAPIParser::SchemaValidator::StringValidator.new(self, @coerce_value, @datetime_coerce_class)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def integer_validator
|
|
126
|
+
@integer_validator ||= OpenAPIParser::SchemaValidator::IntegerValidator.new(self, @coerce_value)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def float_validator
|
|
130
|
+
@float_validator ||= OpenAPIParser::SchemaValidator::FloatValidator.new(self, @coerce_value)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def boolean_validator
|
|
134
|
+
@boolean_validator ||= OpenAPIParser::SchemaValidator::BooleanValidator.new(self, @coerce_value)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def object_validator
|
|
138
|
+
@object_validator ||= OpenAPIParser::SchemaValidator::ObjectValidator.new(self, @coerce_value)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def array_validator
|
|
142
|
+
@array_validator ||= OpenAPIParser::SchemaValidator::ArrayValidator.new(self, @coerce_value)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def any_of_validator
|
|
146
|
+
@any_of_validator ||= OpenAPIParser::SchemaValidator::AnyOfValidator.new(self, @coerce_value)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def all_of_validator
|
|
150
|
+
@all_of_validator ||= OpenAPIParser::SchemaValidator::AllOfValidator.new(self, @coerce_value)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def one_of_validator
|
|
154
|
+
@one_of_validator ||= OpenAPIParser::SchemaValidator::OneOfValidator.new(self, @coerce_value)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def nil_validator
|
|
158
|
+
@nil_validator ||= OpenAPIParser::SchemaValidator::NilValidator.new(self, @coerce_value)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def unspecified_type_validator
|
|
162
|
+
@unspecified_type_validator ||= OpenAPIParser::SchemaValidator::UnspecifiedTypeValidator.new(self, @coerce_value)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module OpenAPIParser::Schemas
|
|
2
|
+
class Base
|
|
3
|
+
include OpenAPIParser::Parser
|
|
4
|
+
include OpenAPIParser::Findable
|
|
5
|
+
include OpenAPIParser::Expandable
|
|
6
|
+
|
|
7
|
+
attr_reader :parent, :raw_schema, :object_reference, :root
|
|
8
|
+
|
|
9
|
+
# @param [OpenAPIParser::Schemas::Base]
|
|
10
|
+
def initialize(object_reference, parent, root, raw_schema)
|
|
11
|
+
@raw_schema = raw_schema
|
|
12
|
+
@parent = parent
|
|
13
|
+
@root = root
|
|
14
|
+
@object_reference = object_reference
|
|
15
|
+
|
|
16
|
+
load_data
|
|
17
|
+
after_init
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# override
|
|
21
|
+
def after_init
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def inspect
|
|
25
|
+
@object_reference
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# We want to use class name for DSL in class definition so we should define first...
|
|
2
|
+
|
|
3
|
+
module OpenAPIParser::Schemas
|
|
4
|
+
class Base; end
|
|
5
|
+
class Discriminator < Base; end
|
|
6
|
+
class OpenAPI < Base; end
|
|
7
|
+
class Operation < Base; end
|
|
8
|
+
class Parameter < Base; end
|
|
9
|
+
class PathItem < Base; end
|
|
10
|
+
class Paths < Base; end
|
|
11
|
+
class Reference < Base; end
|
|
12
|
+
class RequestBody < Base; end
|
|
13
|
+
class Responses < Base; end
|
|
14
|
+
class Response < Base; end
|
|
15
|
+
class MediaType < Base; end
|
|
16
|
+
class Schema < Base; end
|
|
17
|
+
class Components < Base; end
|
|
18
|
+
class Header < Base; end
|
|
19
|
+
class SecuritySchemes < Base; end
|
|
20
|
+
class Info < Base; end
|
|
21
|
+
class Security < Base; end
|
|
22
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# TODO: examples
|
|
2
|
+
# TODO: securitySchemes
|
|
3
|
+
# TODO: links
|
|
4
|
+
# TODO: callbacks
|
|
5
|
+
|
|
6
|
+
module OpenAPIParser::Schemas
|
|
7
|
+
class Components < Base
|
|
8
|
+
# @!attribute [r] parameters
|
|
9
|
+
# @return [Hash{String => Parameter}, nil]
|
|
10
|
+
openapi_attr_hash_object :parameters, Parameter, reference: true
|
|
11
|
+
|
|
12
|
+
# @!attribute [r] parameters
|
|
13
|
+
# @return [Hash{String => Parameter}, nil]
|
|
14
|
+
openapi_attr_hash_object :schemas, Schema, reference: true
|
|
15
|
+
|
|
16
|
+
# @!attribute [r] responses
|
|
17
|
+
# @return [Hash{String => Response}, nil]
|
|
18
|
+
openapi_attr_hash_object :responses, Response, reference: true
|
|
19
|
+
|
|
20
|
+
# @!attribute [r] request_bodies
|
|
21
|
+
# @return [Hash{String => RequestBody}, nil]
|
|
22
|
+
openapi_attr_hash_object :request_bodies, RequestBody, reference: true, schema_key: :requestBodies
|
|
23
|
+
|
|
24
|
+
# @!attribute [r] headers
|
|
25
|
+
# @return [Hash{String => Header}, nil] header objects
|
|
26
|
+
openapi_attr_hash_object :headers, Header, reference: true
|
|
27
|
+
|
|
28
|
+
# @!attribute [r] security_schemes
|
|
29
|
+
# @return [Hash{String => Header}, nil]
|
|
30
|
+
openapi_attr_hash_object :security_schemes, SecuritySchemes, reference: true, schema_key: :securitySchemes
|
|
31
|
+
end
|
|
32
|
+
end
|