-
Notifications
You must be signed in to change notification settings - Fork 421
/
Copy pathschema_validation.py
128 lines (101 loc) · 5.08 KB
/
schema_validation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
from aws_lambda_powertools.event_handler.exceptions import BadRequestError, InternalServerError
from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware
from aws_lambda_powertools.utilities.validation import validate
from aws_lambda_powertools.utilities.validation.exceptions import InvalidSchemaFormatError, SchemaValidationError
if TYPE_CHECKING:
from aws_lambda_powertools.event_handler.api_gateway import Response
from aws_lambda_powertools.event_handler.types import EventHandlerInstance
logger = logging.getLogger(__name__)
class SchemaValidationMiddleware(BaseMiddlewareHandler):
"""Middleware to validate API request and response against JSON Schema using the [Validation utility](https://docs.powertools.aws.dev/lambda/python/latest/utilities/validation/).
Example
--------
**Validating incoming event**
```python
import requests
from aws_lambda_powertools import Logger
from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response
from aws_lambda_powertools.event_handler.middlewares import BaseMiddlewareHandler, NextMiddleware
from aws_lambda_powertools.event_handler.middlewares.schema_validation import SchemaValidationMiddleware
app = APIGatewayRestResolver()
logger = Logger()
json_schema_validation = SchemaValidationMiddleware(inbound_schema=INCOMING_JSON_SCHEMA)
@app.get("/todos", middlewares=[json_schema_validation])
def get_todos():
todos: requests.Response = requests.get("https://jsonplaceholder.typicode.com/todos")
todos.raise_for_status()
# for brevity, we'll limit to the first 10 only
return {"todos": todos.json()[:10]}
@logger.inject_lambda_context
def lambda_handler(event, context):
return app.resolve(event, context)
```
"""
def __init__(
self,
inbound_schema: dict,
inbound_formats: dict | None = None,
outbound_schema: dict | None = None,
outbound_formats: dict | None = None,
):
"""See [Validation utility](https://docs.powertools.aws.dev/lambda/python/latest/utilities/validation/) docs for examples on all parameters.
Parameters
----------
inbound_schema : dict
JSON Schema to validate incoming event
inbound_formats : dict | None, optional
Custom formats containing a key (e.g. int64) and a value expressed as regex or callback returning bool, by default None
JSON Schema to validate outbound event, by default None
outbound_formats : dict | None, optional
Custom formats containing a key (e.g. int64) and a value expressed as regex or callback returning bool, by default None
""" # noqa: E501
super().__init__()
self.inbound_schema = inbound_schema
self.inbound_formats = inbound_formats
self.outbound_schema = outbound_schema
self.outbound_formats = outbound_formats
def bad_response(self, error: SchemaValidationError) -> Response:
message: str = f"Bad Response: {error.message}"
logger.debug(message)
raise BadRequestError(message)
def bad_request(self, error: SchemaValidationError) -> Response:
message: str = f"Bad Request: {error.message}"
logger.debug(message)
raise BadRequestError(message)
def bad_config(self, error: InvalidSchemaFormatError) -> Response:
logger.debug(f"Invalid Schema Format: {error}")
raise InternalServerError("Internal Server Error")
def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) -> Response:
"""Validates incoming JSON payload (body) against JSON Schema provided.
Parameters
----------
app : EventHandlerInstance
An instance of an Event Handler
next_middleware : NextMiddleware
Callable to get response from the next middleware or route handler in the chain
Returns
-------
Response
It can return three types of response objects
- Original response: Propagates HTTP response returned from the next middleware if validation succeeds
- HTTP 400: Payload or response failed JSON Schema validation
- HTTP 500: JSON Schema provided has incorrect format
"""
try:
validate(event=app.current_event.json_body, schema=self.inbound_schema, formats=self.inbound_formats)
except SchemaValidationError as error:
return self.bad_request(error)
except InvalidSchemaFormatError as error:
return self.bad_config(error)
result = next_middleware(app)
if self.outbound_formats is not None:
try:
validate(event=result.body, schema=self.inbound_schema, formats=self.inbound_formats)
except SchemaValidationError as error:
return self.bad_response(error)
except InvalidSchemaFormatError as error:
return self.bad_config(error)
return result