Skip to content

Commit fea7687

Browse files
fix(event_handler): add tests for PEP 563 compatibility with OpenAPI (#5886)
Adding tests with OpenAPI and PEP563
1 parent 465afe5 commit fea7687

File tree

4 files changed

+183
-1
lines changed

4 files changed

+183
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import annotations
2+
3+
from pydantic import BaseModel, Field
4+
5+
from aws_lambda_powertools.event_handler import (
6+
APIGatewayRestResolver,
7+
)
8+
9+
10+
class Todo(BaseModel):
11+
id: int = Field(examples=[1])
12+
title: str = Field(examples=["Example 1"])
13+
priority: float = Field(examples=[0.5])
14+
completed: bool = Field(examples=[True])
15+
16+
17+
app = APIGatewayRestResolver(enable_validation=True)
18+
19+
20+
@app.get("/openapi_schema_with_pep563")
21+
def openapi_schema():
22+
return app.get_openapi_json_schema(
23+
title="Powertools e2e API",
24+
version="1.0.0",
25+
description="This is a sample Powertools e2e API",
26+
openapi_extensions={"x-amazon-apigateway-gateway-responses": {"DEFAULT_4XX"}},
27+
)
28+
29+
30+
@app.get("/")
31+
def handler() -> Todo:
32+
return Todo(id=0, title="", priority=0.0, completed=False)
33+
34+
35+
def lambda_handler(event, context):
36+
return app.resolve(event, context)

tests/e2e/event_handler/infrastructure.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ def create_resources(self):
1818
functions = self.create_lambda_functions(function_props={"timeout": Duration.seconds(10)})
1919

2020
self._create_alb(function=[functions["AlbHandler"], functions["AlbHandlerWithBodyNone"]])
21-
self._create_api_gateway_rest(function=[functions["ApiGatewayRestHandler"], functions["OpenapiHandler"]])
21+
self._create_api_gateway_rest(
22+
function=[
23+
functions["ApiGatewayRestHandler"],
24+
functions["OpenapiHandler"],
25+
functions["OpenapiHandlerWithPep563"],
26+
],
27+
)
2228
self._create_api_gateway_http(function=functions["ApiGatewayHttpHandler"])
2329
self._create_lambda_function_url(function=functions["LambdaFunctionUrlHandler"])
2430

@@ -92,6 +98,9 @@ def _create_api_gateway_rest(self, function: List[Function]):
9298
openapi_schema = apigw.root.add_resource("openapi_schema")
9399
openapi_schema.add_method("GET", apigwv1.LambdaIntegration(function[1], proxy=True))
94100

101+
openapi_schema = apigw.root.add_resource("openapi_schema_with_pep563")
102+
openapi_schema.add_method("GET", apigwv1.LambdaIntegration(function[2], proxy=True))
103+
95104
CfnOutput(self.stack, "APIGatewayRestUrl", value=apigw.url)
96105

97106
def _create_lambda_function_url(self, function: Function):

tests/e2e/event_handler/test_openapi.py

+17
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,20 @@ def test_get_openapi_schema(apigw_rest_endpoint):
2525
assert "Powertools e2e API" in response.text
2626
assert "x-amazon-apigateway-gateway-responses" in response.text
2727
assert response.status_code == 200
28+
29+
30+
def test_get_openapi_schema_with_pep563(apigw_rest_endpoint):
31+
# GIVEN
32+
url = f"{apigw_rest_endpoint}openapi_schema_with_pep563"
33+
34+
# WHEN
35+
response = data_fetcher.get_http_response(
36+
Request(
37+
method="GET",
38+
url=url,
39+
),
40+
)
41+
42+
assert "Powertools e2e API" in response.text
43+
assert "x-amazon-apigateway-gateway-responses" in response.text
44+
assert response.status_code == 200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from __future__ import annotations
2+
3+
from pydantic import BaseModel, Field
4+
from typing_extensions import Annotated
5+
6+
from aws_lambda_powertools.event_handler.api_gateway import APIGatewayRestResolver
7+
from aws_lambda_powertools.event_handler.openapi.models import (
8+
ParameterInType,
9+
Schema,
10+
)
11+
from aws_lambda_powertools.event_handler.openapi.params import (
12+
Body,
13+
Query,
14+
)
15+
16+
JSON_CONTENT_TYPE = "application/json"
17+
18+
19+
class Todo(BaseModel):
20+
id: int = Field(examples=[1])
21+
title: str = Field(examples=["Example 1"])
22+
priority: float = Field(examples=[0.5])
23+
completed: bool = Field(examples=[True])
24+
25+
26+
def test_openapi_with_pep563_and_input_model():
27+
app = APIGatewayRestResolver()
28+
29+
@app.get("/users", summary="Get Users", operation_id="GetUsers", description="Get paginated users", tags=["Users"])
30+
def handler(
31+
count: Annotated[
32+
int,
33+
Query(gt=0, lt=100, examples=["Example 1"]),
34+
] = 1,
35+
):
36+
print(count)
37+
raise NotImplementedError()
38+
39+
schema = app.get_openapi_schema()
40+
41+
get = schema.paths["/users"].get
42+
assert len(get.parameters) == 1
43+
assert get.summary == "Get Users"
44+
assert get.operationId == "GetUsers"
45+
assert get.description == "Get paginated users"
46+
assert get.tags == ["Users"]
47+
48+
parameter = get.parameters[0]
49+
assert parameter.required is False
50+
assert parameter.name == "count"
51+
assert parameter.in_ == ParameterInType.query
52+
assert parameter.schema_.type == "integer"
53+
assert parameter.schema_.default == 1
54+
assert parameter.schema_.title == "Count"
55+
assert parameter.schema_.exclusiveMinimum == 0
56+
assert parameter.schema_.exclusiveMaximum == 100
57+
assert len(parameter.schema_.examples) == 1
58+
assert parameter.schema_.examples[0] == "Example 1"
59+
60+
61+
def test_openapi_with_pep563_and_output_model():
62+
63+
app = APIGatewayRestResolver()
64+
65+
@app.get("/")
66+
def handler() -> Todo:
67+
return Todo(id=0, title="", priority=0.0, completed=False)
68+
69+
schema = app.get_openapi_schema()
70+
assert "Todo" in schema.components.schemas
71+
todo_schema = schema.components.schemas["Todo"]
72+
assert isinstance(todo_schema, Schema)
73+
74+
assert "id" in todo_schema.properties
75+
id_property = todo_schema.properties["id"]
76+
assert id_property.examples == [1]
77+
78+
assert "title" in todo_schema.properties
79+
title_property = todo_schema.properties["title"]
80+
assert title_property.examples == ["Example 1"]
81+
82+
assert "priority" in todo_schema.properties
83+
priority_property = todo_schema.properties["priority"]
84+
assert priority_property.examples == [0.5]
85+
86+
assert "completed" in todo_schema.properties
87+
completed_property = todo_schema.properties["completed"]
88+
assert completed_property.examples == [True]
89+
90+
91+
def test_openapi_with_pep563_and_annotated_body():
92+
93+
app = APIGatewayRestResolver()
94+
95+
@app.post("/todo")
96+
def create_todo(
97+
todo_create_request: Annotated[Todo, Body(title="New Todo")],
98+
) -> dict:
99+
return {"message": f"Created todo {todo_create_request.title}"}
100+
101+
schema = app.get_openapi_schema()
102+
assert "Todo" in schema.components.schemas
103+
todo_schema = schema.components.schemas["Todo"]
104+
assert isinstance(todo_schema, Schema)
105+
106+
assert "id" in todo_schema.properties
107+
id_property = todo_schema.properties["id"]
108+
assert id_property.examples == [1]
109+
110+
assert "title" in todo_schema.properties
111+
title_property = todo_schema.properties["title"]
112+
assert title_property.examples == ["Example 1"]
113+
114+
assert "priority" in todo_schema.properties
115+
priority_property = todo_schema.properties["priority"]
116+
assert priority_property.examples == [0.5]
117+
118+
assert "completed" in todo_schema.properties
119+
completed_property = todo_schema.properties["completed"]
120+
assert completed_property.examples == [True]

0 commit comments

Comments
 (0)