Extensible schema models

This commit is contained in:
Artur Maciag 2020-03-03 13:01:45 +00:00 committed by p1c2u
parent 4815905d01
commit c0b9916dc8
24 changed files with 179 additions and 24 deletions

View file

@ -1,5 +1,6 @@
from openapi_core.compat import lru_cache
from openapi_core.schema.components.models import Components
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.schemas.generators import SchemasGenerator
from openapi_core.schema.security_schemes.generators import (
SecuritySchemesGenerator,
@ -21,6 +22,8 @@ class ComponentsFactory(object):
request_bodies_spec = components_deref.get('requestBodies', {})
security_schemes_spec = components_deref.get('securitySchemes', {})
extensions = self.extensions_generator.generate(components_deref)
schemas = self.schemas_generator.generate(schemas_spec)
responses = self._generate_response(responses_spec)
parameters = self._generate_parameters(parameters_spec)
@ -30,6 +33,7 @@ class ComponentsFactory(object):
return Components(
schemas=list(schemas), responses=responses, parameters=parameters,
request_bodies=request_bodies, security_schemes=security_schemes,
extensions=extensions,
)
@property
@ -37,6 +41,11 @@ class ComponentsFactory(object):
def schemas_generator(self):
return SchemasGenerator(self.dereferencer, self.schemas_registry)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)
def _generate_response(self, responses_spec):
return responses_spec

View file

@ -3,7 +3,7 @@ class Components(object):
def __init__(
self, schemas=None, responses=None, parameters=None,
request_bodies=None, security_schemes=None):
request_bodies=None, security_schemes=None, extensions=None):
self.schemas = schemas and dict(schemas) or {}
self.responses = responses and dict(responses) or {}
self.parameters = parameters and dict(parameters) or {}
@ -11,3 +11,5 @@ class Components(object):
self.security_schemes = (
security_schemes and dict(security_schemes) or {}
)
self.extensions = extensions and dict(extensions) or {}

View file

@ -1,5 +1,7 @@
"""OpenAPI core contacts factories module"""
from openapi_core.compat import lru_cache
from openapi_core.schema.contacts.models import Contact
from openapi_core.schema.extensions.generators import ExtensionsGenerator
class ContactFactory(object):
@ -12,4 +14,12 @@ class ContactFactory(object):
name = contact_deref.get('name')
url = contact_deref.get('url')
email = contact_deref.get('email')
return Contact(name=name, url=url, email=email)
extensions = self.extensions_generator.generate(contact_deref)
return Contact(name=name, url=url, email=email, extensions=extensions)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -3,7 +3,9 @@
class Contact(object):
def __init__(self, name=None, url=None, email=None):
def __init__(self, name=None, url=None, email=None, extensions=None):
self.name = name
self.url = url
self.email = email
self.extensions = extensions and dict(extensions) or {}

View file

@ -1,4 +1,6 @@
"""OpenAPI core external docs factories module"""
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.external_docs.models import ExternalDocumentation
@ -11,4 +13,14 @@ class ExternalDocumentationFactory(object):
url = external_doc_spec['url']
description = external_doc_spec.get('description')
return ExternalDocumentation(url, description=description)
extensions = self.extensions_generator.generate(external_doc_spec)
return ExternalDocumentation(
url,
description=description, extensions=extensions,
)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -4,6 +4,8 @@
class ExternalDocumentation(object):
"""Represents an OpenAPI External Documentation."""
def __init__(self, url, description=None):
def __init__(self, url, description=None, extensions=None):
self.url = url
self.description = description
self.extensions = extensions and dict(extensions) or {}

View file

@ -1,6 +1,7 @@
"""OpenAPI core infos factories module"""
from openapi_core.compat import lru_cache
from openapi_core.schema.contacts.factories import ContactFactory
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.infos.models import Info
from openapi_core.schema.licenses.factories import LicenseFactory
@ -17,6 +18,8 @@ class InfoFactory(object):
description = info_deref.get('description')
terms_of_service = info_deref.get('termsOfService')
extensions = self.extensions_generator.generate(info_deref)
contact = None
if 'contact' in info_deref:
contact_spec = info_deref.get('contact')
@ -30,7 +33,7 @@ class InfoFactory(object):
return Info(
title, version,
description=description, terms_of_service=terms_of_service,
contact=contact, license=license,
contact=contact, license=license, extensions=extensions,
)
@property
@ -42,3 +45,8 @@ class InfoFactory(object):
@lru_cache()
def license_factory(self):
return LicenseFactory(self.dereferencer)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -5,7 +5,7 @@ class Info(object):
def __init__(
self, title, version, description=None, terms_of_service=None,
contact=None, license=None,
contact=None, license=None, extensions=None,
):
self.title = title
self.version = version
@ -13,3 +13,5 @@ class Info(object):
self.terms_of_service = terms_of_service
self.contact = contact
self.license = license
self.extensions = extensions and dict(extensions) or {}

View file

@ -1,4 +1,6 @@
"""OpenAPI core licenses factories module"""
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.licenses.models import License
@ -11,4 +13,12 @@ class LicenseFactory(object):
license_deref = self.dereferencer.dereference(license_spec)
name = license_deref['name']
url = license_deref.get('url')
return License(name, url=url)
extensions = self.extensions_generator.generate(license_deref)
return License(name, url=url, extensions=extensions)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -3,6 +3,8 @@
class License(object):
def __init__(self, name, url=None):
def __init__(self, name, url=None, extensions=None):
self.name = name
self.url = url
self.extensions = extensions and dict(extensions) or {}

View file

@ -1,6 +1,8 @@
"""OpenAPI core media types generators module"""
from six import iteritems
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.media_types.models import MediaType
@ -21,8 +23,18 @@ class MediaTypeGenerator(object):
else:
example = example_spec
extensions = self.extensions_generator.generate(media_type)
schema = None
if schema_spec:
schema, _ = self.schemas_registry.get_or_create(schema_spec)
yield mimetype, MediaType(mimetype, schema=schema, example=example)
yield mimetype, MediaType(
mimetype,
schema=schema, example=example, extensions=extensions,
)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -4,7 +4,9 @@
class MediaType(object):
"""Represents an OpenAPI MediaType."""
def __init__(self, mimetype, schema=None, example=None):
def __init__(self, mimetype, schema=None, example=None, extensions=None):
self.mimetype = mimetype
self.schema = schema
self.example = example
self.extensions = extensions and dict(extensions) or {}

View file

@ -4,6 +4,7 @@ from six import iteritems
from openapi_spec_validator.validators import PathItemValidator
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.external_docs.factories import (
ExternalDocumentationFactory,
)
@ -47,6 +48,7 @@ class OperationsGenerator(object):
servers = self.servers_generator.generate(servers_spec)
security = self.security_requirements_generator.generate(
security_spec)
extensions = self.extensions_generator.generate(operation_deref)
external_docs = None
if 'externalDocs' in operation_deref:
@ -68,7 +70,7 @@ class OperationsGenerator(object):
external_docs=external_docs, security=list(security),
request_body=request_body, deprecated=deprecated,
operation_id=operation_id, tags=list(tags_list),
servers=list(servers),
servers=list(servers), extensions=extensions,
),
)
@ -101,3 +103,8 @@ class OperationsGenerator(object):
@lru_cache()
def servers_generator(self):
return ServersGenerator(self.dereferencer)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -10,7 +10,7 @@ class Operation(object):
self, http_method, path_name, responses, parameters,
summary=None, description=None, external_docs=None, security=None,
request_body=None, deprecated=False, operation_id=None, tags=None,
servers=None):
servers=None, extensions=None):
self.http_method = http_method
self.path_name = path_name
self.responses = dict(responses)
@ -25,6 +25,8 @@ class Operation(object):
self.tags = tags
self.servers = servers
self.extensions = extensions and dict(extensions) or {}
def __getitem__(self, name):
return self.parameters[name]

View file

@ -2,6 +2,7 @@
from six import iteritems
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.operations.generators import OperationsGenerator
from openapi_core.schema.parameters.generators import ParametersGenerator
from openapi_core.schema.paths.models import Path
@ -29,12 +30,14 @@ class PathsGenerator(object):
servers = self.servers_generator.generate(servers_spec)
parameters = self.parameters_generator.generate_from_list(
parameters_list)
extensions = self.extensions_generator.generate(path_deref)
yield (
path_name,
Path(
path_name, list(operations), parameters=list(parameters),
summary=summary, description=description,
servers=list(servers),
servers=list(servers), extensions=extensions,
),
)
@ -52,3 +55,8 @@ class PathsGenerator(object):
@lru_cache()
def parameters_generator(self):
return ParametersGenerator(self.dereferencer, self.schemas_registry)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -7,6 +7,7 @@ class Path(object):
def __init__(
self, name, operations,
summary=None, description=None, parameters=None, servers=None,
extensions=None,
):
self.name = name
self.operations = dict(operations)
@ -15,5 +16,7 @@ class Path(object):
self.servers = servers
self.parameters = dict(parameters) if parameters else {}
self.extensions = extensions and dict(extensions) or {}
def __getitem__(self, http_method):
return self.operations[http_method]

View file

@ -1,5 +1,6 @@
"""OpenAPI core request bodies factories module"""
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.media_types.generators import MediaTypeGenerator
from openapi_core.schema.request_bodies.models import RequestBody
@ -16,9 +17,20 @@ class RequestBodyFactory(object):
content = request_body_deref['content']
media_types = self.media_types_generator.generate(content)
required = request_body_deref.get('required', False)
return RequestBody(media_types, required=required)
extensions = self.extensions_generator.generate(request_body_deref)
return RequestBody(
media_types,
required=required, extensions=extensions,
)
@property
@lru_cache()
def media_types_generator(self):
return MediaTypeGenerator(self.dereferencer, self.schemas_registry)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -7,10 +7,12 @@ from openapi_core.schema.media_types.exceptions import InvalidContentType
class RequestBody(object):
"""Represents an OpenAPI RequestBody."""
def __init__(self, content, required=False):
def __init__(self, content, required=False, extensions=None):
self.content = Content(content)
self.required = required
self.extensions = extensions and dict(extensions) or {}
def __getitem__(self, mimetype):
try:
return self.content[mimetype]

View file

@ -2,6 +2,7 @@
from six import iteritems
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.links.generators import LinksGenerator
from openapi_core.schema.media_types.generators import MediaTypeGenerator
from openapi_core.schema.parameters.generators import ParametersGenerator
@ -23,6 +24,8 @@ class ResponsesGenerator(object):
links_dict = response_deref.get('links', {})
links = self.links_generator.generate(links_dict)
extensions = self.extensions_generator.generate(response_deref)
media_types = None
if content:
media_types = self.media_types_generator.generate(content)
@ -33,7 +36,9 @@ class ResponsesGenerator(object):
yield http_status, Response(
http_status, description,
content=media_types, headers=parameters, links=links)
content=media_types, headers=parameters, links=links,
extensions=extensions,
)
@property
@lru_cache()
@ -49,3 +54,8 @@ class ResponsesGenerator(object):
@lru_cache()
def links_generator(self):
return LinksGenerator(self.dereferencer, self.schemas_registry)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -8,13 +8,15 @@ class Response(object):
def __init__(
self, http_status, description, headers=None, content=None,
links=None):
links=None, extensions=None):
self.http_status = http_status
self.description = description
self.headers = headers and dict(headers) or {}
self.content = content and Content(content) or Content()
self.links = links and dict(links) or {}
self.extensions = extensions and dict(extensions) or {}
def __getitem__(self, mimetype):
return self.get_content_type(mimetype)

View file

@ -2,6 +2,7 @@
from six import iteritems
from openapi_core.compat import lru_cache
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.servers.models import Server, ServerVariable
@ -17,17 +18,28 @@ class ServersGenerator(object):
variables_spec = server_spec.get('variables', {})
description = server_spec.get('description')
extensions = self.extensions_generator.generate(server_spec)
variables = None
if variables_spec:
variables = self.variables_generator.generate(variables_spec)
yield Server(url, variables=variables, description=description)
yield Server(
url,
variables=variables, description=description,
extensions=extensions,
)
@property
@lru_cache()
def variables_generator(self):
return ServerVariablesGenerator(self.dereferencer)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)
class ServerVariablesGenerator(object):
@ -41,5 +53,15 @@ class ServerVariablesGenerator(object):
default = variable_spec['default']
enum = variable_spec.get('enum')
variable = ServerVariable(variable_name, default, enum=enum)
extensions = self.extensions_generator.generate(variable_spec)
variable = ServerVariable(
variable_name, default,
enum=enum, extensions=extensions,
)
yield variable_name, variable
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -5,11 +5,13 @@ from six.moves.urllib.parse import urljoin
class Server(object):
def __init__(self, url, variables=None, description=None):
def __init__(self, url, variables=None, description=None, extensions=None):
self.url = url
self.variables = variables and dict(variables) or {}
self.description = description
self.extensions = extensions and dict(extensions) or {}
@property
def default_url(self):
return self.get_url()
@ -38,7 +40,9 @@ class Server(object):
class ServerVariable(object):
def __init__(self, name, default, enum=None):
def __init__(self, name, default, enum=None, extensions=None):
self.name = name
self.default = default
self.enum = enum and list(enum) or []
self.extensions = extensions and dict(extensions) or {}

View file

@ -6,6 +6,7 @@ from openapi_spec_validator.validators import Dereferencer
from openapi_core.compat import lru_cache
from openapi_core.schema.components.factories import ComponentsFactory
from openapi_core.schema.extensions.generators import ExtensionsGenerator
from openapi_core.schema.infos.factories import InfoFactory
from openapi_core.schema.paths.generators import PathsGenerator
from openapi_core.schema.schemas.registries import SchemaRegistry
@ -39,6 +40,8 @@ class SpecFactory(object):
{'url': '/'},
]
extensions = self.extensions_generator.generate(spec_dict_deref)
info = self.info_factory.create(info_spec)
servers = self.servers_generator.generate(servers_spec)
paths = self.paths_generator.generate(paths)
@ -49,7 +52,7 @@ class SpecFactory(object):
spec = Spec(
info, list(paths), servers=list(servers), components=components,
security=list(security),
security=list(security), extensions=extensions,
_resolver=self.spec_resolver,
)
return spec
@ -88,3 +91,8 @@ class SpecFactory(object):
@lru_cache()
def security_requirements_generator(self):
return SecurityRequirementsGenerator(self.dereferencer)
@property
@lru_cache()
def extensions_generator(self):
return ExtensionsGenerator(self.dereferencer)

View file

@ -16,13 +16,15 @@ class Spec(object):
def __init__(
self, info, paths, servers=None, components=None,
security=None, _resolver=None):
security=None, extensions=None, _resolver=None):
self.info = info
self.paths = paths and dict(paths)
self.servers = servers or []
self.components = components
self.security = security
self.extensions = extensions and dict(extensions) or {}
self._resolver = _resolver
def __getitem__(self, path_pattern):