Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions templates/api/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from templates.api.models import Item
from templates.api.response import JsonResponse
from templates.models import Entity
from templates.api.settings import Settings
from templates.repository import Repository

Expand All @@ -27,8 +28,13 @@ def get_item(id: str) -> Response:
id: The unique identifier of the item.

Returns:
200 with the item, 404 if not found, or 500 on error.
200 with the item, 400 on invalid ID, 404 if not found, or 500 on error.
"""
try:
Entity(id=id)
except ValidationError:
return JsonResponse({"message": "Invalid item ID length"}, status_code=400)

try:
if (item := repository.get_item(id)) is None:
return JsonResponse({"message": f"Item '{id}' not found"}, status_code=404)
Expand All @@ -51,10 +57,10 @@ def create_item() -> Response:
try:
item = Item.model_validate_json(app.current_event.body)
except ValidationError as exc:
return JsonResponse({"errors": exc.errors()}, status_code=422)
return JsonResponse({"errors": exc.errors(include_input=False, include_url=False)}, status_code=422)

try:
repository.put_item(item.model_dump())
repository.put_item(item.dump())
except Exception as exc:
logger.error("DynamoDB put_item failed", exc_info=exc, extra={"itemId": item.id})
return JsonResponse({"message": "Internal server error"}, status_code=500)
Expand Down
17 changes: 17 additions & 0 deletions tests/api/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ def test_get_item_not_found(mock_repo, lambda_context):
assert body["message"] == "Item 'missing' not found"


def test_get_item_id_too_long(mock_repo, lambda_context):
"""GET /items/{id} returns 400 when the ID exceeds 50 characters."""
import templates.api.handler as handler_module

long_id = "a" * 51
event = _apigw_event("GET", f"/items/{long_id}", path_params={"id": long_id})
response = handler_module.main(event, lambda_context)

assert response["statusCode"] == 400
body = loads(response["body"])
assert body["message"] == "Invalid item ID length"


def test_post_item_invalid_body(mock_repo, lambda_context):
"""POST /items returns 422 when the request body fails Pydantic validation."""
import templates.api.handler as handler_module
Expand All @@ -123,6 +136,10 @@ def test_post_item_name_too_long(mock_repo, lambda_context):
body = loads(response["body"])
assert "errors" in body
assert any(err["type"] == "string_too_long" for err in body["errors"])
# Verify sanitization (no input, no url)
for error in body["errors"]:
assert "input" not in error
assert "url" not in error


def test_get_item_dynamodb_error(mock_repo, lambda_context):
Expand Down