mirror of
https://github.com/LukeHagar/connexion.git
synced 2025-12-09 20:37:46 +00:00
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:
@@ -1,2 +1,3 @@
|
|||||||
include *.txt
|
include *.txt
|
||||||
include *.rst
|
include *.rst
|
||||||
|
recursive-include connexion/resources *.json
|
||||||
|
|||||||
1591
connexion/resources/schemas/v2.0/schema.json
Normal file
1591
connexion/resources/schemas/v2.0/schema.json
Normal file
File diff suppressed because it is too large
Load Diff
1654
connexion/resources/schemas/v3.0/schema.json
Normal file
1654
connexion/resources/schemas/v3.0/schema.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,19 +4,54 @@ This module defines Python interfaces for OpenAPI specifications.
|
|||||||
|
|
||||||
import abc
|
import abc
|
||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
import jsonschema
|
||||||
|
import pkg_resources
|
||||||
import yaml
|
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 .exceptions import InvalidSpecification
|
||||||
from .json_schema import resolve_refs
|
from .json_schema import resolve_refs
|
||||||
from .operations import OpenAPIOperation, Swagger2Operation
|
from .operations import OpenAPIOperation, Swagger2Operation
|
||||||
from .utils import deep_get
|
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.
|
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"'
|
||||||
from the top level of your spec."""
|
from the top level of your spec."""
|
||||||
@@ -44,10 +79,15 @@ class Specification(Mapping):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abc.abstractmethod
|
|
||||||
def _validate_spec(cls, spec):
|
def _validate_spec(cls, spec):
|
||||||
""" validate spec against schema
|
""" 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):
|
def get_path_params(self, path):
|
||||||
return deep_get(self._spec, ["paths", path]).get("parameters", [])
|
return deep_get(self._spec, ["paths", path]).get("parameters", [])
|
||||||
@@ -164,6 +204,9 @@ class Swagger2Specification(Specification):
|
|||||||
yaml_name = 'swagger.yaml'
|
yaml_name = 'swagger.yaml'
|
||||||
operation_cls = Swagger2Operation
|
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
|
@classmethod
|
||||||
def _set_defaults(cls, spec):
|
def _set_defaults(cls, spec):
|
||||||
spec.setdefault('produces', [])
|
spec.setdefault('produces', [])
|
||||||
@@ -206,14 +249,6 @@ class Swagger2Specification(Specification):
|
|||||||
self._raw_spec['basePath'] = base_path
|
self._raw_spec['basePath'] = base_path
|
||||||
self._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):
|
class OpenAPISpecification(Specification):
|
||||||
"""Python interface for an OpenAPI 3 specification."""
|
"""Python interface for an OpenAPI 3 specification."""
|
||||||
@@ -221,6 +256,9 @@ class OpenAPISpecification(Specification):
|
|||||||
yaml_name = 'openapi.yaml'
|
yaml_name = 'openapi.yaml'
|
||||||
operation_cls = OpenAPIOperation
|
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
|
@classmethod
|
||||||
def _set_defaults(cls, spec):
|
def _set_defaults(cls, spec):
|
||||||
spec.setdefault('components', {})
|
spec.setdefault('components', {})
|
||||||
@@ -233,14 +271,6 @@ class OpenAPISpecification(Specification):
|
|||||||
def components(self):
|
def components(self):
|
||||||
return self._spec['components']
|
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
|
@property
|
||||||
def base_path(self):
|
def base_path(self):
|
||||||
servers = self._spec.get('servers', [])
|
servers = self._spec.get('servers', [])
|
||||||
|
|||||||
Reference in New Issue
Block a user