Use jsonschema for validation (#936)

* jsonschema validation

* doh, exceptions should be raised

* internalize OpenAPI specs

from 6d17b631ff

* enforce that each default value validates against the schema in which it resides

* Fix import order

* Remove unused imports

* Move _validate_spec() to Specification base class

Co-authored-by: Patrick Wang <patrickkwang@users.noreply.github.com>
This commit is contained in:
Patrick Wang
2021-07-31 16:47:09 -04:00
committed by GitHub
parent 2229831e80
commit e3e851dd31
4 changed files with 3294 additions and 18 deletions

View File

@@ -1,2 +1,3 @@
include *.txt
include *.rst
recursive-include connexion/resources *.json

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,19 +4,54 @@ This module defines Python interfaces for OpenAPI specifications.
import abc
import copy
import json
import pathlib
from collections.abc import Mapping
from urllib.parse import urlsplit
import jinja2
import jsonschema
import pkg_resources
import yaml
from openapi_spec_validator.exceptions import OpenAPIValidationError
from jsonschema import Draft4Validator
from jsonschema.validators import extend as extend_validator
from .exceptions import InvalidSpecification
from .json_schema import resolve_refs
from .operations import OpenAPIOperation, Swagger2Operation
from .utils import deep_get
validate_properties = Draft4Validator.VALIDATORS["properties"]
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
# 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
):
yield error
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"'
from the top level of your spec."""
@@ -44,10 +79,15 @@ class Specification(Mapping):
"""
@classmethod
@abc.abstractmethod
def _validate_spec(cls, spec):
""" validate spec against schema
"""
try:
validator = OpenApiValidator(cls.openapi_schema)
validator.instance_validator = Draft4Validator(spec)
validator.validate(spec)
except jsonschema.exceptions.ValidationError as e:
raise InvalidSpecification.create_from(e)
def get_path_params(self, path):
return deep_get(self._spec, ["paths", path]).get("parameters", [])
@@ -164,6 +204,9 @@ class Swagger2Specification(Specification):
yaml_name = 'swagger.yaml'
operation_cls = Swagger2Operation
schema_string = pkg_resources.resource_string('connexion', 'resources/schemas/v2.0/schema.json')
openapi_schema = json.loads(schema_string.decode('utf-8'))
@classmethod
def _set_defaults(cls, spec):
spec.setdefault('produces', [])
@@ -206,14 +249,6 @@ class Swagger2Specification(Specification):
self._raw_spec['basePath'] = base_path
self._spec['basePath'] = base_path
@classmethod
def _validate_spec(cls, spec):
from openapi_spec_validator import validate_v2_spec as validate_spec
try:
validate_spec(spec)
except OpenAPIValidationError as e:
raise InvalidSpecification.create_from(e)
class OpenAPISpecification(Specification):
"""Python interface for an OpenAPI 3 specification."""
@@ -221,6 +256,9 @@ class OpenAPISpecification(Specification):
yaml_name = 'openapi.yaml'
operation_cls = OpenAPIOperation
schema_string = pkg_resources.resource_string('connexion', 'resources/schemas/v3.0/schema.json')
openapi_schema = json.loads(schema_string.decode('utf-8'))
@classmethod
def _set_defaults(cls, spec):
spec.setdefault('components', {})
@@ -233,14 +271,6 @@ class OpenAPISpecification(Specification):
def components(self):
return self._spec['components']
@classmethod
def _validate_spec(cls, spec):
from openapi_spec_validator import validate_v3_spec as validate_spec
try:
validate_spec(spec)
except OpenAPIValidationError as e:
raise InvalidSpecification.create_from(e)
@property
def base_path(self):
servers = self._spec.get('servers', [])