Skip to content

Commit d50e261

Browse files
committed
improv: refactor structure to fit with utilities
1 parent 7c55154 commit d50e261

File tree

14 files changed

+204
-149
lines changed

14 files changed

+204
-149
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Validation utility
2+
"""
3+
from .envelopes import DynamoDBEnvelope, EventBridgeEnvelope, UserEnvelope
4+
from .validator import validator
5+
6+
__all__ = ["UserEnvelope", "DynamoDBEnvelope", "EventBridgeEnvelope", "validator"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from .base import UserEnvelope
2+
from .dynamodb import DynamoDBEnvelope
3+
from .event_bridge import EventBridgeEnvelope
4+
5+
__all__ = [
6+
"UserEnvelope",
7+
"DynamoDBEnvelope",
8+
"EventBridgeEnvelope",
9+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import logging
2+
from abc import ABC, abstractmethod
3+
from typing import Any, Dict
4+
5+
from pydantic import BaseModel, ValidationError
6+
7+
logger = logging.getLogger(__name__)
8+
9+
10+
class BaseEnvelope(ABC):
11+
def _parse_user_dict_schema(self, user_event: Dict[str, Any], inbound_schema_model: BaseModel) -> Any:
12+
logger.debug("parsing user dictionary schema")
13+
try:
14+
return inbound_schema_model(**user_event)
15+
except (ValidationError, TypeError):
16+
logger.exception("Valdation exception while extracting user custom schema")
17+
raise
18+
19+
def _parse_user_json_string_schema(self, user_event: str, inbound_schema_model: BaseModel) -> Any:
20+
logger.debug("parsing user dictionary schema")
21+
try:
22+
return inbound_schema_model.parse_raw(user_event)
23+
except (ValidationError, TypeError):
24+
logger.exception("Valdation exception while extracting user custom schema")
25+
raise
26+
27+
@abstractmethod
28+
def parse(self, event: Dict[str, Any], inbound_schema_model: BaseModel) -> Any:
29+
return NotImplemented
30+
31+
32+
class UserEnvelope(BaseEnvelope):
33+
def parse(self, event: Dict[str, Any], inbound_schema_model: BaseModel) -> Any:
34+
try:
35+
return inbound_schema_model(**event)
36+
except (ValidationError, TypeError):
37+
logger.exception("Valdation exception received from input user custom envelopes event")
38+
raise
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import logging
2+
from typing import Any, Dict
3+
4+
from pydantic import BaseModel, ValidationError
5+
6+
from ..schemas import DynamoDBSchema
7+
from .base import BaseEnvelope
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
class DynamoDBEnvelope(BaseEnvelope):
13+
def parse(self, event: Dict[str, Any], inbound_schema_model: BaseModel) -> Any:
14+
try:
15+
parsed_envelope = DynamoDBSchema(**event)
16+
except (ValidationError, TypeError):
17+
logger.exception("Valdation exception received from input dynamodb stream event")
18+
raise
19+
output = []
20+
for record in parsed_envelope.Records:
21+
parsed_new_image = (
22+
{}
23+
if not record.dynamodb.NewImage
24+
else self._parse_user_dict_schema(record.dynamodb.NewImage, inbound_schema_model)
25+
) # noqa: E501
26+
parsed_old_image = (
27+
{}
28+
if not record.dynamodb.OldImage
29+
else self._parse_user_dict_schema(record.dynamodb.OldImage, inbound_schema_model)
30+
) # noqa: E501
31+
output.append({"new": parsed_new_image, "old": parsed_old_image})
32+
return output
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import logging
2+
from typing import Any, Dict
3+
4+
from pydantic import BaseModel, ValidationError
5+
6+
from ..schemas import EventBridgeSchema
7+
from .base import BaseEnvelope
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
class EventBridgeEnvelope(BaseEnvelope):
13+
def parse(self, event: Dict[str, Any], inbound_schema_model: BaseModel) -> Any:
14+
try:
15+
parsed_envelope = EventBridgeSchema(**event)
16+
except (ValidationError, TypeError):
17+
logger.exception("Valdation exception received from input eventbridge event")
18+
raise
19+
return self._parse_user_dict_schema(parsed_envelope.detail, inbound_schema_model)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from .dynamodb import DynamoDBSchema
2+
from .event_bridge import EventBridgeSchema
3+
from .sns import SnsSchema
4+
from .sqs import SqsSchema
5+
6+
__all__ = [
7+
"DynamoDBSchema",
8+
"EventBridgeSchema",
9+
"SnsSchema",
10+
"SqsSchema",
11+
]

aws_lambda_powertools/validation/schemas.py renamed to aws_lambda_powertools/utilities/validation/schemas/dynamodb.py

+1-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import date, datetime
1+
from datetime import date
22
from typing import Any, Dict, List, Optional
33

44
from pydantic import BaseModel, root_validator
@@ -35,22 +35,3 @@ class DynamoRecordSchema(BaseModel):
3535

3636
class DynamoDBSchema(BaseModel):
3737
Records: List[DynamoRecordSchema]
38-
39-
40-
class EventBridgeSchema(BaseModel):
41-
version: str
42-
id: str # noqa: A003,VNE003
43-
source: str
44-
account: int
45-
time: datetime
46-
region: str
47-
resources: List[str]
48-
detail: Dict[str, Any]
49-
50-
51-
class SqsSchema(BaseModel):
52-
todo: str
53-
54-
55-
class SnsSchema(BaseModel):
56-
todo: str
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from datetime import datetime
2+
from typing import Any, Dict, List
3+
4+
from pydantic import BaseModel
5+
6+
7+
class EventBridgeSchema(BaseModel):
8+
version: str
9+
id: str # noqa: A003,VNE003
10+
source: str
11+
account: int
12+
time: datetime
13+
region: str
14+
resources: List[str]
15+
detail: Dict[str, Any]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pydantic import BaseModel
2+
3+
4+
class SnsSchema(BaseModel):
5+
todo: str
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pydantic import BaseModel
2+
3+
4+
class SqsSchema(BaseModel):
5+
todo: str
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import logging
2+
from typing import Any, Callable, Dict
3+
4+
from pydantic import BaseModel, ValidationError
5+
6+
from aws_lambda_powertools.middleware_factory import lambda_handler_decorator
7+
8+
from .envelopes.base import BaseEnvelope
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
@lambda_handler_decorator
14+
def validator(
15+
handler: Callable[[Dict, Any], Any],
16+
event: Dict[str, Any],
17+
context: Dict[str, Any],
18+
inbound_schema_model: BaseModel,
19+
outbound_schema_model: BaseModel,
20+
envelope: BaseEnvelope,
21+
) -> Any:
22+
"""Decorator to create validation for lambda handlers events - both inbound and outbound
23+
24+
As Lambda follows (event, context) signature we can remove some of the boilerplate
25+
and also capture any exception any Lambda function throws or its response as metadata
26+
27+
Example
28+
-------
29+
**Lambda function using validation decorator**
30+
31+
@validator(inbound=inbound_schema_model, outbound=outbound_schema_model)
32+
def handler(parsed_event_model, context):
33+
...
34+
35+
Parameters
36+
----------
37+
todo add
38+
39+
Raises
40+
------
41+
err
42+
TypeError or pydantic.ValidationError or any exception raised by the lambda handler itself
43+
"""
44+
lambda_handler_name = handler.__name__
45+
logger.debug("Validating inbound schema")
46+
parsed_event_model = envelope.parse(event, inbound_schema_model)
47+
try:
48+
logger.debug(f"Calling handler {lambda_handler_name}")
49+
response = handler({"orig": event, "custom": parsed_event_model}, context)
50+
logger.debug("Received lambda handler response successfully")
51+
logger.debug(response)
52+
except Exception:
53+
logger.exception(f"Exception received from {lambda_handler_name}")
54+
raise
55+
56+
try:
57+
logger.debug("Validating outbound response schema")
58+
outbound_schema_model(**response)
59+
except (ValidationError, TypeError):
60+
logger.exception(f"Validation exception received from {lambda_handler_name} response event")
61+
raise
62+
return response

aws_lambda_powertools/validation/__init__.py

-2
This file was deleted.

aws_lambda_powertools/validation/validator.py

-126
This file was deleted.

tests/functional/test_validator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pydantic import BaseModel
88
from pydantic.error_wrappers import ValidationError
99

10-
from aws_lambda_powertools.validation.validator import DynamoDBEnvelope, EventBridgeEnvelope, UserEnvelope, validator
10+
from aws_lambda_powertools.utilities.validation import DynamoDBEnvelope, EventBridgeEnvelope, UserEnvelope, validator
1111

1212

1313
class OutboundSchema(BaseModel):

0 commit comments

Comments
 (0)