From e128fd59f15210ef21f0c46b2c5160bb6e580c0f Mon Sep 17 00:00:00 2001 From: p1c2u Date: Tue, 30 Mar 2021 11:12:36 +0100 Subject: [PATCH] Schema content refactor --- openapi_core/schema/content/factories.py | 21 +++++++++++++ openapi_core/schema/parameters/factories.py | 14 ++++++++- openapi_core/schema/parameters/models.py | 3 +- .../schema/request_bodies/factories.py | 12 ++++---- openapi_core/schema/request_bodies/models.py | 3 +- tests/integration/data/v3.0/petstore.yaml | 14 +++++++++ tests/integration/schema/test_spec.py | 30 +++++++++++++++++++ tests/integration/validation/test_petstore.py | 10 +++---- 8 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 openapi_core/schema/content/factories.py diff --git a/openapi_core/schema/content/factories.py b/openapi_core/schema/content/factories.py new file mode 100644 index 0000000..8c7b4f0 --- /dev/null +++ b/openapi_core/schema/content/factories.py @@ -0,0 +1,21 @@ +"""OpenAPI core content factories module""" +from openapi_core.compat import lru_cache +from openapi_core.schema.content.models import Content +from openapi_core.schema.media_types.generators import MediaTypeGenerator + + +class ContentFactory(object): + + def __init__(self, dereferencer, schemas_registry): + self.dereferencer = dereferencer + self.schemas_registry = schemas_registry + + def create(self, content_spec): + media_types = self.media_types_generator.generate(content_spec) + + return Content(media_types) + + @property + @lru_cache() + def media_types_generator(self): + return MediaTypeGenerator(self.dereferencer, self.schemas_registry) diff --git a/openapi_core/schema/parameters/factories.py b/openapi_core/schema/parameters/factories.py index 2e13ceb..6689366 100644 --- a/openapi_core/schema/parameters/factories.py +++ b/openapi_core/schema/parameters/factories.py @@ -1,4 +1,6 @@ """OpenAPI core parameters factories module""" +from openapi_core.compat import lru_cache +from openapi_core.schema.content.factories import ContentFactory from openapi_core.schema.parameters.models import Parameter @@ -25,9 +27,19 @@ class ParameterFactory(object): if schema_spec: schema, _ = self.schemas_registry.get_or_create(schema_spec) + content_spec = parameter_deref.get('content', None) + content = None + if content_spec: + content = self.content_factory.create(content_spec) + return Parameter( parameter_name, parameter_in, schema=schema, required=required, allow_empty_value=allow_empty_value, - style=style, explode=explode, + style=style, explode=explode, content=content, ) + + @property + @lru_cache() + def content_factory(self): + return ContentFactory(self.dereferencer, self.schemas_registry) diff --git a/openapi_core/schema/parameters/models.py b/openapi_core/schema/parameters/models.py index e99194f..e1b1ff9 100644 --- a/openapi_core/schema/parameters/models.py +++ b/openapi_core/schema/parameters/models.py @@ -15,7 +15,7 @@ class Parameter(object): def __init__( self, name, location, schema=None, required=False, deprecated=False, allow_empty_value=False, - items=None, style=None, explode=None): + items=None, style=None, explode=None, content=None): self.name = name self.location = ParameterLocation(location) self.schema = schema @@ -30,6 +30,7 @@ class Parameter(object): self.items = items self.style = ParameterStyle(style or self.default_style) self.explode = self.default_explode if explode is None else explode + self.content = content @property def aslist(self): diff --git a/openapi_core/schema/request_bodies/factories.py b/openapi_core/schema/request_bodies/factories.py index a8a756e..620b92e 100644 --- a/openapi_core/schema/request_bodies/factories.py +++ b/openapi_core/schema/request_bodies/factories.py @@ -1,7 +1,7 @@ """OpenAPI core request bodies factories module""" from openapi_core.compat import lru_cache +from openapi_core.schema.content.factories import ContentFactory 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 @@ -14,21 +14,21 @@ class RequestBodyFactory(object): def create(self, request_body_spec): request_body_deref = self.dereferencer.dereference( request_body_spec) - content = request_body_deref['content'] - media_types = self.media_types_generator.generate(content) + content_spec = request_body_deref['content'] + content = self.content_factory.create(content_spec) required = request_body_deref.get('required', False) extensions = self.extensions_generator.generate(request_body_deref) return RequestBody( - media_types, + content, required=required, extensions=extensions, ) @property @lru_cache() - def media_types_generator(self): - return MediaTypeGenerator(self.dereferencer, self.schemas_registry) + def content_factory(self): + return ContentFactory(self.dereferencer, self.schemas_registry) @property @lru_cache() diff --git a/openapi_core/schema/request_bodies/models.py b/openapi_core/schema/request_bodies/models.py index 925112d..42ed772 100644 --- a/openapi_core/schema/request_bodies/models.py +++ b/openapi_core/schema/request_bodies/models.py @@ -1,6 +1,5 @@ """OpenAPI core request bodies models module""" from openapi_core.schema.content.exceptions import MimeTypeNotFound -from openapi_core.schema.content.models import Content from openapi_core.schema.media_types.exceptions import InvalidContentType @@ -8,7 +7,7 @@ class RequestBody(object): """Represents an OpenAPI RequestBody.""" def __init__(self, content, required=False, extensions=None): - self.content = Content(content) + self.content = content self.required = required self.extensions = extensions and dict(extensions) or {} diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml index 1785a5b..b226173 100644 --- a/tests/integration/data/v3.0/petstore.yaml +++ b/tests/integration/data/v3.0/petstore.yaml @@ -67,6 +67,20 @@ paths: items: $ref: "#/components/schemas/Tag" explode: false + - name: coordinates + in: query + content: + application/json: + schema: + type: object + required: + - lat + - long + properties: + lat: + type: number + long: + type: number responses: '200': $ref: "#/components/responses/PetsResponse" diff --git a/tests/integration/schema/test_spec.py b/tests/integration/schema/test_spec.py index 0537306..6e208bc 100644 --- a/tests/integration/schema/test_spec.py +++ b/tests/integration/schema/test_spec.py @@ -231,6 +231,36 @@ class TestPetstore(object): assert parameter.schema.required == schema_spec.get( 'required', []) + content_spec = parameter_spec.get('content') + assert bool(content_spec) == bool(parameter.content) + + if not content_spec: + continue + + for mimetype, media_type in iteritems( + parameter.content): + assert type(media_type) == MediaType + assert media_type.mimetype == mimetype + + media_spec = parameter_spec['content'][mimetype] + schema_spec = media_spec.get('schema') + assert bool(schema_spec) == bool(media_type.schema) + + if not schema_spec: + continue + + # @todo: test with defererence + if '$ref' in schema_spec: + continue + + assert type(media_type.schema) == Schema + assert media_type.schema.type.value ==\ + schema_spec['type'] + assert media_type.schema.format ==\ + schema_spec.get('format') + assert media_type.schema.required == \ + schema_spec.get('required', False) + request_body_spec = operation_spec.get('requestBody') assert bool(request_body_spec) == bool(operation.request_body) diff --git a/tests/integration/validation/test_petstore.py b/tests/integration/validation/test_petstore.py index c483c03..24c00b4 100644 --- a/tests/integration/validation/test_petstore.py +++ b/tests/integration/validation/test_petstore.py @@ -40,23 +40,23 @@ class TestPetstore(object): api_key_bytes_enc = b64encode(api_key_bytes) return text_type(api_key_bytes_enc, 'utf8') - @pytest.fixture + @pytest.fixture(scope='module') def spec_uri(self): return "file://tests/integration/data/v3.0/petstore.yaml" - @pytest.fixture + @pytest.fixture(scope='module') def spec_dict(self, factory): return factory.spec_from_file("data/v3.0/petstore.yaml") - @pytest.fixture + @pytest.fixture(scope='module') def spec(self, spec_dict, spec_uri): return create_spec(spec_dict, spec_uri) - @pytest.fixture + @pytest.fixture(scope='module') def request_validator(self, spec): return RequestValidator(spec) - @pytest.fixture + @pytest.fixture(scope='module') def response_validator(self, spec): return ResponseValidator(spec)