Skip to content

Commit

Permalink
Bump upperbound version for jsonschema to 5.0.0 (#1447)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
RobbeSneyders committed Jan 29, 2022
1 parent 425d5d5 commit 86af42d
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 42 deletions.
31 changes: 13 additions & 18 deletions connexion/json_schema.py
Expand Up @@ -2,10 +2,11 @@
Module containing all code related to json schema validation.
"""

import typing as t
from collections.abc import Mapping
from copy import deepcopy

from jsonschema import Draft4Validator, RefResolver, _utils
from jsonschema import Draft4Validator, RefResolver
from jsonschema.exceptions import RefResolutionError, ValidationError # noqa
from jsonschema.validators import extend
from openapi_spec_validator.handlers import UrlHandler
Expand Down Expand Up @@ -54,22 +55,16 @@ def _do_resolve(node):
return res


def validate_type(validator, types, instance, schema):
if instance is None and (schema.get('x-nullable') is True or schema.get('nullable')):
return

types = _utils.ensure_list(types)
def allow_nullable(validation_fn: t.Callable) -> t.Callable:
"""Extend an existing validation function, so it allows nullable values to be null."""

if not any(validator.is_type(instance, type) for type in types):
yield ValidationError(_utils.types_msg(instance, types))
def nullable_validation_fn(validator, to_validate, instance, schema):
if instance is None and (schema.get('x-nullable') is True or schema.get('nullable')):
return

yield from validation_fn(validator, to_validate, instance, schema)

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}")
return nullable_validation_fn


def validate_required(validator, required, instance, schema):
Expand Down Expand Up @@ -100,14 +95,14 @@ def validate_writeOnly(validator, wo, instance, schema):


Draft4RequestValidator = extend(Draft4Validator, {
'type': validate_type,
'enum': validate_enum,
'type': allow_nullable(Draft4Validator.VALIDATORS['type']),
'enum': allow_nullable(Draft4Validator.VALIDATORS['enum']),
'required': validate_required,
'readOnly': validate_readOnly})

Draft4ResponseValidator = extend(Draft4Validator, {
'type': validate_type,
'enum': validate_enum,
'type': allow_nullable(Draft4Validator.VALIDATORS['type']),
'enum': allow_nullable(Draft4Validator.VALIDATORS['enum']),
'required': validate_required,
'writeOnly': validate_writeOnly,
'x-writeOnly': validate_writeOnly})
53 changes: 30 additions & 23 deletions connexion/spec.py
Expand Up @@ -6,6 +6,7 @@
import copy
import json
import pathlib
import typing as t
from collections.abc import Mapping
from urllib.parse import urlsplit

Expand All @@ -24,33 +25,37 @@
validate_properties = Draft4Validator.VALIDATORS["properties"]


def validate_defaults(validator, properties, instance, schema):
"""Validate `properties` subschema.
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
Enforcing each default value validates against the schema in which it resides.
"""
valid = True
for error in validate_properties(
validator, properties, instance, schema,
):
valid = False
yield error

# Validate default only when the subschema has validated successfully
# and only when an instance validator is available.
if not valid or not hasattr(validator, 'instance_validator'):
return
if isinstance(instance, dict) and 'default' in instance:
for error in validator.instance_validator.iter_errors(
instance['default'],
instance
):

def validate_defaults(validator, properties, instance, schema):
"""Validate `properties` subschema.
Enforcing each default value validates against the schema in which it resides.
"""
valid = True
for error in validate_properties(validator, properties, instance, schema):
valid = False
yield error

# Validate default only when the subschema has validated successfully
if not valid:
return
if isinstance(instance, dict) and 'default' in instance:
for error in instance_validator.iter_errors(instance['default'], instance):
yield error

return validate_defaults

OpenApiValidator = extend_validator(
Draft4Validator, {"properties": validate_defaults},
)

NO_SPEC_VERSION_ERR_MSG = """Unable to get the spec version.
You are missing either '"swagger": "2.0"' or '"openapi": "3.0.0"'
Expand Down Expand Up @@ -83,8 +88,10 @@ def _validate_spec(cls, spec):
""" validate spec against schema
"""
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.instance_validator = Draft4Validator(spec)
validator.validate(spec)
except jsonschema.exceptions.ValidationError as e:
raise InvalidSpecification.create_from(e)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -21,7 +21,7 @@ def read_version(package):

install_requires = [
'clickclick>=1.2,<21',
'jsonschema>=2.5.1,<4',
'jsonschema>=2.5.1,<5',
'PyYAML>=5.1,<6',
'requests>=2.9.1,<3',
'inflection>=0.3.1,<0.6',
Expand Down

0 comments on commit 86af42d

Please sign in to comment.