Skip to content

Commit

Permalink
Merge pull request #331 from proteanhq/196-restrict-list-to-specific-…
Browse files Browse the repository at this point in the history
…content-type

Restrict `List` datatype to specific content types
  • Loading branch information
advishnuprasad authored Aug 6, 2020
2 parents cf9364a + 62ca154 commit dc239ad
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/protean/core/field/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,43 @@ class List(Field):

default_error_messages = {
"invalid": '"{value}" value must be of list type.',
"invalid_content": "Invalid value",
}

def __init__(self, content_type=String, **kwargs):
if content_type not in [
String,
Integer,
Identifier,
Float,
Date,
DateTime,
Boolean,
]:
raise ValidationError({"content_type": ["Content type not supported"]})
self.content_type = content_type

super().__init__(**kwargs)

def _cast_to_type(self, value):
""" Raise error if the value is not a list """
""" Raise errors if the value is not a list, or
the items in the list are not of the right data type.
"""
if not isinstance(value, list):
self.fail("invalid", value=value)

# Try to cast value into the destination type.
# Throw error if the underlying type does not support value.
new_value = []
try:
for item in value:
new_value.append(self.content_type()._load(item))
except ValidationError:
self.fail("invalid_content", value=value)

if new_value != value:
self.fail("invalid_content", value=value)

return value


Expand Down Expand Up @@ -176,7 +207,9 @@ def __init__(self, content_type=String, **kwargs):
super().__init__(**kwargs)

def _cast_to_type(self, value):
""" Raise error if the value is not a list """
""" Raise errors if the value is not a list, or
the items in the list are not of the right data type.
"""
if not isinstance(value, list):
self.fail("invalid", value=value)

Expand Down
89 changes: 89 additions & 0 deletions tests/impl/model/dict_model/test_list_datatype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest

from datetime import datetime

from protean.core.aggregate import BaseAggregate
from protean.core.exceptions import ValidationError
from protean.core.field.basic import (
List,
Auto,
Boolean,
Date,
DateTime,
Float,
Identifier,
Integer,
String,
)


class ListUser(BaseAggregate):
email = String(max_length=255, required=True, unique=True)
roles = List() # Defaulted to String Content Type


class IntegerListUser(BaseAggregate):
email = String(max_length=255, required=True, unique=True)
roles = List(content_type=Integer)


@pytest.mark.postgresql
def test_basic_array_data_type_support(test_domain):
test_domain.register(ListUser)

model_cls = test_domain.get_model(ListUser)
user = ListUser(email="[email protected]", roles=["ADMIN", "USER"])
user_model_obj = model_cls.from_entity(user)

user_copy = model_cls.to_entity(user_model_obj)
assert user_copy is not None
assert user_copy.roles == ["ADMIN", "USER"]


@pytest.mark.postgresql
def test_array_content_type_validation(test_domain):
test_domain.register(ListUser)
test_domain.register(IntegerListUser)

for kwargs in [
{"email": "[email protected]", "roles": [1, 2]},
{"email": "[email protected]", "roles": ["1", 2]},
{"email": "[email protected]", "roles": [1.0, 2.0]},
{"email": "[email protected]", "roles": [datetime.utcnow()]},
]:
with pytest.raises(ValidationError) as exception:
ListUser(**kwargs)
assert exception.value.messages == {"roles": ["Invalid value"]}

model_cls = test_domain.get_model(IntegerListUser)
user = IntegerListUser(email="[email protected]", roles=[1, 2])
user_model_obj = model_cls.from_entity(user)

user_copy = model_cls.to_entity(user_model_obj)
assert user_copy is not None
assert user_copy.roles == [1, 2]

for kwargs in [
{"email": "[email protected]", "roles": ["ADMIN", "USER"]},
{"email": "[email protected]", "roles": ["1", "2"]},
{"email": "[email protected]", "roles": [datetime.utcnow()]},
]:
with pytest.raises(ValidationError) as exception:
IntegerListUser(**kwargs)
assert exception.value.messages == {"roles": ["Invalid value"]}


@pytest.mark.postgresql
def test_that_only_specific_primitive_types_are_allowed_as_content_types(test_domain):
List(content_type=String)
List(content_type=Identifier)
List(content_type=Integer)
List(content_type=Float)
List(content_type=Boolean)
List(content_type=Date)
List(content_type=DateTime)

with pytest.raises(ValidationError) as error:
List(content_type=Auto)

assert error.value.messages == {"content_type": ["Content type not supported"]}

0 comments on commit dc239ad

Please sign in to comment.