Skip to content

Commit

Permalink
Merge pull request #358 from nickgaya/collapse-required-properties
Browse files Browse the repository at this point in the history
Collapse required properties from inherited models
  • Loading branch information
sjaensch authored Nov 5, 2019
2 parents 265d63f + dbbb34a commit c46ee32
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 76 deletions.
3 changes: 2 additions & 1 deletion bravado_core/marshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from bravado_core.model import Model
from bravado_core.model import MODEL_MARKER
from bravado_core.schema import collapsed_properties
from bravado_core.schema import collapsed_required
from bravado_core.schema import get_type_from_schema
from bravado_core.schema import is_dict_like
from bravado_core.schema import is_list_like
Expand Down Expand Up @@ -335,7 +336,7 @@ def _marshaling_method_object(swagger_spec, object_schema):
)

properties = collapsed_properties(object_schema, swagger_spec)
required_properties = set(object_schema.get('required', []))
required_properties = collapsed_required(object_schema, swagger_spec)
properties_to_marshaling_function = {
prop_name: _get_marshaling_method(
swagger_spec=swagger_spec,
Expand Down
23 changes: 23 additions & 0 deletions bravado_core/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,29 @@ def collapsed_properties(model_spec, swagger_spec):
return properties


def collapsed_required(model_spec, swagger_spec):
"""
Processes model spec and outputs a set of required properties for the model.
This handles traversing any polymorphic models and the hierarchy
of properties properly.
:param model_spec: model specification (must be dereferenced already)
:type model_spec: dict
:param swagger_spec: :class:`bravado_core.spec.Spec`
:returns: Set of required properties.
"""
required = set(model_spec.get('required', []))

if 'allOf' in model_spec:
deref = swagger_spec.deref
for item_spec in model_spec['allOf']:
item_spec = deref(item_spec)
required.update(collapsed_required(item_spec, swagger_spec))

return required


def get_type_from_schema(swagger_spec, schema_object_spec):
try:
return schema_object_spec['type']
Expand Down
3 changes: 2 additions & 1 deletion bravado_core/unmarshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from bravado_core.exception import SwaggerMappingError
from bravado_core.model import MODEL_MARKER
from bravado_core.schema import collapsed_properties
from bravado_core.schema import collapsed_required
from bravado_core.schema import get_type_from_schema
from bravado_core.schema import is_dict_like
from bravado_core.schema import is_list_like
Expand Down Expand Up @@ -337,7 +338,7 @@ def _unmarshaling_method_object(swagger_spec, object_schema, use_models=True):
model_type = None

properties = collapsed_properties(object_schema, swagger_spec)
required_properties = object_schema.get('required', [])
required_properties = collapsed_required(object_schema, swagger_spec)
properties_to_unmarshaling_function = {
prop_name: _get_unmarshaling_method(
swagger_spec=swagger_spec,
Expand Down
74 changes: 0 additions & 74 deletions tests/schema/collapsed_properties_test.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,5 @@
# -*- coding: utf-8 -*-
import pytest

from bravado_core.schema import collapsed_properties
from bravado_core.spec import Spec


@pytest.fixture
def users_spec():
return {
"User": {
"properties": {
"id": {
"type": "integer",
"format": "int64",
},
"username": {
"type": "string",
},
"email": {
"type": "string",
},
"password": {
"type": "string",
},
},
},
"VIP": {
"allOf": [
{
"$ref": "#/definitions/User",
},
{
"properties": {
"vip_pass_no": {
"type": "string",
},
},
},
],
},
"Admin": {
"allOf": [
{
"$ref": "#/definitions/User",
},
{
"type": "object",
"properties": {
"permissions": {
"type": "array",
"items": {
"type": "string",
},
},
},
},
],
},
"SuperUser": {
"allOf": [
{
"$ref": "#/definitions/Admin",
},
{
"$ref": "#/definitions/VIP",
},
],
},
}


@pytest.fixture
def users_swagger_spec(minimal_swagger_dict, users_spec):
minimal_swagger_dict['definitions'] = users_spec
return Spec.from_dict(minimal_swagger_dict)


def test_allOf(users_spec, users_swagger_spec):
Expand Down
14 changes: 14 additions & 0 deletions tests/schema/collapsed_required_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from bravado_core.schema import collapsed_required


def test_allOf(users_spec, users_swagger_spec):
"""Test allOf functionality, including:
- multiple levels of allOf
- multiple references within one allOf
- referencing the same model multiple times across the
allOf-hierarchy
"""
superuser_spec = users_spec['SuperUser']
required = collapsed_required(superuser_spec, users_swagger_spec)
assert required == {'id', 'username', 'password', 'permissions'}
77 changes: 77 additions & 0 deletions tests/schema/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
import pytest

from bravado_core.spec import Spec


@pytest.fixture
def users_spec():
return {
"User": {
"properties": {
"id": {
"type": "integer",
"format": "int64",
},
"username": {
"type": "string",
},
"email": {
"type": "string",
},
"password": {
"type": "string",
},
},
"required": ["id", "username", "password"],
},
"VIP": {
"allOf": [
{
"$ref": "#/definitions/User",
},
{
"properties": {
"vip_pass_no": {
"type": "string",
},
},
},
],
},
"Admin": {
"allOf": [
{
"$ref": "#/definitions/User",
},
{
"type": "object",
"properties": {
"permissions": {
"type": "array",
"items": {
"type": "string",
},
},
},
"required": ["permissions"],
},
],
},
"SuperUser": {
"allOf": [
{
"$ref": "#/definitions/Admin",
},
{
"$ref": "#/definitions/VIP",
},
],
},
}


@pytest.fixture
def users_swagger_spec(minimal_swagger_dict, users_spec):
minimal_swagger_dict['definitions'] = users_spec
return Spec.from_dict(minimal_swagger_dict)

0 comments on commit c46ee32

Please sign in to comment.