Skip to content

Commit

Permalink
fix: don't validate primitive attributes of type any
Browse files Browse the repository at this point in the history
  • Loading branch information
soofstad committed Nov 1, 2023
1 parent 91718cf commit 2d7a41a
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 8 deletions.
14 changes: 8 additions & 6 deletions src/common/entity/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def is_blueprint_instance_of(


def _validate_primitive_attribute(attribute: BlueprintAttribute, value: bool | int | float | str, key: str):
if attribute.attribute_type == BuiltinDataTypes.ANY.value:
return # If type is "any", no need to validate further
python_type = BuiltinDataTypes(attribute.attribute_type).to_py_type()
if attribute.attribute_type == "number" and isinstance(value, int): # float is considered a superset containing int
return
Expand Down Expand Up @@ -143,12 +145,6 @@ def _validate_entity(
len(attributeDefinition.dimensions.dimensions) if attributeDefinition.dimensions else 0,
implementation_mode,
)
elif attributeDefinition.is_primitive:
_validate_primitive_attribute(
attributeDefinition,
entity[attributeDefinition.name],
f"{key}.{attributeDefinition.name}",
)
elif attributeDefinition.attribute_type == "any" and attributeDefinition.name == "default":
default_attribute_definition = BlueprintAttribute(
name=attributeDefinition.name,
Expand Down Expand Up @@ -178,6 +174,12 @@ def _validate_entity(
f"{key}.{default_attribute_definition.name}",
"extend",
)
elif attributeDefinition.is_primitive:
_validate_primitive_attribute(
attributeDefinition,
entity[attributeDefinition.name],
f"{key}.{attributeDefinition.name}",
)
else:
_validate_complex_attribute(
attributeDefinition,
Expand Down
6 changes: 5 additions & 1 deletion src/domain_classes/dimension.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Convert dmt attribute_types to python types. If complex, return type as string.
from typing import Any

from enums import BuiltinDataTypes


Expand Down Expand Up @@ -27,7 +29,9 @@ def __init__(self, dimensions: str, attribute_type: str):
a string value is stored.
"""
self.dimensions: list[str] = dimensions.split(",")
self.type: type[bool] | type[int] | type[float] | type[str] | str = _get_data_type_from_dmt_type(attribute_type)
self.type: type[bool] | type[int] | type[float] | type[str] | str | Any = _get_data_type_from_dmt_type(
attribute_type
)
self.value = None

def is_array(self) -> bool:
Expand Down
6 changes: 5 additions & 1 deletion src/enums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from enum import Enum
from typing import Any

PRIMITIVES = {"string", "number", "integer", "boolean"}
PRIMITIVES = {"string", "number", "integer", "boolean", "any"}


class Protocols(Enum):
Expand All @@ -14,6 +15,7 @@ class BuiltinDataTypes(Enum):
BOOL = "boolean"
OBJECT = "object" # Any complex type (i.e. any blueprint type)
BINARY = "binary"
ANY = "any"

def to_py_type(self):
if self is BuiltinDataTypes.BOOL:
Expand All @@ -26,6 +28,8 @@ def to_py_type(self):
return str
elif self is BuiltinDataTypes.OBJECT:
return dict
elif self is BuiltinDataTypes.ANY:
return Any


class RepositoryType(Enum):
Expand Down
37 changes: 37 additions & 0 deletions src/tests/bdd/entity/validate_default_entity.feature
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ Feature: Validate Default Entity
"address": "$AnimalBlueprint",
"type": "dmss://system/SIMOS/Reference",
"referenceType": "storage"
},
{
"address": "$SomeBlueprint",
"type": "dmss://system/SIMOS/Reference",
"referenceType": "storage"
}
]
}
Expand Down Expand Up @@ -189,6 +194,24 @@ Feature: Validate Default Entity
}
"""

Given there exist document with id "SomeBlueprint" in data source "data-source-name"
"""
{
"name": "SomeBlueprint",
"type": "dmss://system/SIMOS/Blueprint",
"attributes": [
{
"name": "pets",
"type": "dmss://system/SIMOS/BlueprintAttribute",
"attributeType": "number",
"label": "Number of pets",
"optional": false,
"default": "five"
}
]
}
"""

Scenario: Validate existing simple example

Given i access the resource url "/api/entity/validate-existing-entity/data-source-name/root_package/PersonBlueprint"
Expand All @@ -207,4 +230,18 @@ Feature: Validate Default Entity
"debug": "Location: Entity in key '^.attributes.0.default'",
"data": null
}
"""

Given i access the resource url "/api/entity/validate-existing-entity/data-source-name/root_package/SomeBlueprint"
When i make a "POST" request
Then the response status should be "Bad Request"
And the response should contain
"""
{
"status": 400,
"type": "ValidationException",
"message": "Attribute 'default' should be type 'float'. Got 'str'. Value: five",
"debug": "Location: Entity in key '^.attributes.0.default'",
"data": null
}
"""
50 changes: 50 additions & 0 deletions src/tests/unit/common/tree/test_tree_node_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from common.address import Address
from common.exceptions import BadRequestException, ValidationException
from common.tree.tree_node import Node
from common.tree.tree_node_serializer import tree_node_from_dict
from enums import REFERENCE_TYPES, SIMOS
from features.document.use_cases.add_document_use_case import add_document_use_case
from features.document.use_cases.update_document_use_case import (
Expand All @@ -22,6 +23,9 @@ def setUp(self) -> None:
simos_blueprints = [
"dmss://system/SIMOS/NamedEntity",
"dmss://system/SIMOS/Reference",
"dmss://system/SIMOS/Action",
"dmss://system/SIMOS/Blueprint",
"dmss://system/SIMOS/BlueprintAttribute",
]
mock_blueprint_folder = "src/tests/unit/common/tree/mock_data/mock_blueprints"
mock_blueprints_and_file_names = {
Expand Down Expand Up @@ -163,6 +167,52 @@ def test_add_optional_nested(self):

self.assertDictEqual(self.doc_storage["1"], entity_after)

def test_update_blueprint_attribute_default_with_int(self):
blueprint = self.mock_blueprint_provider.get_blueprint("dmss://system/SIMOS/Action").to_dict()
blueprint_node = tree_node_from_dict(blueprint, self.mock_blueprint_provider.get_blueprint)
attribute_node = blueprint_node.get_by_path(["attributes", "7"])

new_value = {
"type": "dmss://system/SIMOS/BlueprintAttribute",
"name": "method",
"description": "What method to call inside runnable.tsx",
"attributeType": "number",
"default": 67,
}
attribute_node.update(new_value)
self.assertEqual(attribute_node.entity["default"], 67)

def test_update_blueprint_attribute_default_with_dict(self):
blueprint = self.mock_blueprint_provider.get_blueprint("dmss://system/SIMOS/Action").to_dict()
blueprint_node = tree_node_from_dict(blueprint, self.mock_blueprint_provider.get_blueprint)
attribute_node = blueprint_node.get_by_path(["attributes", "7"])

new_value = {
"type": "dmss://system/SIMOS/BlueprintAttribute",
"name": "method",
"description": "What method to call inside runnable.tsx",
# There is no check if default value matches 'attributeType' in node.update(). It should happen before...
"attributeType": "number",
"default": {"type": "test"},
}
attribute_node.update(new_value)
self.assertDictEqual(attribute_node.entity["default"], {"type": "test"})

def test_update_blueprint_attribute_default_with_bool(self):
blueprint = self.mock_blueprint_provider.get_blueprint("dmss://system/SIMOS/Action").to_dict()
blueprint_node = tree_node_from_dict(blueprint, self.mock_blueprint_provider.get_blueprint)
attribute_node = blueprint_node.get_by_path(["attributes", "7"])

new_value = {
"type": "dmss://system/SIMOS/BlueprintAttribute",
"name": "method",
"description": "What method to call inside runnable.tsx",
"attributeType": "number",
"default": True,
}
attribute_node.update(new_value)
self.assertEqual(attribute_node.entity["default"], True)

def test_add_duplicate(self):
self.doc_storage = {
"1": {
Expand Down

0 comments on commit 2d7a41a

Please sign in to comment.