diff --git a/templates/api/handler.py b/templates/api/handler.py index d6d3e95..fe2e9b7 100644 --- a/templates/api/handler.py +++ b/templates/api/handler.py @@ -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 @@ -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) @@ -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) diff --git a/tests/api/test_handler.py b/tests/api/test_handler.py index 76ea911..4bc8378 100644 --- a/tests/api/test_handler.py +++ b/tests/api/test_handler.py @@ -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 @@ -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):