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 *.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 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', [])
|
||||
|
||||
Reference in New Issue
Block a user