mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-22 19:18:36 +00:00
commit
5d9a671ca1
83 changed files with 1236 additions and 1070 deletions
|
@ -1,77 +0,0 @@
|
|||
"""OpenAPI core exceptions module"""
|
||||
|
||||
|
||||
class OpenAPIError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class OpenAPIMappingError(OpenAPIError):
|
||||
pass
|
||||
|
||||
|
||||
class OpenAPIServerError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class OpenAPIOperationError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidValueType(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class OpenAPIParameterError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class OpenAPIBodyError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidServer(OpenAPIServerError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidOperation(OpenAPIOperationError):
|
||||
pass
|
||||
|
||||
|
||||
class EmptyValue(OpenAPIParameterError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingParameter(OpenAPIParameterError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidParameterValue(OpenAPIParameterError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingBody(OpenAPIBodyError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidMediaTypeValue(OpenAPIBodyError):
|
||||
pass
|
||||
|
||||
|
||||
class UndefinedSchemaProperty(OpenAPIBodyError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingProperty(OpenAPIBodyError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidContentType(OpenAPIBodyError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidResponse(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidValue(OpenAPIMappingError):
|
||||
pass
|
1
openapi_core/extensions/__init__.py
Normal file
1
openapi_core/extensions/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""OpenAPI extensions package"""
|
1
openapi_core/extensions/models/__init__.py
Normal file
1
openapi_core/extensions/models/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""OpenAPI X-Model extension package"""
|
12
openapi_core/extensions/models/factories.py
Normal file
12
openapi_core/extensions/models/factories.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""OpenAPI X-Model extension factories module"""
|
||||
from openapi_core.extensions.models.models import BaseModel
|
||||
|
||||
|
||||
class ModelFactory(object):
|
||||
|
||||
def create(self, properties, name=None):
|
||||
model = BaseModel
|
||||
if name is not None:
|
||||
model = type(name, (BaseModel, ), {})
|
||||
|
||||
return model(**properties)
|
|
@ -1,8 +1,8 @@
|
|||
"""OpenAPI core models module"""
|
||||
"""OpenAPI X-Model extension models module"""
|
||||
|
||||
|
||||
class BaseModel(dict):
|
||||
"""Base class for OpenAPI models."""
|
||||
"""Base class for OpenAPI X-Model."""
|
||||
|
||||
def __getattr__(self, attr_name):
|
||||
"""Only search through properties if attribute not found normally.
|
||||
|
@ -15,13 +15,3 @@ class BaseModel(dict):
|
|||
'type object {0!r} has no attribute {1!r}'
|
||||
.format(type(self).__name__, attr_name)
|
||||
)
|
||||
|
||||
|
||||
class ModelFactory(object):
|
||||
|
||||
def create(self, properties, name=None):
|
||||
model = BaseModel
|
||||
if name is not None:
|
||||
model = type(name, (BaseModel, ), {})
|
||||
|
||||
return model(**properties)
|
|
@ -1,149 +0,0 @@
|
|||
"""OpenAPI core parameters module"""
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from functools import lru_cache
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.enums import ParameterLocation, ParameterStyle, SchemaType
|
||||
from openapi_core.exceptions import (
|
||||
EmptyValue, InvalidValueType, InvalidParameterValue,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PARAMETER_STYLE_DESERIALIZERS = {
|
||||
ParameterStyle.FORM: lambda x: x.split(','),
|
||||
ParameterStyle.SIMPLE: lambda x: x.split(','),
|
||||
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
|
||||
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
|
||||
}
|
||||
|
||||
|
||||
class Parameter(object):
|
||||
"""Represents an OpenAPI operation Parameter."""
|
||||
|
||||
def __init__(
|
||||
self, name, location, schema=None, required=False,
|
||||
deprecated=False, allow_empty_value=False,
|
||||
items=None, style=None, explode=None):
|
||||
self.name = name
|
||||
self.location = ParameterLocation(location)
|
||||
self.schema = schema
|
||||
self.required = (
|
||||
True if self.location == ParameterLocation.PATH else required
|
||||
)
|
||||
self.deprecated = deprecated
|
||||
self.allow_empty_value = (
|
||||
allow_empty_value if self.location == ParameterLocation.QUERY
|
||||
else False
|
||||
)
|
||||
self.items = items
|
||||
self.style = ParameterStyle(style or self.default_style)
|
||||
self.explode = self.default_explode if explode is None else explode
|
||||
|
||||
@property
|
||||
def aslist(self):
|
||||
return (
|
||||
self.schema and
|
||||
self.schema.type in [SchemaType.ARRAY, SchemaType.OBJECT]
|
||||
)
|
||||
|
||||
@property
|
||||
def default_style(self):
|
||||
simple_locations = [ParameterLocation.PATH, ParameterLocation.HEADER]
|
||||
return (
|
||||
'simple' if self.location in simple_locations else "form"
|
||||
)
|
||||
|
||||
@property
|
||||
def default_explode(self):
|
||||
return self.style == ParameterStyle.FORM
|
||||
|
||||
def get_dererializer(self):
|
||||
return PARAMETER_STYLE_DESERIALIZERS[self.style]
|
||||
|
||||
def deserialize(self, value):
|
||||
if not self.aslist or self.explode:
|
||||
return value
|
||||
|
||||
deserializer = self.get_dererializer()
|
||||
return deserializer(value)
|
||||
|
||||
def unmarshal(self, value):
|
||||
if self.deprecated:
|
||||
warnings.warn(
|
||||
"{0} parameter is deprecated".format(self.name),
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if (self.location == ParameterLocation.QUERY and value == "" and
|
||||
not self.allow_empty_value):
|
||||
raise EmptyValue(
|
||||
"Value of {0} parameter cannot be empty".format(self.name))
|
||||
|
||||
if not self.schema:
|
||||
return value
|
||||
|
||||
deserialized = self.deserialize(value)
|
||||
|
||||
try:
|
||||
return self.schema.unmarshal(deserialized)
|
||||
except InvalidValueType as exc:
|
||||
raise InvalidParameterValue(str(exc))
|
||||
|
||||
|
||||
class ParameterFactory(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def create(self, parameter_spec, parameter_name=None):
|
||||
parameter_deref = self.dereferencer.dereference(parameter_spec)
|
||||
|
||||
parameter_name = parameter_name or parameter_deref['name']
|
||||
parameter_in = parameter_deref.get('in', 'header')
|
||||
|
||||
allow_empty_value = parameter_deref.get('allowEmptyValue')
|
||||
required = parameter_deref.get('required', False)
|
||||
|
||||
style = parameter_deref.get('style')
|
||||
explode = parameter_deref.get('explode')
|
||||
|
||||
schema_spec = parameter_deref.get('schema', None)
|
||||
schema = None
|
||||
if schema_spec:
|
||||
schema, _ = self.schemas_registry.get_or_create(schema_spec)
|
||||
|
||||
return Parameter(
|
||||
parameter_name, parameter_in,
|
||||
schema=schema, required=required,
|
||||
allow_empty_value=allow_empty_value,
|
||||
style=style, explode=explode,
|
||||
)
|
||||
|
||||
|
||||
class ParametersGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def generate(self, parameters):
|
||||
for parameter_name, parameter_spec in iteritems(parameters):
|
||||
parameter = self.parameter_factory.create(
|
||||
parameter_spec, parameter_name=parameter_name)
|
||||
|
||||
yield (parameter_name, parameter)
|
||||
|
||||
def generate_from_list(self, parameters_list):
|
||||
for parameter_spec in parameters_list:
|
||||
parameter = self.parameter_factory.create(parameter_spec)
|
||||
|
||||
yield (parameter.name, parameter)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def parameter_factory(self):
|
||||
return ParameterFactory(self.dereferencer, self.schemas_registry)
|
0
openapi_core/schema/__init__.py
Normal file
0
openapi_core/schema/__init__.py
Normal file
0
openapi_core/schema/components/__init__.py
Normal file
0
openapi_core/schema/components/__init__.py
Normal file
|
@ -1,18 +1,7 @@
|
|||
from functools import lru_cache
|
||||
|
||||
from openapi_core.schemas import SchemasGenerator
|
||||
|
||||
|
||||
class Components(object):
|
||||
"""Represents an OpenAPI Components in a service."""
|
||||
|
||||
def __init__(
|
||||
self, schemas=None, responses=None, parameters=None,
|
||||
request_bodies=None):
|
||||
self.schemas = schemas and dict(schemas) or {}
|
||||
self.responses = responses and dict(responses) or {}
|
||||
self.parameters = parameters and dict(parameters) or {}
|
||||
self.request_bodies = request_bodies and dict(request_bodies) or {}
|
||||
from openapi_core.schema.components.models import Components
|
||||
from openapi_core.schema.schemas.generators import SchemasGenerator
|
||||
|
||||
|
||||
class ComponentsFactory(object):
|
10
openapi_core/schema/components/models.py
Normal file
10
openapi_core/schema/components/models.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
class Components(object):
|
||||
"""Represents an OpenAPI Components in a service."""
|
||||
|
||||
def __init__(
|
||||
self, schemas=None, responses=None, parameters=None,
|
||||
request_bodies=None):
|
||||
self.schemas = schemas and dict(schemas) or {}
|
||||
self.responses = responses and dict(responses) or {}
|
||||
self.parameters = parameters and dict(parameters) or {}
|
||||
self.request_bodies = request_bodies and dict(request_bodies) or {}
|
9
openapi_core/schema/exceptions.py
Normal file
9
openapi_core/schema/exceptions.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""OpenAPI core schema exceptions module"""
|
||||
|
||||
|
||||
class OpenAPIError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class OpenAPIMappingError(OpenAPIError):
|
||||
pass
|
0
openapi_core/schema/infos/__init__.py
Normal file
0
openapi_core/schema/infos/__init__.py
Normal file
|
@ -1,8 +1,5 @@
|
|||
class Info(object):
|
||||
|
||||
def __init__(self, title, version):
|
||||
self.title = title
|
||||
self.version = version
|
||||
"""OpenAPI core infos factories module"""
|
||||
from openapi_core.schema.infos.models import Info
|
||||
|
||||
|
||||
class InfoFactory(object):
|
8
openapi_core/schema/infos/models.py
Normal file
8
openapi_core/schema/infos/models.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
"""OpenAPI core infos models module"""
|
||||
|
||||
|
||||
class Info(object):
|
||||
|
||||
def __init__(self, title, version):
|
||||
self.title = title
|
||||
self.version = version
|
0
openapi_core/schema/media_types/__init__.py
Normal file
0
openapi_core/schema/media_types/__init__.py
Normal file
13
openapi_core/schema/media_types/exceptions.py
Normal file
13
openapi_core/schema/media_types/exceptions.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPIMediaTypeError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidMediaTypeValue(OpenAPIMediaTypeError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidContentType(OpenAPIMediaTypeError):
|
||||
pass
|
21
openapi_core/schema/media_types/generators.py
Normal file
21
openapi_core/schema/media_types/generators.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
"""OpenAPI core media types generators module"""
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
|
||||
|
||||
class MediaTypeGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def generate(self, content):
|
||||
for mimetype, media_type in iteritems(content):
|
||||
schema_spec = media_type.get('schema')
|
||||
|
||||
schema = None
|
||||
if schema_spec:
|
||||
schema, _ = self.schemas_registry.get_or_create(schema_spec)
|
||||
|
||||
yield mimetype, MediaType(mimetype, schema)
|
|
@ -1,10 +1,10 @@
|
|||
"""OpenAPI core mediaTypes module"""
|
||||
"""OpenAPI core media types models module"""
|
||||
from collections import defaultdict
|
||||
|
||||
from json import loads
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.exceptions import InvalidValueType, InvalidMediaTypeValue
|
||||
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
|
||||
from openapi_core.schema.schemas.exceptions import InvalidSchemaValue
|
||||
|
||||
|
||||
MEDIA_TYPE_DESERIALIZERS = {
|
||||
|
@ -42,22 +42,5 @@ class MediaType(object):
|
|||
|
||||
try:
|
||||
return self.schema.unmarshal(deserialized)
|
||||
except InvalidValueType as exc:
|
||||
except InvalidSchemaValue as exc:
|
||||
raise InvalidMediaTypeValue(str(exc))
|
||||
|
||||
|
||||
class MediaTypeGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def generate(self, content):
|
||||
for mimetype, media_type in iteritems(content):
|
||||
schema_spec = media_type.get('schema')
|
||||
|
||||
schema = None
|
||||
if schema_spec:
|
||||
schema, _ = self.schemas_registry.get_or_create(schema_spec)
|
||||
|
||||
yield mimetype, MediaType(mimetype, schema)
|
0
openapi_core/schema/operations/__init__.py
Normal file
0
openapi_core/schema/operations/__init__.py
Normal file
9
openapi_core/schema/operations/exceptions.py
Normal file
9
openapi_core/schema/operations/exceptions.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPIOperationError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidOperation(OpenAPIOperationError):
|
||||
pass
|
|
@ -1,50 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""OpenAPI core operations module"""
|
||||
import logging
|
||||
"""OpenAPI core operations models module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from six import iteritems
|
||||
from openapi_spec_validator.validators import PathItemValidator
|
||||
|
||||
from openapi_core.exceptions import InvalidResponse
|
||||
from openapi_core.parameters import ParametersGenerator
|
||||
from openapi_core.request_bodies import RequestBodyFactory
|
||||
from openapi_core.responses import ResponsesGenerator
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Operation(object):
|
||||
"""Represents an OpenAPI Operation."""
|
||||
|
||||
def __init__(
|
||||
self, http_method, path_name, responses, parameters,
|
||||
request_body=None, deprecated=False, operation_id=None):
|
||||
self.http_method = http_method
|
||||
self.path_name = path_name
|
||||
self.responses = dict(responses)
|
||||
self.parameters = dict(parameters)
|
||||
self.request_body = request_body
|
||||
self.deprecated = deprecated
|
||||
self.operation_id = operation_id
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.parameters[name]
|
||||
|
||||
def get_response(self, http_status='default'):
|
||||
try:
|
||||
return self.responses[http_status]
|
||||
except KeyError:
|
||||
# try range
|
||||
http_status_range = '{0}XX'.format(http_status[0])
|
||||
if http_status_range in self.responses:
|
||||
return self.responses[http_status_range]
|
||||
|
||||
if 'default' not in self.responses:
|
||||
raise InvalidResponse(
|
||||
"Unknown response http status {0}".format(http_status))
|
||||
|
||||
return self.responses['default']
|
||||
from openapi_core.schema.operations.models import Operation
|
||||
from openapi_core.schema.parameters.generators import ParametersGenerator
|
||||
from openapi_core.schema.request_bodies.factories import RequestBodyFactory
|
||||
from openapi_core.schema.responses.generators import ResponsesGenerator
|
||||
|
||||
|
||||
class OperationsGenerator(object):
|
37
openapi_core/schema/operations/models.py
Normal file
37
openapi_core/schema/operations/models.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""OpenAPI core operations models module"""
|
||||
from openapi_core.schema.responses.exceptions import InvalidResponse
|
||||
|
||||
|
||||
class Operation(object):
|
||||
"""Represents an OpenAPI Operation."""
|
||||
|
||||
def __init__(
|
||||
self, http_method, path_name, responses, parameters,
|
||||
request_body=None, deprecated=False, operation_id=None):
|
||||
self.http_method = http_method
|
||||
self.path_name = path_name
|
||||
self.responses = dict(responses)
|
||||
self.parameters = dict(parameters)
|
||||
self.request_body = request_body
|
||||
self.deprecated = deprecated
|
||||
self.operation_id = operation_id
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.parameters[name]
|
||||
|
||||
def get_response(self, http_status='default'):
|
||||
# @todo: move to Responses object
|
||||
try:
|
||||
return self.responses[http_status]
|
||||
except KeyError:
|
||||
# try range
|
||||
http_status_range = '{0}XX'.format(http_status[0])
|
||||
if http_status_range in self.responses:
|
||||
return self.responses[http_status_range]
|
||||
|
||||
if 'default' not in self.responses:
|
||||
raise InvalidResponse(
|
||||
"Unknown response http status {0}".format(http_status))
|
||||
|
||||
return self.responses['default']
|
0
openapi_core/schema/parameters/__init__.py
Normal file
0
openapi_core/schema/parameters/__init__.py
Normal file
|
@ -1,3 +1,4 @@
|
|||
"""OpenAPI core parameters enums module"""
|
||||
from enum import Enum
|
||||
|
||||
|
||||
|
@ -22,27 +23,3 @@ class ParameterStyle(Enum):
|
|||
SPACE_DELIMITED = 'spaceDelimited'
|
||||
PIPE_DELIMITED = 'pipeDelimited'
|
||||
DEEP_OBJECT = 'deepObject'
|
||||
|
||||
|
||||
class SchemaType(Enum):
|
||||
|
||||
INTEGER = 'integer'
|
||||
NUMBER = 'number'
|
||||
STRING = 'string'
|
||||
BOOLEAN = 'boolean'
|
||||
ARRAY = 'array'
|
||||
OBJECT = 'object'
|
||||
|
||||
|
||||
class SchemaFormat(Enum):
|
||||
|
||||
NONE = None
|
||||
INT32 = 'int32'
|
||||
INT64 = 'int64'
|
||||
FLOAT = 'float'
|
||||
DOUBLE = 'double'
|
||||
BYTE = 'byte'
|
||||
BINARY = 'binary'
|
||||
DATE = 'date'
|
||||
DATETIME = 'date-time'
|
||||
PASSWORD = 'password'
|
21
openapi_core/schema/parameters/exceptions.py
Normal file
21
openapi_core/schema/parameters/exceptions.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPIParameterError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingParameter(OpenAPIParameterError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingRequiredParameter(OpenAPIParameterError):
|
||||
pass
|
||||
|
||||
|
||||
class EmptyParameterValue(OpenAPIParameterError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidParameterValue(OpenAPIParameterError):
|
||||
pass
|
33
openapi_core/schema/parameters/factories.py
Normal file
33
openapi_core/schema/parameters/factories.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""OpenAPI core parameters factories module"""
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
|
||||
|
||||
class ParameterFactory(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def create(self, parameter_spec, parameter_name=None):
|
||||
parameter_deref = self.dereferencer.dereference(parameter_spec)
|
||||
|
||||
parameter_name = parameter_name or parameter_deref['name']
|
||||
parameter_in = parameter_deref.get('in', 'header')
|
||||
|
||||
allow_empty_value = parameter_deref.get('allowEmptyValue')
|
||||
required = parameter_deref.get('required', False)
|
||||
|
||||
style = parameter_deref.get('style')
|
||||
explode = parameter_deref.get('explode')
|
||||
|
||||
schema_spec = parameter_deref.get('schema', None)
|
||||
schema = None
|
||||
if schema_spec:
|
||||
schema, _ = self.schemas_registry.get_or_create(schema_spec)
|
||||
|
||||
return Parameter(
|
||||
parameter_name, parameter_in,
|
||||
schema=schema, required=required,
|
||||
allow_empty_value=allow_empty_value,
|
||||
style=style, explode=explode,
|
||||
)
|
31
openapi_core/schema/parameters/generators.py
Normal file
31
openapi_core/schema/parameters/generators.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
"""OpenAPI core parameters generators module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.schema.parameters.factories import ParameterFactory
|
||||
|
||||
|
||||
class ParametersGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def generate(self, parameters):
|
||||
for parameter_name, parameter_spec in iteritems(parameters):
|
||||
parameter = self.parameter_factory.create(
|
||||
parameter_spec, parameter_name=parameter_name)
|
||||
|
||||
yield (parameter_name, parameter)
|
||||
|
||||
def generate_from_list(self, parameters_list):
|
||||
for parameter_spec in parameters_list:
|
||||
parameter = self.parameter_factory.create(parameter_spec)
|
||||
|
||||
yield (parameter.name, parameter)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def parameter_factory(self):
|
||||
return ParameterFactory(self.dereferencer, self.schemas_registry)
|
116
openapi_core/schema/parameters/models.py
Normal file
116
openapi_core/schema/parameters/models.py
Normal file
|
@ -0,0 +1,116 @@
|
|||
"""OpenAPI core parameters models module"""
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
from openapi_core.schema.parameters.enums import (
|
||||
ParameterLocation, ParameterStyle,
|
||||
)
|
||||
from openapi_core.schema.parameters.exceptions import (
|
||||
MissingRequiredParameter, MissingParameter, InvalidParameterValue,
|
||||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.schema.schemas.exceptions import InvalidSchemaValue
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Parameter(object):
|
||||
"""Represents an OpenAPI operation Parameter."""
|
||||
|
||||
PARAMETER_STYLE_DESERIALIZERS = {
|
||||
ParameterStyle.FORM: lambda x: x.split(','),
|
||||
ParameterStyle.SIMPLE: lambda x: x.split(','),
|
||||
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
|
||||
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self, name, location, schema=None, required=False,
|
||||
deprecated=False, allow_empty_value=False,
|
||||
items=None, style=None, explode=None):
|
||||
self.name = name
|
||||
self.location = ParameterLocation(location)
|
||||
self.schema = schema
|
||||
self.required = (
|
||||
True if self.location == ParameterLocation.PATH else required
|
||||
)
|
||||
self.deprecated = deprecated
|
||||
self.allow_empty_value = (
|
||||
allow_empty_value if self.location == ParameterLocation.QUERY
|
||||
else False
|
||||
)
|
||||
self.items = items
|
||||
self.style = ParameterStyle(style or self.default_style)
|
||||
self.explode = self.default_explode if explode is None else explode
|
||||
|
||||
@property
|
||||
def aslist(self):
|
||||
return (
|
||||
self.schema and
|
||||
self.schema.type in [SchemaType.ARRAY, SchemaType.OBJECT]
|
||||
)
|
||||
|
||||
@property
|
||||
def default_style(self):
|
||||
simple_locations = [ParameterLocation.PATH, ParameterLocation.HEADER]
|
||||
return (
|
||||
'simple' if self.location in simple_locations else "form"
|
||||
)
|
||||
|
||||
@property
|
||||
def default_explode(self):
|
||||
return self.style == ParameterStyle.FORM
|
||||
|
||||
def get_dererializer(self):
|
||||
return self.PARAMETER_STYLE_DESERIALIZERS[self.style]
|
||||
|
||||
def deserialize(self, value):
|
||||
if not self.aslist or self.explode:
|
||||
return value
|
||||
|
||||
deserializer = self.get_dererializer()
|
||||
return deserializer(value)
|
||||
|
||||
def get_value(self, request):
|
||||
location = request.parameters[self.location.value]
|
||||
|
||||
try:
|
||||
raw = location[self.name]
|
||||
except KeyError:
|
||||
if self.required:
|
||||
raise MissingRequiredParameter(
|
||||
"Missing required `{0}` parameter".format(self.name))
|
||||
|
||||
if not self.schema or self.schema.default is None:
|
||||
raise MissingParameter(
|
||||
"Missing `{0}` parameter".format(self.name))
|
||||
|
||||
raw = self.schema.default
|
||||
|
||||
if self.aslist and self.explode:
|
||||
return location.getlist(self.name)
|
||||
|
||||
return raw
|
||||
|
||||
def unmarshal(self, value):
|
||||
if self.deprecated:
|
||||
warnings.warn(
|
||||
"{0} parameter is deprecated".format(self.name),
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if (self.location == ParameterLocation.QUERY and value == "" and
|
||||
not self.allow_empty_value):
|
||||
raise EmptyParameterValue(
|
||||
"Value of {0} parameter cannot be empty".format(self.name))
|
||||
|
||||
if not self.schema:
|
||||
return value
|
||||
|
||||
deserialized = self.deserialize(value)
|
||||
|
||||
try:
|
||||
return self.schema.unmarshal(deserialized)
|
||||
except InvalidSchemaValue as exc:
|
||||
raise InvalidParameterValue(str(exc))
|
0
openapi_core/schema/paths/__init__.py
Normal file
0
openapi_core/schema/paths/__init__.py
Normal file
|
@ -1,20 +1,10 @@
|
|||
"""OpenAPI core paths module"""
|
||||
"""OpenAPI core paths generators module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.operations import OperationsGenerator
|
||||
|
||||
|
||||
class Path(object):
|
||||
"""Represents an OpenAPI Path."""
|
||||
|
||||
def __init__(self, name, operations):
|
||||
self.name = name
|
||||
self.operations = dict(operations)
|
||||
|
||||
def __getitem__(self, http_method):
|
||||
return self.operations[http_method]
|
||||
from openapi_core.schema.operations.generators import OperationsGenerator
|
||||
from openapi_core.schema.paths.models import Path
|
||||
|
||||
|
||||
class PathsGenerator(object):
|
12
openapi_core/schema/paths/models.py
Normal file
12
openapi_core/schema/paths/models.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""OpenAPI core paths models module"""
|
||||
|
||||
|
||||
class Path(object):
|
||||
"""Represents an OpenAPI Path."""
|
||||
|
||||
def __init__(self, name, operations):
|
||||
self.name = name
|
||||
self.operations = dict(operations)
|
||||
|
||||
def __getitem__(self, http_method):
|
||||
return self.operations[http_method]
|
0
openapi_core/schema/properties/__init__.py
Normal file
0
openapi_core/schema/properties/__init__.py
Normal file
17
openapi_core/schema/properties/generators.py
Normal file
17
openapi_core/schema/properties/generators.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
"""OpenAPI core properties generators module"""
|
||||
from six import iteritems
|
||||
|
||||
|
||||
class PropertiesGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
self.dereferencer = dereferencer
|
||||
|
||||
def generate(self, properties):
|
||||
for property_name, schema_spec in iteritems(properties):
|
||||
schema = self._create_schema(schema_spec)
|
||||
yield property_name, schema
|
||||
|
||||
def _create_schema(self, schema_spec):
|
||||
from openapi_core.schema.schemas.factories import SchemaFactory
|
||||
return SchemaFactory(self.dereferencer).create(schema_spec)
|
0
openapi_core/schema/request_bodies/__init__.py
Normal file
0
openapi_core/schema/request_bodies/__init__.py
Normal file
9
openapi_core/schema/request_bodies/exceptions.py
Normal file
9
openapi_core/schema/request_bodies/exceptions.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPIRequestBodyError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingRequestBody(OpenAPIRequestBodyError):
|
||||
pass
|
|
@ -1,23 +1,8 @@
|
|||
"""OpenAPI core requestBodies module"""
|
||||
"""OpenAPI core request bodies factories module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from openapi_core.exceptions import InvalidContentType
|
||||
from openapi_core.media_types import MediaTypeGenerator
|
||||
|
||||
|
||||
class RequestBody(object):
|
||||
"""Represents an OpenAPI RequestBody."""
|
||||
|
||||
def __init__(self, content, required=False):
|
||||
self.content = dict(content)
|
||||
self.required = required
|
||||
|
||||
def __getitem__(self, mimetype):
|
||||
try:
|
||||
return self.content[mimetype]
|
||||
except KeyError:
|
||||
raise InvalidContentType(
|
||||
"Invalid mime type `{0}`".format(mimetype))
|
||||
from openapi_core.schema.media_types.generators import MediaTypeGenerator
|
||||
from openapi_core.schema.request_bodies.models import RequestBody
|
||||
|
||||
|
||||
class RequestBodyFactory(object):
|
25
openapi_core/schema/request_bodies/models.py
Normal file
25
openapi_core/schema/request_bodies/models.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
"""OpenAPI core request bodies models module"""
|
||||
|
||||
from openapi_core.schema.media_types.exceptions import InvalidContentType
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
|
||||
|
||||
class RequestBody(object):
|
||||
"""Represents an OpenAPI RequestBody."""
|
||||
|
||||
def __init__(self, content, required=False):
|
||||
self.content = dict(content)
|
||||
self.required = required
|
||||
|
||||
def __getitem__(self, mimetype):
|
||||
try:
|
||||
return self.content[mimetype]
|
||||
except KeyError:
|
||||
raise InvalidContentType(
|
||||
"Invalid mime type `{0}`".format(mimetype))
|
||||
|
||||
def get_value(self, request):
|
||||
if not request.body and self.required:
|
||||
raise MissingRequestBody("Missing required request body")
|
||||
|
||||
return request.body
|
0
openapi_core/schema/responses/__init__.py
Normal file
0
openapi_core/schema/responses/__init__.py
Normal file
13
openapi_core/schema/responses/exceptions.py
Normal file
13
openapi_core/schema/responses/exceptions.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPIResponseError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidResponse(OpenAPIResponseError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingResponseContent(OpenAPIResponseError):
|
||||
pass
|
|
@ -1,30 +1,11 @@
|
|||
"""OpenAPI core responses module"""
|
||||
"""OpenAPI core responses generators module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.exceptions import InvalidContentType
|
||||
from openapi_core.media_types import MediaTypeGenerator
|
||||
from openapi_core.parameters import ParametersGenerator
|
||||
|
||||
|
||||
class Response(object):
|
||||
|
||||
def __init__(
|
||||
self, http_status, description, headers=None, content=None,
|
||||
links=None):
|
||||
self.http_status = http_status
|
||||
self.description = description
|
||||
self.headers = headers and dict(headers) or {}
|
||||
self.content = content and dict(content) or {}
|
||||
self.links = links and dict(links) or {}
|
||||
|
||||
def __getitem__(self, mimetype):
|
||||
try:
|
||||
return self.content[mimetype]
|
||||
except KeyError:
|
||||
raise InvalidContentType(
|
||||
"Invalid mime type `{0}`".format(mimetype))
|
||||
from openapi_core.schema.media_types.generators import MediaTypeGenerator
|
||||
from openapi_core.schema.parameters.generators import ParametersGenerator
|
||||
from openapi_core.schema.responses.models import Response
|
||||
|
||||
|
||||
class ResponsesGenerator(object):
|
28
openapi_core/schema/responses/models.py
Normal file
28
openapi_core/schema/responses/models.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
"""OpenAPI core responses models module"""
|
||||
from openapi_core.schema.media_types.exceptions import InvalidContentType
|
||||
from openapi_core.schema.responses.exceptions import MissingResponseContent
|
||||
|
||||
|
||||
class Response(object):
|
||||
|
||||
def __init__(
|
||||
self, http_status, description, headers=None, content=None,
|
||||
links=None):
|
||||
self.http_status = http_status
|
||||
self.description = description
|
||||
self.headers = headers and dict(headers) or {}
|
||||
self.content = content and dict(content) or {}
|
||||
self.links = links and dict(links) or {}
|
||||
|
||||
def __getitem__(self, mimetype):
|
||||
try:
|
||||
return self.content[mimetype]
|
||||
except KeyError:
|
||||
raise InvalidContentType(
|
||||
"Invalid mime type `{0}`".format(mimetype))
|
||||
|
||||
def get_value(self, response):
|
||||
if not response.data:
|
||||
raise MissingResponseContent("Missing response content")
|
||||
|
||||
return response.data
|
0
openapi_core/schema/schemas/__init__.py
Normal file
0
openapi_core/schema/schemas/__init__.py
Normal file
26
openapi_core/schema/schemas/enums.py
Normal file
26
openapi_core/schema/schemas/enums.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
"""OpenAPI core schemas enums module"""
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class SchemaType(Enum):
|
||||
|
||||
INTEGER = 'integer'
|
||||
NUMBER = 'number'
|
||||
STRING = 'string'
|
||||
BOOLEAN = 'boolean'
|
||||
ARRAY = 'array'
|
||||
OBJECT = 'object'
|
||||
|
||||
|
||||
class SchemaFormat(Enum):
|
||||
|
||||
NONE = None
|
||||
INT32 = 'int32'
|
||||
INT64 = 'int64'
|
||||
FLOAT = 'float'
|
||||
DOUBLE = 'double'
|
||||
BYTE = 'byte'
|
||||
BINARY = 'binary'
|
||||
DATE = 'date'
|
||||
DATETIME = 'date-time'
|
||||
PASSWORD = 'password'
|
17
openapi_core/schema/schemas/exceptions.py
Normal file
17
openapi_core/schema/schemas/exceptions.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPISchemaError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSchemaValue(OpenAPISchemaError):
|
||||
pass
|
||||
|
||||
|
||||
class UndefinedSchemaProperty(OpenAPISchemaError):
|
||||
pass
|
||||
|
||||
|
||||
class MissingSchemaProperty(OpenAPISchemaError):
|
||||
pass
|
56
openapi_core/schema/schemas/factories.py
Normal file
56
openapi_core/schema/schemas/factories.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
"""OpenAPI core schemas factories module"""
|
||||
import logging
|
||||
from functools import lru_cache
|
||||
|
||||
from openapi_core.schema.properties.generators import PropertiesGenerator
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SchemaFactory(object):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
self.dereferencer = dereferencer
|
||||
|
||||
def create(self, schema_spec):
|
||||
schema_deref = self.dereferencer.dereference(schema_spec)
|
||||
|
||||
schema_type = schema_deref.get('type', 'object')
|
||||
schema_format = schema_deref.get('format')
|
||||
model = schema_deref.get('x-model', None)
|
||||
required = schema_deref.get('required', False)
|
||||
default = schema_deref.get('default', None)
|
||||
properties_spec = schema_deref.get('properties', None)
|
||||
items_spec = schema_deref.get('items', None)
|
||||
nullable = schema_deref.get('nullable', False)
|
||||
enum = schema_deref.get('enum', None)
|
||||
deprecated = schema_deref.get('deprecated', False)
|
||||
all_of_spec = schema_deref.get('allOf', None)
|
||||
|
||||
properties = None
|
||||
if properties_spec:
|
||||
properties = self.properties_generator.generate(properties_spec)
|
||||
|
||||
all_of = []
|
||||
if all_of_spec:
|
||||
all_of = map(self.create, all_of_spec)
|
||||
|
||||
items = None
|
||||
if items_spec:
|
||||
items = self._create_items(items_spec)
|
||||
|
||||
return Schema(
|
||||
schema_type=schema_type, model=model, properties=properties,
|
||||
items=items, schema_format=schema_format, required=required,
|
||||
default=default, nullable=nullable, enum=enum,
|
||||
deprecated=deprecated, all_of=all_of,
|
||||
)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def properties_generator(self):
|
||||
return PropertiesGenerator(self.dereferencer)
|
||||
|
||||
def _create_items(self, items_spec):
|
||||
return self.create(items_spec)
|
20
openapi_core/schema/schemas/generators.py
Normal file
20
openapi_core/schema/schemas/generators.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
"""OpenAPI core schemas generators module"""
|
||||
import logging
|
||||
|
||||
from six import iteritems
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SchemasGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def generate(self, schemas_spec):
|
||||
schemas_deref = self.dereferencer.dereference(schemas_spec)
|
||||
|
||||
for schema_name, schema_spec in iteritems(schemas_deref):
|
||||
schema, _ = self.schemas_registry.get_or_create(schema_spec)
|
||||
yield schema_name, schema
|
|
@ -1,39 +1,29 @@
|
|||
"""OpenAPI core schemas module"""
|
||||
"""OpenAPI core schemas models module"""
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
import warnings
|
||||
|
||||
from distutils.util import strtobool
|
||||
from functools import lru_cache
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.enums import SchemaType, SchemaFormat
|
||||
from openapi_core.exceptions import (
|
||||
InvalidValueType, UndefinedSchemaProperty, MissingProperty, InvalidValue,
|
||||
from openapi_core.extensions.models.factories import ModelFactory
|
||||
from openapi_core.schema.schemas.enums import SchemaType, SchemaFormat
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty,
|
||||
)
|
||||
from openapi_core.models import ModelFactory
|
||||
from openapi_core.schema.schemas.util import forcebool
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def forcebool(val):
|
||||
if isinstance(val, str):
|
||||
val = strtobool(val)
|
||||
|
||||
return bool(val)
|
||||
|
||||
|
||||
DEFAULT_CAST_CALLABLE_GETTER = {
|
||||
SchemaType.INTEGER: int,
|
||||
SchemaType.NUMBER: float,
|
||||
SchemaType.BOOLEAN: forcebool,
|
||||
}
|
||||
|
||||
|
||||
class Schema(object):
|
||||
"""Represents an OpenAPI Schema."""
|
||||
|
||||
DEFAULT_CAST_CALLABLE_GETTER = {
|
||||
SchemaType.INTEGER: int,
|
||||
SchemaType.NUMBER: float,
|
||||
SchemaType.BOOLEAN: forcebool,
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self, schema_type=None, model=None, properties=None, items=None,
|
||||
schema_format=None, required=None, default=None, nullable=False,
|
||||
|
@ -72,7 +62,7 @@ class Schema(object):
|
|||
return required
|
||||
|
||||
def get_cast_mapping(self):
|
||||
mapping = DEFAULT_CAST_CALLABLE_GETTER.copy()
|
||||
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy()
|
||||
mapping.update({
|
||||
SchemaType.ARRAY: self._unmarshal_collection,
|
||||
SchemaType.OBJECT: self._unmarshal_object,
|
||||
|
@ -84,7 +74,7 @@ class Schema(object):
|
|||
"""Cast value to schema type"""
|
||||
if value is None:
|
||||
if not self.nullable:
|
||||
raise InvalidValueType("Null value for non-nullable schema")
|
||||
raise InvalidSchemaValue("Null value for non-nullable schema")
|
||||
return self.default
|
||||
|
||||
if self.type is None:
|
||||
|
@ -99,7 +89,7 @@ class Schema(object):
|
|||
try:
|
||||
return cast_callable(value)
|
||||
except ValueError:
|
||||
raise InvalidValueType(
|
||||
raise InvalidSchemaValue(
|
||||
"Failed to cast value of {0} to {1}".format(value, self.type)
|
||||
)
|
||||
|
||||
|
@ -114,7 +104,7 @@ class Schema(object):
|
|||
return None
|
||||
|
||||
if self.enum and casted not in self.enum:
|
||||
raise InvalidValue(
|
||||
raise InvalidSchemaValue(
|
||||
"Value of {0} not in enum choices: {1}".format(
|
||||
value, self.enum)
|
||||
)
|
||||
|
@ -126,7 +116,8 @@ class Schema(object):
|
|||
|
||||
def _unmarshal_object(self, value):
|
||||
if not isinstance(value, (dict, )):
|
||||
raise InvalidValueType("Value of {0} not an object".format(value))
|
||||
raise InvalidSchemaValue(
|
||||
"Value of {0} not an object".format(value))
|
||||
|
||||
all_properties = self.get_all_properties()
|
||||
all_required_properties = self.get_all_required_properties()
|
||||
|
@ -145,102 +136,10 @@ class Schema(object):
|
|||
prop_value = value[prop_name]
|
||||
except KeyError:
|
||||
if prop_name in all_required_properties:
|
||||
raise MissingProperty(
|
||||
raise MissingSchemaProperty(
|
||||
"Missing schema property {0}".format(prop_name))
|
||||
if not prop.nullable and not prop.default:
|
||||
continue
|
||||
prop_value = prop.default
|
||||
properties[prop_name] = prop.unmarshal(prop_value)
|
||||
return ModelFactory().create(properties, name=self.model)
|
||||
|
||||
|
||||
class PropertiesGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
self.dereferencer = dereferencer
|
||||
|
||||
def generate(self, properties):
|
||||
for property_name, schema_spec in iteritems(properties):
|
||||
schema = self._create_schema(schema_spec)
|
||||
yield property_name, schema
|
||||
|
||||
def _create_schema(self, schema_spec):
|
||||
return SchemaFactory(self.dereferencer).create(schema_spec)
|
||||
|
||||
|
||||
class SchemaFactory(object):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
self.dereferencer = dereferencer
|
||||
|
||||
def create(self, schema_spec):
|
||||
schema_deref = self.dereferencer.dereference(schema_spec)
|
||||
|
||||
schema_type = schema_deref.get('type', 'object')
|
||||
schema_format = schema_deref.get('format')
|
||||
model = schema_deref.get('x-model', None)
|
||||
required = schema_deref.get('required', False)
|
||||
default = schema_deref.get('default', None)
|
||||
properties_spec = schema_deref.get('properties', None)
|
||||
items_spec = schema_deref.get('items', None)
|
||||
nullable = schema_deref.get('nullable', False)
|
||||
enum = schema_deref.get('enum', None)
|
||||
deprecated = schema_deref.get('deprecated', False)
|
||||
all_of_spec = schema_deref.get('allOf', None)
|
||||
|
||||
properties = None
|
||||
if properties_spec:
|
||||
properties = self.properties_generator.generate(properties_spec)
|
||||
|
||||
all_of = []
|
||||
if all_of_spec:
|
||||
all_of = map(self.create, all_of_spec)
|
||||
|
||||
items = None
|
||||
if items_spec:
|
||||
items = self._create_items(items_spec)
|
||||
|
||||
return Schema(
|
||||
schema_type=schema_type, model=model, properties=properties,
|
||||
items=items, schema_format=schema_format, required=required,
|
||||
default=default, nullable=nullable, enum=enum,
|
||||
deprecated=deprecated, all_of=all_of,
|
||||
)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def properties_generator(self):
|
||||
return PropertiesGenerator(self.dereferencer)
|
||||
|
||||
def _create_items(self, items_spec):
|
||||
return self.create(items_spec)
|
||||
|
||||
|
||||
class SchemaRegistry(SchemaFactory):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
super(SchemaRegistry, self).__init__(dereferencer)
|
||||
self._schemas = {}
|
||||
|
||||
def get_or_create(self, schema_spec):
|
||||
schema_deref = self.dereferencer.dereference(schema_spec)
|
||||
model = schema_deref.get('x-model', None)
|
||||
|
||||
if model and model in self._schemas:
|
||||
return self._schemas[model], False
|
||||
|
||||
return self.create(schema_deref), True
|
||||
|
||||
|
||||
class SchemasGenerator(object):
|
||||
|
||||
def __init__(self, dereferencer, schemas_registry):
|
||||
self.dereferencer = dereferencer
|
||||
self.schemas_registry = schemas_registry
|
||||
|
||||
def generate(self, schemas_spec):
|
||||
schemas_deref = self.dereferencer.dereference(schemas_spec)
|
||||
|
||||
for schema_name, schema_spec in iteritems(schemas_deref):
|
||||
schema, _ = self.schemas_registry.get_or_create(schema_spec)
|
||||
yield schema_name, schema
|
22
openapi_core/schema/schemas/registries.py
Normal file
22
openapi_core/schema/schemas/registries.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
"""OpenAPI core schemas registries module"""
|
||||
import logging
|
||||
|
||||
from openapi_core.schema.schemas.factories import SchemaFactory
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SchemaRegistry(SchemaFactory):
|
||||
|
||||
def __init__(self, dereferencer):
|
||||
super(SchemaRegistry, self).__init__(dereferencer)
|
||||
self._schemas = {}
|
||||
|
||||
def get_or_create(self, schema_spec):
|
||||
schema_deref = self.dereferencer.dereference(schema_spec)
|
||||
model = schema_deref.get('x-model', None)
|
||||
|
||||
if model and model in self._schemas:
|
||||
return self._schemas[model], False
|
||||
|
||||
return self.create(schema_deref), True
|
9
openapi_core/schema/schemas/util.py
Normal file
9
openapi_core/schema/schemas/util.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""OpenAPI core schemas util module"""
|
||||
from distutils.util import strtobool
|
||||
|
||||
|
||||
def forcebool(val):
|
||||
if isinstance(val, str):
|
||||
val = strtobool(val)
|
||||
|
||||
return bool(val)
|
0
openapi_core/schema/servers/__init__.py
Normal file
0
openapi_core/schema/servers/__init__.py
Normal file
9
openapi_core/schema/servers/exceptions.py
Normal file
9
openapi_core/schema/servers/exceptions.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
|
||||
class OpenAPIServerError(OpenAPIMappingError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidServer(OpenAPIServerError):
|
||||
pass
|
|
@ -1,37 +1,9 @@
|
|||
"""OpenAPI core servers generators module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from six import iteritems
|
||||
|
||||
|
||||
class Server(object):
|
||||
|
||||
def __init__(self, url, variables=None):
|
||||
self.url = url
|
||||
self.variables = variables and dict(variables) or {}
|
||||
|
||||
@property
|
||||
def default_url(self):
|
||||
return self.get_url()
|
||||
|
||||
@property
|
||||
def default_variables(self):
|
||||
defaults = {}
|
||||
for name, variable in iteritems(self.variables):
|
||||
defaults[name] = variable.default
|
||||
return defaults
|
||||
|
||||
def get_url(self, **variables):
|
||||
if not variables:
|
||||
variables = self.default_variables
|
||||
return self.url.format(**variables)
|
||||
|
||||
|
||||
class ServerVariable(object):
|
||||
|
||||
def __init__(self, name, default, enum=None):
|
||||
self.name = name
|
||||
self.default = default
|
||||
self.enum = enum and list(enum) or []
|
||||
from openapi_core.schema.servers.models import Server, ServerVariable
|
||||
|
||||
|
||||
class ServersGenerator(object):
|
33
openapi_core/schema/servers/models.py
Normal file
33
openapi_core/schema/servers/models.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""OpenAPI core servers models module"""
|
||||
from six import iteritems
|
||||
|
||||
|
||||
class Server(object):
|
||||
|
||||
def __init__(self, url, variables=None):
|
||||
self.url = url
|
||||
self.variables = variables and dict(variables) or {}
|
||||
|
||||
@property
|
||||
def default_url(self):
|
||||
return self.get_url()
|
||||
|
||||
@property
|
||||
def default_variables(self):
|
||||
defaults = {}
|
||||
for name, variable in iteritems(self.variables):
|
||||
defaults[name] = variable.default
|
||||
return defaults
|
||||
|
||||
def get_url(self, **variables):
|
||||
if not variables:
|
||||
variables = self.default_variables
|
||||
return self.url.format(**variables)
|
||||
|
||||
|
||||
class ServerVariable(object):
|
||||
|
||||
def __init__(self, name, default, enum=None):
|
||||
self.name = name
|
||||
self.default = default
|
||||
self.enum = enum and list(enum) or []
|
0
openapi_core/schema/specs/__init__.py
Normal file
0
openapi_core/schema/specs/__init__.py
Normal file
63
openapi_core/schema/specs/factories.py
Normal file
63
openapi_core/schema/specs/factories.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""OpenAPI core specs factories module"""
|
||||
from functools import lru_cache
|
||||
|
||||
from openapi_spec_validator import openapi_v3_spec_validator
|
||||
|
||||
from openapi_core.schema.components.factories import ComponentsFactory
|
||||
from openapi_core.schema.infos.factories import InfoFactory
|
||||
from openapi_core.schema.paths.generators import PathsGenerator
|
||||
from openapi_core.schema.schemas.registries import SchemaRegistry
|
||||
from openapi_core.schema.servers.generators import ServersGenerator
|
||||
from openapi_core.schema.specs.models import Spec
|
||||
|
||||
|
||||
class SpecFactory(object):
|
||||
|
||||
def __init__(self, dereferencer, config=None):
|
||||
self.dereferencer = dereferencer
|
||||
self.config = config or {}
|
||||
|
||||
def create(self, spec_dict, spec_url=''):
|
||||
if self.config.get('validate_spec', True):
|
||||
openapi_v3_spec_validator.validate(spec_dict, spec_url=spec_url)
|
||||
|
||||
spec_dict_deref = self.dereferencer.dereference(spec_dict)
|
||||
|
||||
info_spec = spec_dict_deref.get('info', {})
|
||||
servers_spec = spec_dict_deref.get('servers', [])
|
||||
paths = spec_dict_deref.get('paths', {})
|
||||
components_spec = spec_dict_deref.get('components', {})
|
||||
|
||||
info = self.info_factory.create(info_spec)
|
||||
servers = self.servers_generator.generate(servers_spec)
|
||||
paths = self.paths_generator.generate(paths)
|
||||
components = self.components_factory.create(components_spec)
|
||||
spec = Spec(
|
||||
info, list(paths), servers=list(servers), components=components)
|
||||
return spec
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def schemas_registry(self):
|
||||
return SchemaRegistry(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def info_factory(self):
|
||||
return InfoFactory(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def servers_generator(self):
|
||||
return ServersGenerator(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def paths_generator(self):
|
||||
return PathsGenerator(self.dereferencer, self.schemas_registry)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def components_factory(self):
|
||||
return ComponentsFactory(self.dereferencer, self.schemas_registry)
|
59
openapi_core/schema/specs/models.py
Normal file
59
openapi_core/schema/specs/models.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""OpenAPI core specs models module"""
|
||||
import logging
|
||||
from functools import partialmethod
|
||||
|
||||
from openapi_core.schema.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.servers.exceptions import InvalidServer
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Spec(object):
|
||||
"""Represents an OpenAPI Specification for a service."""
|
||||
|
||||
def __init__(self, info, paths, servers=None, components=None):
|
||||
self.info = info
|
||||
self.paths = paths and dict(paths)
|
||||
self.servers = servers or []
|
||||
self.components = components
|
||||
|
||||
def __getitem__(self, path_name):
|
||||
return self.paths[path_name]
|
||||
|
||||
@property
|
||||
def default_url(self):
|
||||
return self.servers[0].default_url
|
||||
|
||||
def get_server(self, full_url_pattern):
|
||||
for spec_server in self.servers:
|
||||
if spec_server.default_url in full_url_pattern:
|
||||
return spec_server
|
||||
|
||||
raise InvalidServer(
|
||||
"Invalid request server {0}".format(full_url_pattern))
|
||||
|
||||
def get_server_url(self, index=0):
|
||||
return self.servers[index].default_url
|
||||
|
||||
def get_operation(self, path_pattern, http_method):
|
||||
try:
|
||||
return self.paths[path_pattern].operations[http_method]
|
||||
except KeyError:
|
||||
raise InvalidOperation(
|
||||
"Unknown operation path {0} with method {1}".format(
|
||||
path_pattern, http_method))
|
||||
|
||||
def get_schema(self, name):
|
||||
return self.components.schemas[name]
|
||||
|
||||
# operations shortcuts
|
||||
|
||||
get = partialmethod(get_operation, http_method='get')
|
||||
put = partialmethod(get_operation, http_method='put')
|
||||
post = partialmethod(get_operation, http_method='post')
|
||||
delete = partialmethod(get_operation, http_method='delete')
|
||||
options = partialmethod(get_operation, http_method='options')
|
||||
head = partialmethod(get_operation, http_method='head')
|
||||
patch = partialmethod(get_operation, http_method='patch')
|
|
@ -3,9 +3,15 @@ from jsonschema.validators import RefResolver
|
|||
from openapi_spec_validator.validators import Dereferencer
|
||||
from openapi_spec_validator import default_handlers
|
||||
|
||||
from openapi_core.exceptions import OpenAPIParameterError, OpenAPIBodyError
|
||||
from openapi_core.specs import SpecFactory
|
||||
from openapi_core.validators import RequestValidator, ResponseValidator
|
||||
from openapi_core.schema.media_types.exceptions import OpenAPIMediaTypeError
|
||||
from openapi_core.schema.parameters.exceptions import OpenAPIParameterError
|
||||
from openapi_core.schema.request_bodies.exceptions import (
|
||||
OpenAPIRequestBodyError,
|
||||
)
|
||||
from openapi_core.schema.schemas.exceptions import OpenAPISchemaError
|
||||
from openapi_core.schema.specs.factories import SpecFactory
|
||||
from openapi_core.validation.request.validators import RequestValidator
|
||||
from openapi_core.validation.response.validators import ResponseValidator
|
||||
|
||||
|
||||
def create_spec(spec_dict, spec_url=''):
|
||||
|
@ -25,7 +31,10 @@ def validate_parameters(spec, request, wrapper_class=None):
|
|||
|
||||
try:
|
||||
result.raise_for_errors()
|
||||
except OpenAPIBodyError:
|
||||
except (
|
||||
OpenAPIRequestBodyError, OpenAPIMediaTypeError,
|
||||
OpenAPISchemaError,
|
||||
):
|
||||
return result.parameters
|
||||
else:
|
||||
return result.parameters
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""OpenAPI core specs module"""
|
||||
import logging
|
||||
from functools import partialmethod, lru_cache
|
||||
|
||||
from openapi_spec_validator import openapi_v3_spec_validator
|
||||
|
||||
from openapi_core.components import ComponentsFactory
|
||||
from openapi_core.exceptions import InvalidOperation, InvalidServer
|
||||
from openapi_core.infos import InfoFactory
|
||||
from openapi_core.paths import PathsGenerator
|
||||
from openapi_core.schemas import SchemaRegistry
|
||||
from openapi_core.servers import ServersGenerator
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Spec(object):
|
||||
"""Represents an OpenAPI Specification for a service."""
|
||||
|
||||
def __init__(self, info, paths, servers=None, components=None):
|
||||
self.info = info
|
||||
self.paths = paths and dict(paths)
|
||||
self.servers = servers or []
|
||||
self.components = components
|
||||
|
||||
def __getitem__(self, path_name):
|
||||
return self.paths[path_name]
|
||||
|
||||
@property
|
||||
def default_url(self):
|
||||
return self.servers[0].default_url
|
||||
|
||||
def get_server(self, full_url_pattern):
|
||||
for spec_server in self.servers:
|
||||
if spec_server.default_url in full_url_pattern:
|
||||
return spec_server
|
||||
|
||||
raise InvalidServer(
|
||||
"Invalid request server {0}".format(full_url_pattern))
|
||||
|
||||
def get_server_url(self, index=0):
|
||||
return self.servers[index].default_url
|
||||
|
||||
def get_operation(self, path_pattern, http_method):
|
||||
try:
|
||||
return self.paths[path_pattern].operations[http_method]
|
||||
except KeyError:
|
||||
raise InvalidOperation(
|
||||
"Unknown operation path {0} with method {1}".format(
|
||||
path_pattern, http_method))
|
||||
|
||||
def get_schema(self, name):
|
||||
return self.components.schemas[name]
|
||||
|
||||
# operations shortcuts
|
||||
|
||||
get = partialmethod(get_operation, http_method='get')
|
||||
put = partialmethod(get_operation, http_method='put')
|
||||
post = partialmethod(get_operation, http_method='post')
|
||||
delete = partialmethod(get_operation, http_method='delete')
|
||||
options = partialmethod(get_operation, http_method='options')
|
||||
head = partialmethod(get_operation, http_method='head')
|
||||
patch = partialmethod(get_operation, http_method='patch')
|
||||
|
||||
|
||||
class SpecFactory(object):
|
||||
|
||||
def __init__(self, dereferencer, config=None):
|
||||
self.dereferencer = dereferencer
|
||||
self.config = config or {}
|
||||
|
||||
def create(self, spec_dict, spec_url=''):
|
||||
if self.config.get('validate_spec', True):
|
||||
openapi_v3_spec_validator.validate(spec_dict, spec_url=spec_url)
|
||||
|
||||
spec_dict_deref = self.dereferencer.dereference(spec_dict)
|
||||
|
||||
info_spec = spec_dict_deref.get('info', {})
|
||||
servers_spec = spec_dict_deref.get('servers', [])
|
||||
paths = spec_dict_deref.get('paths', {})
|
||||
components_spec = spec_dict_deref.get('components', {})
|
||||
|
||||
info = self.info_factory.create(info_spec)
|
||||
servers = self.servers_generator.generate(servers_spec)
|
||||
paths = self.paths_generator.generate(paths)
|
||||
components = self.components_factory.create(components_spec)
|
||||
spec = Spec(
|
||||
info, list(paths), servers=list(servers), components=components)
|
||||
return spec
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def schemas_registry(self):
|
||||
return SchemaRegistry(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def info_factory(self):
|
||||
return InfoFactory(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def servers_generator(self):
|
||||
return ServersGenerator(self.dereferencer)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def paths_generator(self):
|
||||
return PathsGenerator(self.dereferencer, self.schemas_registry)
|
||||
|
||||
@property
|
||||
@lru_cache()
|
||||
def components_factory(self):
|
||||
return ComponentsFactory(self.dereferencer, self.schemas_registry)
|
0
openapi_core/validation/__init__.py
Normal file
0
openapi_core/validation/__init__.py
Normal file
11
openapi_core/validation/models.py
Normal file
11
openapi_core/validation/models.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
"""OpenAPI core validation models module"""
|
||||
|
||||
|
||||
class BaseValidationResult(object):
|
||||
|
||||
def __init__(self, errors):
|
||||
self.errors = errors
|
||||
|
||||
def raise_for_errors(self):
|
||||
for error in self.errors:
|
||||
raise error
|
0
openapi_core/validation/request/__init__.py
Normal file
0
openapi_core/validation/request/__init__.py
Normal file
31
openapi_core/validation/request/models.py
Normal file
31
openapi_core/validation/request/models.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
"""OpenAPI core validation request models module"""
|
||||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
|
||||
from openapi_core.validation.models import BaseValidationResult
|
||||
|
||||
|
||||
class RequestParameters(dict):
|
||||
|
||||
valid_locations = ['path', 'query', 'headers', 'cookies']
|
||||
|
||||
def __getitem__(self, location):
|
||||
self.validate_location(location)
|
||||
|
||||
return self.setdefault(location, {})
|
||||
|
||||
def __setitem__(self, location, value):
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def validate_location(cls, location):
|
||||
if location not in cls.valid_locations:
|
||||
raise OpenAPIMappingError(
|
||||
"Unknown parameter location: {0}".format(str(location)))
|
||||
|
||||
|
||||
class RequestValidationResult(BaseValidationResult):
|
||||
|
||||
def __init__(self, errors, body=None, parameters=None):
|
||||
super(RequestValidationResult, self).__init__(errors)
|
||||
self.body = body
|
||||
self.parameters = parameters or RequestParameters()
|
85
openapi_core/validation/request/validators.py
Normal file
85
openapi_core/validation/request/validators.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
"""OpenAPI core validation request validators module"""
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
from openapi_core.schema.parameters.exceptions import MissingParameter
|
||||
from openapi_core.validation.request.models import (
|
||||
RequestParameters, RequestValidationResult,
|
||||
)
|
||||
from openapi_core.validation.util import get_operation_pattern
|
||||
|
||||
|
||||
class RequestValidator(object):
|
||||
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
def validate(self, request):
|
||||
try:
|
||||
server = self.spec.get_server(request.full_url_pattern)
|
||||
# don't process if server errors
|
||||
except OpenAPIMappingError as exc:
|
||||
return RequestValidationResult([exc, ], None, None)
|
||||
|
||||
operation_pattern = get_operation_pattern(
|
||||
server.default_url, request.full_url_pattern
|
||||
)
|
||||
|
||||
try:
|
||||
operation = self.spec.get_operation(
|
||||
operation_pattern, request.method)
|
||||
# don't process if operation errors
|
||||
except OpenAPIMappingError as exc:
|
||||
return RequestValidationResult([exc, ], None, None)
|
||||
|
||||
params, params_errors = self._get_parameters(request, operation)
|
||||
body, body_errors = self._get_body(request, operation)
|
||||
|
||||
errors = params_errors + body_errors
|
||||
return RequestValidationResult(errors, body, params)
|
||||
|
||||
def _get_parameters(self, request, operation):
|
||||
errors = []
|
||||
|
||||
parameters = RequestParameters()
|
||||
for param_name, param in iteritems(operation.parameters):
|
||||
try:
|
||||
raw_value = param.get_value(request)
|
||||
except MissingParameter:
|
||||
continue
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
|
||||
try:
|
||||
value = param.unmarshal(raw_value)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
parameters[param.location.value][param_name] = value
|
||||
|
||||
return parameters, errors
|
||||
|
||||
def _get_body(self, request, operation):
|
||||
errors = []
|
||||
|
||||
if operation.request_body is None:
|
||||
return None, errors
|
||||
|
||||
body = None
|
||||
try:
|
||||
media_type = operation.request_body[request.mimetype]
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
raw_body = operation.request_body.get_value(request)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
body = media_type.unmarshal(raw_body)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
|
||||
return body, errors
|
0
openapi_core/validation/response/__init__.py
Normal file
0
openapi_core/validation/response/__init__.py
Normal file
10
openapi_core/validation/response/models.py
Normal file
10
openapi_core/validation/response/models.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
"""OpenAPI core validation response models module"""
|
||||
from openapi_core.validation.models import BaseValidationResult
|
||||
|
||||
|
||||
class ResponseValidationResult(BaseValidationResult):
|
||||
|
||||
def __init__(self, errors, data=None, headers=None):
|
||||
super(ResponseValidationResult, self).__init__(errors)
|
||||
self.data = data
|
||||
self.headers = headers
|
75
openapi_core/validation/response/validators.py
Normal file
75
openapi_core/validation/response/validators.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
"""OpenAPI core validation response validators module"""
|
||||
from openapi_core.schema.exceptions import OpenAPIMappingError
|
||||
from openapi_core.validation.response.models import ResponseValidationResult
|
||||
from openapi_core.validation.util import get_operation_pattern
|
||||
|
||||
|
||||
class ResponseValidator(object):
|
||||
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
def validate(self, request, response):
|
||||
try:
|
||||
server = self.spec.get_server(request.full_url_pattern)
|
||||
# don't process if server errors
|
||||
except OpenAPIMappingError as exc:
|
||||
return ResponseValidationResult([exc, ], None, None)
|
||||
|
||||
operation_pattern = get_operation_pattern(
|
||||
server.default_url, request.full_url_pattern
|
||||
)
|
||||
|
||||
try:
|
||||
operation = self.spec.get_operation(
|
||||
operation_pattern, request.method)
|
||||
# don't process if operation errors
|
||||
except OpenAPIMappingError as exc:
|
||||
return ResponseValidationResult([exc, ], None, None)
|
||||
|
||||
try:
|
||||
operation_response = operation.get_response(
|
||||
str(response.status_code))
|
||||
# don't process if operation response errors
|
||||
except OpenAPIMappingError as exc:
|
||||
return ResponseValidationResult([exc, ], None, None)
|
||||
|
||||
data, data_errors = self._get_data(response, operation_response)
|
||||
|
||||
headers, headers_errors = self._get_headers(
|
||||
response, operation_response)
|
||||
|
||||
errors = data_errors + headers_errors
|
||||
return ResponseValidationResult(errors, data, headers)
|
||||
|
||||
def _get_data(self, response, operation_response):
|
||||
errors = []
|
||||
|
||||
if not operation_response.content:
|
||||
return None, errors
|
||||
|
||||
data = None
|
||||
try:
|
||||
media_type = operation_response[response.mimetype]
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
raw_data = operation_response.get_value(response)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
data = media_type.unmarshal(raw_data)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
|
||||
return data, errors
|
||||
|
||||
def _get_headers(self, response, operation_response):
|
||||
errors = []
|
||||
|
||||
# @todo: implement
|
||||
headers = {}
|
||||
|
||||
return headers, errors
|
12
openapi_core/validation/util.py
Normal file
12
openapi_core/validation/util.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
"""OpenAPI core validation util module"""
|
||||
from yarl import URL
|
||||
|
||||
|
||||
def get_operation_pattern(server_url, request_url_pattern):
|
||||
"""Return an updated request URL pattern with the server URL removed."""
|
||||
if server_url[-1] == "/":
|
||||
# operations have to start with a slash, so do not remove it
|
||||
server_url = server_url[:-1]
|
||||
if URL(server_url).is_absolute():
|
||||
return request_url_pattern.replace(server_url, "", 1)
|
||||
return URL(request_url_pattern).path_qs.replace(server_url, "", 1)
|
|
@ -1,211 +0,0 @@
|
|||
"""OpenAPI core validators module"""
|
||||
from six import iteritems
|
||||
from yarl import URL
|
||||
|
||||
from openapi_core.exceptions import (
|
||||
OpenAPIMappingError, MissingParameter, MissingBody, InvalidResponse,
|
||||
)
|
||||
|
||||
|
||||
class RequestParameters(dict):
|
||||
|
||||
valid_locations = ['path', 'query', 'headers', 'cookies']
|
||||
|
||||
def __getitem__(self, location):
|
||||
self.validate_location(location)
|
||||
|
||||
return self.setdefault(location, {})
|
||||
|
||||
def __setitem__(self, location, value):
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def validate_location(cls, location):
|
||||
if location not in cls.valid_locations:
|
||||
raise OpenAPIMappingError(
|
||||
"Unknown parameter location: {0}".format(str(location)))
|
||||
|
||||
|
||||
class BaseValidationResult(object):
|
||||
|
||||
def __init__(self, errors):
|
||||
self.errors = errors
|
||||
|
||||
def raise_for_errors(self):
|
||||
for error in self.errors:
|
||||
raise error
|
||||
|
||||
|
||||
class RequestValidationResult(BaseValidationResult):
|
||||
|
||||
def __init__(self, errors, body=None, parameters=None):
|
||||
super(RequestValidationResult, self).__init__(errors)
|
||||
self.body = body
|
||||
self.parameters = parameters or RequestParameters()
|
||||
|
||||
|
||||
class ResponseValidationResult(BaseValidationResult):
|
||||
|
||||
def __init__(self, errors, data=None, headers=None):
|
||||
super(ResponseValidationResult, self).__init__(errors)
|
||||
self.data = data
|
||||
self.headers = headers
|
||||
|
||||
|
||||
def get_operation_pattern(server_url, request_url_pattern):
|
||||
"""Return an updated request URL pattern with the server URL removed."""
|
||||
if server_url[-1] == "/":
|
||||
# operations have to start with a slash, so do not remove it
|
||||
server_url = server_url[:-1]
|
||||
if URL(server_url).is_absolute():
|
||||
return request_url_pattern.replace(server_url, "", 1)
|
||||
return URL(request_url_pattern).path_qs.replace(server_url, "", 1)
|
||||
|
||||
|
||||
class RequestValidator(object):
|
||||
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
def validate(self, request):
|
||||
errors = []
|
||||
body = None
|
||||
parameters = RequestParameters()
|
||||
|
||||
try:
|
||||
server = self.spec.get_server(request.full_url_pattern)
|
||||
# don't process if server errors
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
return RequestValidationResult(errors, body, parameters)
|
||||
|
||||
operation_pattern = get_operation_pattern(
|
||||
server.default_url, request.full_url_pattern
|
||||
)
|
||||
|
||||
try:
|
||||
operation = self.spec.get_operation(
|
||||
operation_pattern, request.method)
|
||||
# don't process if operation errors
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
return RequestValidationResult(errors, body, parameters)
|
||||
|
||||
for param_name, param in iteritems(operation.parameters):
|
||||
try:
|
||||
raw_value = self._get_raw_value(request, param)
|
||||
except MissingParameter as exc:
|
||||
if param.required:
|
||||
errors.append(exc)
|
||||
|
||||
if not param.schema or param.schema.default is None:
|
||||
continue
|
||||
raw_value = param.schema.default
|
||||
|
||||
try:
|
||||
value = param.unmarshal(raw_value)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
parameters[param.location.value][param_name] = value
|
||||
|
||||
if operation.request_body is not None:
|
||||
try:
|
||||
media_type = operation.request_body[request.mimetype]
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
raw_body = self._get_raw_body(request)
|
||||
except MissingBody as exc:
|
||||
if operation.request_body.required:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
body = media_type.unmarshal(raw_body)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
|
||||
return RequestValidationResult(errors, body, parameters)
|
||||
|
||||
def _get_raw_value(self, request, param):
|
||||
location = request.parameters[param.location.value]
|
||||
|
||||
try:
|
||||
raw = location[param.name]
|
||||
except KeyError:
|
||||
raise MissingParameter(
|
||||
"Missing required `{0}` parameter".format(param.name))
|
||||
|
||||
if param.aslist and param.explode:
|
||||
return location.getlist(param.name)
|
||||
|
||||
return raw
|
||||
|
||||
def _get_raw_body(self, request):
|
||||
if not request.body:
|
||||
raise MissingBody("Missing required request body")
|
||||
|
||||
return request.body
|
||||
|
||||
|
||||
class ResponseValidator(object):
|
||||
|
||||
def __init__(self, spec):
|
||||
self.spec = spec
|
||||
|
||||
def validate(self, request, response):
|
||||
errors = []
|
||||
data = None
|
||||
headers = {}
|
||||
|
||||
try:
|
||||
server = self.spec.get_server(request.full_url_pattern)
|
||||
# don't process if server errors
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
return ResponseValidationResult(errors, data, headers)
|
||||
|
||||
operation_pattern = get_operation_pattern(
|
||||
server.default_url, request.full_url_pattern
|
||||
)
|
||||
|
||||
try:
|
||||
operation = self.spec.get_operation(
|
||||
operation_pattern, request.method)
|
||||
# don't process if operation errors
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
return ResponseValidationResult(errors, data, headers)
|
||||
|
||||
try:
|
||||
operation_response = operation.get_response(
|
||||
str(response.status_code))
|
||||
# don't process if invalid response status code
|
||||
except InvalidResponse as exc:
|
||||
errors.append(exc)
|
||||
return ResponseValidationResult(errors, data, headers)
|
||||
|
||||
if operation_response.content:
|
||||
try:
|
||||
media_type = operation_response[response.mimetype]
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
raw_data = self._get_raw_data(response)
|
||||
except MissingBody as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
try:
|
||||
data = media_type.unmarshal(raw_data)
|
||||
except OpenAPIMappingError as exc:
|
||||
errors.append(exc)
|
||||
|
||||
return ResponseValidationResult(errors, data, headers)
|
||||
|
||||
def _get_raw_data(self, response):
|
||||
if not response.data:
|
||||
raise MissingBody("Missing required response data")
|
||||
|
||||
return response.data
|
|
@ -1,142 +0,0 @@
|
|||
"""OpenAPI core wrappers module"""
|
||||
import warnings
|
||||
|
||||
from six.moves.urllib.parse import urljoin
|
||||
from werkzeug.datastructures import ImmutableMultiDict
|
||||
|
||||
|
||||
class BaseOpenAPIRequest(object):
|
||||
|
||||
host_url = NotImplemented
|
||||
path = NotImplemented
|
||||
path_pattern = NotImplemented
|
||||
method = NotImplemented
|
||||
|
||||
parameters = NotImplemented
|
||||
body = NotImplemented
|
||||
|
||||
mimetype = NotImplemented
|
||||
|
||||
@property
|
||||
def full_url_pattern(self):
|
||||
return urljoin(self.host_url, self.path_pattern)
|
||||
|
||||
def get_body(self, spec):
|
||||
warnings.warn(
|
||||
"`get_body` method is deprecated. "
|
||||
"Use RequestValidator instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
# backward compatibility
|
||||
from openapi_core.shortcuts import validate_body
|
||||
return validate_body(spec, self, wrapper_class=None)
|
||||
|
||||
def get_parameters(self, spec):
|
||||
warnings.warn(
|
||||
"`get_parameters` method is deprecated. "
|
||||
"Use RequestValidator instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
# backward compatibility
|
||||
from openapi_core.shortcuts import validate_parameters
|
||||
return validate_parameters(spec, self, wrapper_class=None)
|
||||
|
||||
|
||||
class MockRequest(BaseOpenAPIRequest):
|
||||
|
||||
def __init__(
|
||||
self, host_url, method, path, path_pattern=None, args=None,
|
||||
view_args=None, headers=None, cookies=None, data=None,
|
||||
mimetype='application/json'):
|
||||
self.host_url = host_url
|
||||
self.path = path
|
||||
self.path_pattern = path_pattern or path
|
||||
self.method = method.lower()
|
||||
|
||||
self.parameters = {
|
||||
'path': view_args or {},
|
||||
'query': ImmutableMultiDict(args or []),
|
||||
'header': headers or {},
|
||||
'cookie': cookies or {},
|
||||
}
|
||||
|
||||
self.body = data or ''
|
||||
|
||||
self.mimetype = mimetype
|
||||
|
||||
|
||||
class FlaskOpenAPIRequest(BaseOpenAPIRequest):
|
||||
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
|
||||
@property
|
||||
def host_url(self):
|
||||
return self.request.host_url
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.request.path
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
return self.request.method.lower()
|
||||
|
||||
@property
|
||||
def path_pattern(self):
|
||||
if self.request.url_rule is None:
|
||||
return self.path
|
||||
|
||||
return self.request.url_rule.rule
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return {
|
||||
'path': self.request.view_args,
|
||||
'query': self.request.args,
|
||||
'headers': self.request.headers,
|
||||
'cookies': self.request.cookies,
|
||||
}
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return self.request.data
|
||||
|
||||
@property
|
||||
def mimetype(self):
|
||||
return self.request.mimetype
|
||||
|
||||
|
||||
class BaseOpenAPIResponse(object):
|
||||
|
||||
body = NotImplemented
|
||||
status_code = NotImplemented
|
||||
|
||||
mimetype = NotImplemented
|
||||
|
||||
|
||||
class MockResponse(BaseOpenAPIRequest):
|
||||
|
||||
def __init__(self, data, status_code=200, mimetype='application/json'):
|
||||
self.data = data
|
||||
|
||||
self.status_code = status_code
|
||||
self.mimetype = mimetype
|
||||
|
||||
|
||||
class FlaskOpenAPIResponse(BaseOpenAPIResponse):
|
||||
|
||||
def __init__(self, response):
|
||||
self.response = response
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return self.response.data
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
return self.response._status_code
|
||||
|
||||
@property
|
||||
def mimetype(self):
|
||||
return self.response.mimetype
|
0
openapi_core/wrappers/__init__.py
Normal file
0
openapi_core/wrappers/__init__.py
Normal file
49
openapi_core/wrappers/base.py
Normal file
49
openapi_core/wrappers/base.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
"""OpenAPI core wrappers module"""
|
||||
import warnings
|
||||
|
||||
from six.moves.urllib.parse import urljoin
|
||||
|
||||
|
||||
class BaseOpenAPIRequest(object):
|
||||
|
||||
host_url = NotImplemented
|
||||
path = NotImplemented
|
||||
path_pattern = NotImplemented
|
||||
method = NotImplemented
|
||||
|
||||
parameters = NotImplemented
|
||||
body = NotImplemented
|
||||
|
||||
mimetype = NotImplemented
|
||||
|
||||
@property
|
||||
def full_url_pattern(self):
|
||||
return urljoin(self.host_url, self.path_pattern)
|
||||
|
||||
def get_body(self, spec):
|
||||
warnings.warn(
|
||||
"`get_body` method is deprecated. "
|
||||
"Use RequestValidator instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
# backward compatibility
|
||||
from openapi_core.shortcuts import validate_body
|
||||
return validate_body(spec, self, wrapper_class=None)
|
||||
|
||||
def get_parameters(self, spec):
|
||||
warnings.warn(
|
||||
"`get_parameters` method is deprecated. "
|
||||
"Use RequestValidator instead.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
# backward compatibility
|
||||
from openapi_core.shortcuts import validate_parameters
|
||||
return validate_parameters(spec, self, wrapper_class=None)
|
||||
|
||||
|
||||
class BaseOpenAPIResponse(object):
|
||||
|
||||
body = NotImplemented
|
||||
status_code = NotImplemented
|
||||
|
||||
mimetype = NotImplemented
|
62
openapi_core/wrappers/flask.py
Normal file
62
openapi_core/wrappers/flask.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
"""OpenAPI core wrappers module"""
|
||||
from openapi_core.wrappers.base import BaseOpenAPIRequest, BaseOpenAPIResponse
|
||||
|
||||
|
||||
class FlaskOpenAPIRequest(BaseOpenAPIRequest):
|
||||
|
||||
def __init__(self, request):
|
||||
self.request = request
|
||||
|
||||
@property
|
||||
def host_url(self):
|
||||
return self.request.host_url
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self.request.path
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
return self.request.method.lower()
|
||||
|
||||
@property
|
||||
def path_pattern(self):
|
||||
if self.request.url_rule is None:
|
||||
return self.path
|
||||
|
||||
return self.request.url_rule.rule
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
return {
|
||||
'path': self.request.view_args,
|
||||
'query': self.request.args,
|
||||
'headers': self.request.headers,
|
||||
'cookies': self.request.cookies,
|
||||
}
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return self.request.data
|
||||
|
||||
@property
|
||||
def mimetype(self):
|
||||
return self.request.mimetype
|
||||
|
||||
|
||||
class FlaskOpenAPIResponse(BaseOpenAPIResponse):
|
||||
|
||||
def __init__(self, response):
|
||||
self.response = response
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return self.response.data
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
return self.response._status_code
|
||||
|
||||
@property
|
||||
def mimetype(self):
|
||||
return self.response.mimetype
|
36
openapi_core/wrappers/mock.py
Normal file
36
openapi_core/wrappers/mock.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
"""OpenAPI core wrappers module"""
|
||||
from werkzeug.datastructures import ImmutableMultiDict
|
||||
|
||||
from openapi_core.wrappers.base import BaseOpenAPIRequest, BaseOpenAPIResponse
|
||||
|
||||
|
||||
class MockRequest(BaseOpenAPIRequest):
|
||||
|
||||
def __init__(
|
||||
self, host_url, method, path, path_pattern=None, args=None,
|
||||
view_args=None, headers=None, cookies=None, data=None,
|
||||
mimetype='application/json'):
|
||||
self.host_url = host_url
|
||||
self.path = path
|
||||
self.path_pattern = path_pattern or path
|
||||
self.method = method.lower()
|
||||
|
||||
self.parameters = {
|
||||
'path': view_args or {},
|
||||
'query': ImmutableMultiDict(args or []),
|
||||
'header': headers or {},
|
||||
'cookie': cookies or {},
|
||||
}
|
||||
|
||||
self.body = data or ''
|
||||
|
||||
self.mimetype = mimetype
|
||||
|
||||
|
||||
class MockResponse(BaseOpenAPIResponse):
|
||||
|
||||
def __init__(self, data, status_code=200, mimetype='application/json'):
|
||||
self.data = data
|
||||
|
||||
self.status_code = status_code
|
||||
self.mimetype = mimetype
|
|
@ -1,9 +1,9 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.exceptions import InvalidOperation
|
||||
from openapi_core.schema.operations.exceptions import InvalidOperation
|
||||
from openapi_core.shortcuts import create_spec
|
||||
from openapi_core.validators import RequestValidator
|
||||
from openapi_core.wrappers import MockRequest
|
||||
from openapi_core.validation.request.validators import RequestValidator
|
||||
from openapi_core.wrappers.mock import MockRequest
|
||||
|
||||
|
||||
class TestMinimal(object):
|
||||
|
|
|
@ -2,22 +2,28 @@ import json
|
|||
import pytest
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.exceptions import (
|
||||
MissingParameter, InvalidContentType, InvalidServer,
|
||||
UndefinedSchemaProperty, MissingProperty,
|
||||
EmptyValue, InvalidMediaTypeValue, InvalidParameterValue,
|
||||
from openapi_core.schema.media_types.exceptions import (
|
||||
InvalidContentType, InvalidMediaTypeValue,
|
||||
)
|
||||
from openapi_core.media_types import MediaType
|
||||
from openapi_core.operations import Operation
|
||||
from openapi_core.parameters import Parameter
|
||||
from openapi_core.paths import Path
|
||||
from openapi_core.request_bodies import RequestBody
|
||||
from openapi_core.responses import Response
|
||||
from openapi_core.schemas import Schema
|
||||
from openapi_core.servers import Server, ServerVariable
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
from openapi_core.schema.operations.models import Operation
|
||||
from openapi_core.schema.parameters.exceptions import (
|
||||
MissingRequiredParameter, InvalidParameterValue, EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
from openapi_core.schema.paths.models import Path
|
||||
from openapi_core.schema.request_bodies.models import RequestBody
|
||||
from openapi_core.schema.responses.models import Response
|
||||
from openapi_core.schema.schemas.exceptions import (
|
||||
UndefinedSchemaProperty, MissingSchemaProperty,
|
||||
)
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
from openapi_core.schema.servers.exceptions import InvalidServer
|
||||
from openapi_core.schema.servers.models import Server, ServerVariable
|
||||
from openapi_core.shortcuts import create_spec
|
||||
from openapi_core.validators import RequestValidator, ResponseValidator
|
||||
from openapi_core.wrappers import MockRequest, MockResponse
|
||||
from openapi_core.validation.request.validators import RequestValidator
|
||||
from openapi_core.validation.response.validators import ResponseValidator
|
||||
from openapi_core.wrappers.mock import MockRequest, MockResponse
|
||||
|
||||
|
||||
class TestPetstore(object):
|
||||
|
@ -312,7 +318,7 @@ class TestPetstore(object):
|
|||
path_pattern=path_pattern,
|
||||
)
|
||||
|
||||
with pytest.raises(MissingParameter):
|
||||
with pytest.raises(MissingRequiredParameter):
|
||||
request.get_parameters(spec)
|
||||
|
||||
body = request.get_body(spec)
|
||||
|
@ -331,7 +337,7 @@ class TestPetstore(object):
|
|||
path_pattern=path_pattern, args=query_params,
|
||||
)
|
||||
|
||||
with pytest.raises(EmptyValue):
|
||||
with pytest.raises(EmptyParameterValue):
|
||||
request.get_parameters(spec)
|
||||
body = request.get_body(spec)
|
||||
|
||||
|
@ -464,7 +470,7 @@ class TestPetstore(object):
|
|||
|
||||
assert parameters == {}
|
||||
|
||||
with pytest.raises(MissingProperty):
|
||||
with pytest.raises(MissingSchemaProperty):
|
||||
request.get_body(spec)
|
||||
|
||||
def test_post_pets_extra_body_properties(self, spec, spec_dict):
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
import json
|
||||
import pytest
|
||||
|
||||
from openapi_core.exceptions import (
|
||||
InvalidServer, InvalidOperation, MissingParameter,
|
||||
MissingBody, InvalidContentType, InvalidResponse, InvalidMediaTypeValue,
|
||||
InvalidValue,
|
||||
from openapi_core.schema.media_types.exceptions import (
|
||||
InvalidContentType, InvalidMediaTypeValue,
|
||||
)
|
||||
from openapi_core.schema.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
from openapi_core.schema.responses.exceptions import (
|
||||
MissingResponseContent, InvalidResponse,
|
||||
)
|
||||
from openapi_core.schema.servers.exceptions import InvalidServer
|
||||
from openapi_core.shortcuts import create_spec
|
||||
from openapi_core.validators import RequestValidator, ResponseValidator
|
||||
from openapi_core.wrappers import MockRequest, MockResponse
|
||||
from openapi_core.validation.request.validators import RequestValidator
|
||||
from openapi_core.validation.response.validators import ResponseValidator
|
||||
from openapi_core.wrappers.mock import MockRequest, MockResponse
|
||||
|
||||
|
||||
class TestRequestValidator(object):
|
||||
|
@ -52,7 +58,7 @@ class TestRequestValidator(object):
|
|||
|
||||
result = validator.validate(request)
|
||||
|
||||
assert type(result.errors[0]) == MissingParameter
|
||||
assert type(result.errors[0]) == MissingRequiredParameter
|
||||
assert result.body is None
|
||||
assert result.parameters == {
|
||||
'query': {
|
||||
|
@ -88,7 +94,7 @@ class TestRequestValidator(object):
|
|||
result = validator.validate(request)
|
||||
|
||||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == MissingBody
|
||||
assert type(result.errors[0]) == MissingRequestBody
|
||||
assert result.body is None
|
||||
assert result.parameters == {}
|
||||
|
||||
|
@ -183,7 +189,7 @@ class TestResponseValidator(object):
|
|||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == InvalidServer
|
||||
assert result.data is None
|
||||
assert result.headers == {}
|
||||
assert result.headers is None
|
||||
|
||||
def test_invalid_operation(self, validator):
|
||||
request = MockRequest(self.host_url, 'get', '/v1')
|
||||
|
@ -194,7 +200,7 @@ class TestResponseValidator(object):
|
|||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == InvalidOperation
|
||||
assert result.data is None
|
||||
assert result.headers == {}
|
||||
assert result.headers is None
|
||||
|
||||
def test_invalid_response(self, validator):
|
||||
request = MockRequest(self.host_url, 'get', '/v1/pets')
|
||||
|
@ -205,7 +211,7 @@ class TestResponseValidator(object):
|
|||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == InvalidResponse
|
||||
assert result.data is None
|
||||
assert result.headers == {}
|
||||
assert result.headers is None
|
||||
|
||||
def test_invalid_content_type(self, validator):
|
||||
request = MockRequest(self.host_url, 'get', '/v1/pets')
|
||||
|
@ -225,7 +231,7 @@ class TestResponseValidator(object):
|
|||
result = validator.validate(request, response)
|
||||
|
||||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == MissingBody
|
||||
assert type(result.errors[0]) == MissingResponseContent
|
||||
assert result.data is None
|
||||
assert result.headers == {}
|
||||
|
||||
|
@ -256,7 +262,7 @@ class TestResponseValidator(object):
|
|||
result = validator.validate(request, response)
|
||||
|
||||
assert len(result.errors) == 1
|
||||
assert type(result.errors[0]) == InvalidValue
|
||||
assert type(result.errors[0]) == InvalidMediaTypeValue
|
||||
assert result.data is None
|
||||
assert result.headers == {}
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ from werkzeug.datastructures import EnvironHeaders, ImmutableMultiDict
|
|||
from werkzeug.routing import Map, Rule, Subdomain
|
||||
from werkzeug.test import create_environ
|
||||
|
||||
from openapi_core.wrappers import FlaskOpenAPIRequest, FlaskOpenAPIResponse
|
||||
from openapi_core.wrappers.flask import (
|
||||
FlaskOpenAPIRequest, FlaskOpenAPIResponse,
|
||||
)
|
||||
|
||||
|
||||
class TestFlaskOpenAPIRequest(object):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from openapi_core.operations import Operation
|
||||
from openapi_core.schema.operations.models import Operation
|
||||
|
||||
|
||||
class TestSchemas(object):
|
|
@ -1,8 +1,8 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.enums import ParameterStyle
|
||||
from openapi_core.exceptions import EmptyValue
|
||||
from openapi_core.parameters import Parameter
|
||||
from openapi_core.schema.parameters.exceptions import EmptyParameterValue
|
||||
from openapi_core.schema.parameters.enums import ParameterStyle
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
|
||||
|
||||
class TestParameterInit(object):
|
||||
|
@ -59,7 +59,7 @@ class TestParameterUnmarshal(object):
|
|||
param = Parameter('param', 'query')
|
||||
value = ''
|
||||
|
||||
with pytest.raises(EmptyValue):
|
||||
with pytest.raises(EmptyParameterValue):
|
||||
param.unmarshal(value)
|
||||
|
||||
def test_query_allow_empty_value(self):
|
|
@ -1,7 +1,7 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from openapi_core.paths import Path
|
||||
from openapi_core.schema.paths.models import Path
|
||||
|
||||
|
||||
class TestPaths(object):
|
|
@ -1,7 +1,7 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from openapi_core.request_bodies import RequestBody
|
||||
from openapi_core.schema.request_bodies.models import RequestBody
|
||||
|
||||
|
||||
class TestRequestBodies(object):
|
|
@ -1,8 +1,8 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from openapi_core.exceptions import InvalidValueType, InvalidValue
|
||||
from openapi_core.schemas import Schema
|
||||
from openapi_core.schema.schemas.exceptions import InvalidSchemaValue
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
|
||||
|
||||
class TestSchemaIteritems(object):
|
||||
|
@ -44,7 +44,7 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('string')
|
||||
value = None
|
||||
|
||||
with pytest.raises(InvalidValueType):
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_string_default(self):
|
||||
|
@ -52,7 +52,7 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('string', default=default_value)
|
||||
value = None
|
||||
|
||||
with pytest.raises(InvalidValueType):
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_string_default_nullable(self):
|
||||
|
@ -76,7 +76,7 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('integer', enum=[1, 2, 3])
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(InvalidValue):
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_integer_enum(self):
|
||||
|
@ -92,7 +92,7 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('integer', default=default_value)
|
||||
value = None
|
||||
|
||||
with pytest.raises(InvalidValueType):
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
schema.unmarshal(value)
|
||||
|
||||
def test_integer_default_nullable(self):
|
||||
|
@ -108,5 +108,5 @@ class TestSchemaUnmarshal(object):
|
|||
schema = Schema('integer')
|
||||
value = 'abc'
|
||||
|
||||
with pytest.raises(InvalidValueType):
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
schema.unmarshal(value)
|
|
@ -1,9 +1,9 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from openapi_core.exceptions import InvalidOperation
|
||||
from openapi_core.paths import Path
|
||||
from openapi_core.specs import Spec
|
||||
from openapi_core.schema.operations.exceptions import InvalidOperation
|
||||
from openapi_core.schema.paths.models import Path
|
||||
from openapi_core.schema.specs.models import Spec
|
||||
|
||||
|
||||
class TestSpecs(object):
|
Loading…
Reference in a new issue