wellapi 0.10.2__tar.gz → 0.10.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wellapi-0.10.2 → wellapi-0.10.4}/PKG-INFO +1 -1
- {wellapi-0.10.2 → wellapi-0.10.4}/pyproject.toml +1 -1
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/dependencies/utils.py +7 -5
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/routing.py +29 -25
- wellapi-0.10.4/tests/test_request_handler_body.py +39 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/uv.lock +225 -225
- {wellapi-0.10.2 → wellapi-0.10.4}/.github/workflows/build.pipeline.yml +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/.gitignore +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/.python-version +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/README.md +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/docs/framework-usage.md +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/docs/superpowers/plans/2026-05-29-otel-native-telemetry.md +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/docs/superpowers/specs/2026-05-29-otel-native-telemetry-design.md +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/docs/telemetry.md +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/__main__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/applications.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/awsmodel.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/build/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/build/cdk.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/build/packager.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/cli/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/cli/main.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/convertors.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/datastructures.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/dependencies/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/dependencies/models.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/exception_handlers.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/exceptions.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/local/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/local/router.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/local/server.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/middleware/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/middleware/base.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/middleware/cors.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/middleware/error.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/middleware/exceptions.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/middleware/main.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/models.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/openapi/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/openapi/docs.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/openapi/models.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/openapi/security_model.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/openapi/utils.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/params.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/security.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/telemetry/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/telemetry/attributes.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/telemetry/config.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/telemetry/flush.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/telemetry/middleware.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/testclient.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/src/wellapi/utils.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/conftest.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/telemetry/__init__.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/telemetry/test_applications.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/telemetry/test_attributes.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/telemetry/test_config.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/telemetry/test_flush.py +0 -0
- {wellapi-0.10.2 → wellapi-0.10.4}/tests/telemetry/test_middleware.py +0 -0
|
@@ -907,11 +907,13 @@ def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
|
|
|
907
907
|
def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]:
|
|
908
908
|
if not fields:
|
|
909
909
|
return fields
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
910
|
+
flat_fields: List[ModelField] = []
|
|
911
|
+
for field in fields:
|
|
912
|
+
if lenient_issubclass(field.type_, BaseModel):
|
|
913
|
+
flat_fields.extend(get_cached_model_fields(field.type_))
|
|
914
|
+
else:
|
|
915
|
+
flat_fields.append(field)
|
|
916
|
+
return flat_fields
|
|
915
917
|
|
|
916
918
|
|
|
917
919
|
def get_flat_params(dependant: Dependant) -> List[ModelField]:
|
|
@@ -9,7 +9,7 @@ from pydantic.main import IncEx
|
|
|
9
9
|
from wellapi.convertors import CONVERTOR_TYPES
|
|
10
10
|
from wellapi.datastructures import Default, DefaultPlaceholder
|
|
11
11
|
from wellapi.dependencies.models import Dependant, ModelField
|
|
12
|
-
from wellapi.dependencies.utils import solve_dependencies
|
|
12
|
+
from wellapi.dependencies.utils import get_flat_dependant, solve_dependencies
|
|
13
13
|
from wellapi.exceptions import (
|
|
14
14
|
HTTPException,
|
|
15
15
|
RequestValidationError,
|
|
@@ -100,33 +100,37 @@ def get_request_handler(
|
|
|
100
100
|
else:
|
|
101
101
|
actual_response_class = response_class
|
|
102
102
|
|
|
103
|
+
has_body_params = bool(get_flat_dependant(dependant).body_params)
|
|
104
|
+
|
|
103
105
|
@functools.wraps(dependant.call)
|
|
104
106
|
def app(request: RequestAPIGateway) -> ResponseAPIGateway:
|
|
105
107
|
response: ResponseAPIGateway | None = None
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
108
|
+
body: Any = None
|
|
109
|
+
if has_body_params:
|
|
110
|
+
try:
|
|
111
|
+
body = request.json()
|
|
112
|
+
except json.JSONDecodeError as e:
|
|
113
|
+
validation_error = RequestValidationError(
|
|
114
|
+
[
|
|
115
|
+
{
|
|
116
|
+
"type": "json_invalid",
|
|
117
|
+
"loc": ("body", e.pos),
|
|
118
|
+
"msg": "JSON decode error",
|
|
119
|
+
"input": {},
|
|
120
|
+
"ctx": {"error": e.msg},
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
body=e.doc,
|
|
124
|
+
)
|
|
125
|
+
raise validation_error from e
|
|
126
|
+
except HTTPException:
|
|
127
|
+
# If a middleware raises an HTTPException, it should be raised again
|
|
128
|
+
raise
|
|
129
|
+
except Exception as e:
|
|
130
|
+
http_error = HTTPException(
|
|
131
|
+
status_code=400, detail="There was an error parsing the body"
|
|
132
|
+
)
|
|
133
|
+
raise http_error from e
|
|
130
134
|
errors: list[Any] = []
|
|
131
135
|
solved_result = solve_dependencies(
|
|
132
136
|
request=request,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Non-JSON request bodies must reach handlers that declare no body params."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from wellapi import Body, WellApi
|
|
8
|
+
from wellapi.exceptions import RequestValidationError
|
|
9
|
+
from wellapi.models import RequestAPIGateway
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _make_request(api_gateway_event, body: str) -> RequestAPIGateway:
|
|
13
|
+
event = api_gateway_event | {"httpMethod": "POST", "body": body}
|
|
14
|
+
return RequestAPIGateway.create_request_from_event(event)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_non_json_body_reaches_handler_without_body_params(api_gateway_event):
|
|
18
|
+
app = WellApi()
|
|
19
|
+
|
|
20
|
+
@app.post("/relay")
|
|
21
|
+
def relay(request: RequestAPIGateway) -> dict:
|
|
22
|
+
return {"raw": request.raw_event.body}
|
|
23
|
+
|
|
24
|
+
lam = next(lambda_ for lambda_ in app.lambdas if lambda_.name == "relay")
|
|
25
|
+
response = lam.app(_make_request(api_gateway_event, "code=abc&state=x%2Fy"))
|
|
26
|
+
|
|
27
|
+
assert response.statusCode == 200
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_invalid_json_with_body_params_still_rejected(api_gateway_event):
|
|
31
|
+
app = WellApi()
|
|
32
|
+
|
|
33
|
+
@app.post("/strict")
|
|
34
|
+
def strict(name: Annotated[str, Body(embed=True)]) -> dict:
|
|
35
|
+
return {"name": name}
|
|
36
|
+
|
|
37
|
+
lam = next(lambda_ for lambda_ in app.lambdas if lambda_.name == "strict")
|
|
38
|
+
with pytest.raises(RequestValidationError):
|
|
39
|
+
lam.app(_make_request(api_gateway_event, "not json"))
|