samtranslator/validator/value_validator.py (79 lines of code) (raw):
"""A plug-able validator to help raise exception when some value is unexpected."""
from typing import Any, Dict, Generic, Optional, TypeVar, cast
from samtranslator.model.exceptions import (
ExpectedType,
InvalidEventException,
InvalidResourceAttributeTypeException,
InvalidResourceException,
InvalidResourcePropertyTypeException,
)
T = TypeVar("T")
class _ResourcePropertyValueValidator(Generic[T]):
value: Optional[T]
resource_id: str
key_path: str
is_sam_event: bool
is_resource_attribute: bool
def __init__(
self,
value: Optional[T],
resource_id: str,
key_path: str,
is_sam_event: bool = False,
is_resource_attribute: bool = False,
) -> None:
self.value = value
self.resource_id = resource_id
self.key_path = key_path
self.is_sam_event = is_sam_event
self.is_resource_attribute = is_resource_attribute
@property
def resource_logical_id(self) -> Optional[str]:
return None if self.is_sam_event else self.resource_id
@property
def event_id(self) -> Optional[str]:
return self.resource_id if self.is_sam_event else None
def to_be_a(self, expected_type: ExpectedType, message: Optional[str] = "") -> T:
"""
Validate the type of the value and return the value if valid.
raise InvalidResourceException for invalid values.
"""
type_description, type_class = expected_type.value
if not isinstance(self.value, type_class):
if self.event_id:
raise InvalidEventException(
self.event_id, message or f"Property '{self.key_path}' should be a {type_description}."
)
if self.resource_logical_id:
if self.is_resource_attribute:
raise InvalidResourceAttributeTypeException(
self.resource_logical_id, self.key_path, expected_type, message
)
raise InvalidResourcePropertyTypeException(
self.resource_logical_id, self.key_path, expected_type, message
)
raise RuntimeError("event_id and resource_logical_id are both None")
# mypy is not smart to derive class from expected_type.value[1], ignore types:
return self.value # type: ignore
def to_not_be_none(self, message: Optional[str] = "") -> T:
"""
Validate the value is not None and return the value if valid.
raise InvalidResourceException for None values.
"""
if self.value is None:
if not message:
message = f"Property '{self.key_path}' is required."
if self.event_id:
raise InvalidEventException(self.event_id, message)
if self.resource_logical_id:
raise InvalidResourceException(self.resource_logical_id, message)
raise RuntimeError("event_id and resource_logical_id are both None")
return self.value
#
# alias methods:
#
def to_be_a_map(self, message: Optional[str] = "") -> Dict[str, Any]:
"""
Return the value with type hint "Dict[str, Any]".
Raise InvalidResourceException/InvalidEventException if the value is not.
"""
return cast(Dict[str, Any], self.to_be_a(ExpectedType.MAP, message))
def to_be_a_list(self, message: Optional[str] = "") -> T:
return self.to_be_a(ExpectedType.LIST, message)
def to_be_a_list_of(self, expected_type: ExpectedType, message: Optional[str] = "") -> T:
"""
Return the value with type hint "List[T]".
Raise InvalidResourceException/InvalidEventException if the value is not.
"""
value = self.to_be_a(ExpectedType.LIST, message)
for index, item in enumerate(value): # type: ignore
sam_expect(item, self.resource_id, f"{self.key_path}[{index}]", is_sam_event=self.is_sam_event).to_be_a(
expected_type, message
)
return value
def to_be_a_string(self, message: Optional[str] = "") -> str:
"""
Return the value with type hint "str".
Raise InvalidResourceException/InvalidEventException if the value is not.
"""
return cast(str, self.to_be_a(ExpectedType.STRING, message))
def to_be_an_integer(self, message: Optional[str] = "") -> int:
"""
Return the value with type hint "int".
Raise InvalidResourceException/InvalidEventException if the value is not.
"""
return cast(int, self.to_be_a(ExpectedType.INTEGER, message))
def to_be_a_bool(self, message: Optional[str] = "") -> bool:
"""
Return the value with type hint "bool".
Raise InvalidResourceException/InvalidEventException if the value is not.
"""
return cast(bool, self.to_be_a(ExpectedType.BOOLEAN, message))
sam_expect = _ResourcePropertyValueValidator