mirror of
https://github.com/LukeHagar/connexion.git
synced 2025-12-10 04:19:37 +00:00
Bump upperbound version for jsonschema to 5.0.0 (#1447)
* Drop usage of private jsonschema modules * Bump upper boundry for jsonschema version to 5.0.0 * Create validate_defaults function by passing in the instance_validator The instance_validator was previously set as an attribute on the OpenApiValidator. However with the jsonschema upgrade to 4.0.0, this attribute is no longer maintained while descending into the spec. Presumably because jsonschema now relies on attrs.resolve.
This commit is contained in:
@@ -2,10 +2,11 @@
|
|||||||
Module containing all code related to json schema validation.
|
Module containing all code related to json schema validation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from jsonschema import Draft4Validator, RefResolver, _utils
|
from jsonschema import Draft4Validator, RefResolver
|
||||||
from jsonschema.exceptions import RefResolutionError, ValidationError # noqa
|
from jsonschema.exceptions import RefResolutionError, ValidationError # noqa
|
||||||
from jsonschema.validators import extend
|
from jsonschema.validators import extend
|
||||||
from openapi_spec_validator.handlers import UrlHandler
|
from openapi_spec_validator.handlers import UrlHandler
|
||||||
@@ -54,22 +55,16 @@ def resolve_refs(spec, store=None, handlers=None):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def validate_type(validator, types, instance, schema):
|
def allow_nullable(validation_fn: t.Callable) -> t.Callable:
|
||||||
|
"""Extend an existing validation function, so it allows nullable values to be null."""
|
||||||
|
|
||||||
|
def nullable_validation_fn(validator, to_validate, instance, schema):
|
||||||
if instance is None and (schema.get('x-nullable') is True or schema.get('nullable')):
|
if instance is None and (schema.get('x-nullable') is True or schema.get('nullable')):
|
||||||
return
|
return
|
||||||
|
|
||||||
types = _utils.ensure_list(types)
|
yield from validation_fn(validator, to_validate, instance, schema)
|
||||||
|
|
||||||
if not any(validator.is_type(instance, type) for type in types):
|
return nullable_validation_fn
|
||||||
yield ValidationError(_utils.types_msg(instance, types))
|
|
||||||
|
|
||||||
|
|
||||||
def validate_enum(validator, enums, instance, schema):
|
|
||||||
if instance is None and (schema.get('x-nullable') is True or schema.get('nullable')):
|
|
||||||
return
|
|
||||||
|
|
||||||
if instance not in enums:
|
|
||||||
yield ValidationError(f"{instance!r} is not one of {enums!r}")
|
|
||||||
|
|
||||||
|
|
||||||
def validate_required(validator, required, instance, schema):
|
def validate_required(validator, required, instance, schema):
|
||||||
@@ -100,14 +95,14 @@ def validate_writeOnly(validator, wo, instance, schema):
|
|||||||
|
|
||||||
|
|
||||||
Draft4RequestValidator = extend(Draft4Validator, {
|
Draft4RequestValidator = extend(Draft4Validator, {
|
||||||
'type': validate_type,
|
'type': allow_nullable(Draft4Validator.VALIDATORS['type']),
|
||||||
'enum': validate_enum,
|
'enum': allow_nullable(Draft4Validator.VALIDATORS['enum']),
|
||||||
'required': validate_required,
|
'required': validate_required,
|
||||||
'readOnly': validate_readOnly})
|
'readOnly': validate_readOnly})
|
||||||
|
|
||||||
Draft4ResponseValidator = extend(Draft4Validator, {
|
Draft4ResponseValidator = extend(Draft4Validator, {
|
||||||
'type': validate_type,
|
'type': allow_nullable(Draft4Validator.VALIDATORS['type']),
|
||||||
'enum': validate_enum,
|
'enum': allow_nullable(Draft4Validator.VALIDATORS['enum']),
|
||||||
'required': validate_required,
|
'required': validate_required,
|
||||||
'writeOnly': validate_writeOnly,
|
'writeOnly': validate_writeOnly,
|
||||||
'x-writeOnly': validate_writeOnly})
|
'x-writeOnly': validate_writeOnly})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import abc
|
|||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import typing as t
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
@@ -24,33 +25,37 @@ from .utils import deep_get
|
|||||||
validate_properties = Draft4Validator.VALIDATORS["properties"]
|
validate_properties = Draft4Validator.VALIDATORS["properties"]
|
||||||
|
|
||||||
|
|
||||||
def validate_defaults(validator, properties, instance, schema):
|
def create_validate_default_fn(instance_validator: Draft4Validator) -> t.Callable:
|
||||||
|
"""Creates a validation function for property defaults. This validation function will be used
|
||||||
|
by a validator that validates an openapi spec against the openapi schema. The default value
|
||||||
|
however needs to be validated against the openapi spec itself.
|
||||||
|
|
||||||
|
:param instance_validator: A validator to validate defaults against the openapi spec itself
|
||||||
|
instead of against the openapi schema.
|
||||||
|
|
||||||
|
:return: A validation function for property defaults using the passed in instance_validator
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def validate_defaults(validator, properties, instance, schema):
|
||||||
"""Validate `properties` subschema.
|
"""Validate `properties` subschema.
|
||||||
|
|
||||||
Enforcing each default value validates against the schema in which it resides.
|
Enforcing each default value validates against the schema in which it resides.
|
||||||
"""
|
"""
|
||||||
valid = True
|
valid = True
|
||||||
for error in validate_properties(
|
for error in validate_properties(validator, properties, instance, schema):
|
||||||
validator, properties, instance, schema,
|
|
||||||
):
|
|
||||||
valid = False
|
valid = False
|
||||||
yield error
|
yield error
|
||||||
|
|
||||||
# Validate default only when the subschema has validated successfully
|
# Validate default only when the subschema has validated successfully
|
||||||
# and only when an instance validator is available.
|
if not valid:
|
||||||
if not valid or not hasattr(validator, 'instance_validator'):
|
|
||||||
return
|
return
|
||||||
if isinstance(instance, dict) and 'default' in instance:
|
if isinstance(instance, dict) and 'default' in instance:
|
||||||
for error in validator.instance_validator.iter_errors(
|
for error in instance_validator.iter_errors(instance['default'], instance):
|
||||||
instance['default'],
|
|
||||||
instance
|
|
||||||
):
|
|
||||||
yield error
|
yield error
|
||||||
|
|
||||||
|
return validate_defaults
|
||||||
|
|
||||||
OpenApiValidator = extend_validator(
|
|
||||||
Draft4Validator, {"properties": validate_defaults},
|
|
||||||
)
|
|
||||||
|
|
||||||
NO_SPEC_VERSION_ERR_MSG = """Unable to get the spec version.
|
NO_SPEC_VERSION_ERR_MSG = """Unable to get the spec version.
|
||||||
You are missing either '"swagger": "2.0"' or '"openapi": "3.0.0"'
|
You are missing either '"swagger": "2.0"' or '"openapi": "3.0.0"'
|
||||||
@@ -83,8 +88,10 @@ class Specification(Mapping):
|
|||||||
""" validate spec against schema
|
""" validate spec against schema
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
instance_validator = Draft4Validator(spec)
|
||||||
|
validate_defaults = create_validate_default_fn(instance_validator)
|
||||||
|
OpenApiValidator = extend_validator(Draft4Validator, {"properties": validate_defaults})
|
||||||
validator = OpenApiValidator(cls.openapi_schema)
|
validator = OpenApiValidator(cls.openapi_schema)
|
||||||
validator.instance_validator = Draft4Validator(spec)
|
|
||||||
validator.validate(spec)
|
validator.validate(spec)
|
||||||
except jsonschema.exceptions.ValidationError as e:
|
except jsonschema.exceptions.ValidationError as e:
|
||||||
raise InvalidSpecification.create_from(e)
|
raise InvalidSpecification.create_from(e)
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -21,7 +21,7 @@ version = read_version('connexion')
|
|||||||
|
|
||||||
install_requires = [
|
install_requires = [
|
||||||
'clickclick>=1.2,<21',
|
'clickclick>=1.2,<21',
|
||||||
'jsonschema>=2.5.1,<4',
|
'jsonschema>=2.5.1,<5',
|
||||||
'PyYAML>=5.1,<6',
|
'PyYAML>=5.1,<6',
|
||||||
'requests>=2.9.1,<3',
|
'requests>=2.9.1,<3',
|
||||||
'inflection>=0.3.1,<0.6',
|
'inflection>=0.3.1,<0.6',
|
||||||
|
|||||||
Reference in New Issue
Block a user