Skip to content

Commit af07200

Browse files
committed
Model extension refactor
1 parent 11f0233 commit af07200

File tree

6 files changed

+76
-40
lines changed

6 files changed

+76
-40
lines changed

openapi_core/extensions/models/factories.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@
44

55
class ModelFactory(object):
66

7-
def create(self, properties, name=None):
8-
model = BaseModel
9-
if name is not None:
10-
model = type(name, (BaseModel, ), {})
7+
base_class = BaseModel
8+
default_name = 'Model'
119

12-
return model(**properties)
10+
def __init__(self, name=None):
11+
self.name = name or self.default_name
12+
13+
def __call__(self, **properties):
14+
model_class = self._get_model_class(**properties)
15+
return model_class()
16+
17+
def _get_model_class(self, **attrs):
18+
return type(self.name, (self.base_class, ), attrs)
Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
"""OpenAPI X-Model extension models module"""
22

33

4-
class BaseModel(dict):
5-
"""Base class for OpenAPI X-Model."""
6-
7-
def __getattr__(self, attr_name):
8-
"""Only search through properties if attribute not found normally.
9-
:type attr_name: str
10-
"""
11-
try:
12-
return self[attr_name]
13-
except KeyError:
14-
raise AttributeError(
15-
'type object {0!r} has no attribute {1!r}'
16-
.format(type(self).__name__, attr_name)
17-
)
4+
class BaseModel(object):
5+
"""Base class for OpenAPI models."""

openapi_core/schema/schemas/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,11 @@ def _unmarshal_object(self, value):
169169
else:
170170
properties = self._unmarshal_properties(value)
171171

172-
return ModelFactory().create(properties, name=self.model)
172+
return self.model_factory(**properties)
173+
174+
@property
175+
def model_factory(self):
176+
return ModelFactory(self.model)
173177

174178
def _unmarshal_properties(self, value, one_of_schema=None):
175179
all_props = self.get_all_properties()

tests/integration/test_models.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import json
2+
import pytest
3+
4+
from openapi_core.extensions.models.models import BaseModel
5+
from openapi_core.schema.media_types.exceptions import (
6+
InvalidContentType, InvalidMediaTypeValue,
7+
)
8+
from openapi_core.schema.operations.exceptions import InvalidOperation
9+
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
10+
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
11+
from openapi_core.schema.responses.exceptions import (
12+
MissingResponseContent, InvalidResponse,
13+
)
14+
from openapi_core.schema.servers.exceptions import InvalidServer
15+
from openapi_core.shortcuts import create_spec
16+
from openapi_core.validation.request.validators import RequestValidator
17+
from openapi_core.validation.response.validators import ResponseValidator
18+
from openapi_core.wrappers.mock import MockRequest, MockResponse
19+
20+
21+
class TestModelsExtension(object):
22+
23+
host_url = 'http://petstore.swagger.io'
24+
25+
@pytest.fixture
26+
def spec_dict(self, factory):
27+
return factory.spec_from_file("data/v3.0/petstore.yaml")
28+
29+
@pytest.fixture
30+
def spec(self, spec_dict):
31+
return create_spec(spec_dict)
32+
33+
@pytest.fixture
34+
def validator(self, spec):
35+
return ResponseValidator(spec)
36+
37+
def test_get_pets(self, validator):
38+
request = MockRequest(self.host_url, 'get', '/v1/pets')
39+
response_json = {
40+
'data': [
41+
{
42+
'id': 1,
43+
'name': 'Sparky'
44+
},
45+
],
46+
}
47+
response_data = json.dumps(response_json)
48+
response = MockResponse(response_data)
49+
50+
result = validator.validate(request, response)
51+
52+
assert result.errors == []
53+
assert list(result.data.keys()) == ['data', ]
54+
assert len(result.data['data']) == 1
55+
assert isinstance(result.data['data'][0], BaseModel)
56+
assert result.data['data'][0].id == 1
57+
assert result.data['data'][0].name == 'Sparky'

tests/integration/test_petstore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ def test_get_pet(self, spec, response_validator):
638638
response_result = response_validator.validate(request, response)
639639

640640
assert response_result.errors == []
641-
assert response_result.data == data_json
641+
assert list(response_result.data.keys()) == ['data', ]
642642

643643
def test_get_pet_not_found(self, spec, response_validator):
644644
host_url = 'http://petstore.swagger.io/v1'

tests/integration/test_validators.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -268,22 +268,3 @@ def test_invalid_value(self, validator):
268268
assert type(result.errors[0]) == InvalidMediaTypeValue
269269
assert result.data is None
270270
assert result.headers == {}
271-
272-
def test_get_pets(self, validator):
273-
request = MockRequest(self.host_url, 'get', '/v1/pets')
274-
response_json = {
275-
'data': [
276-
{
277-
'id': 1,
278-
'name': 'Sparky'
279-
},
280-
],
281-
}
282-
response_data = json.dumps(response_json)
283-
response = MockResponse(response_data)
284-
285-
result = validator.validate(request, response)
286-
287-
assert result.errors == []
288-
assert result.data == response_json
289-
assert result.headers == {}

0 commit comments

Comments
 (0)