Skip to content

Commit

Permalink
fix: no signature model data validation if DTO
Browse files Browse the repository at this point in the history
By the time the signature model is parsed, the DTO has already validated and built the object to be injected as `data`, so we don't need the signature model to do it again.

This PR ensures that `data` fields are typed as `Any` on the signature model so that validation doesn't occur twice.

This is a regression introduced in #2088, so I've added an explicit test for it.

Closes #2149
  • Loading branch information
peterschutt committed Dec 8, 2023
1 parent 8f2cbe6 commit 27fa9f1
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
6 changes: 6 additions & 0 deletions litestar/_signature/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def create(
field_definition=field_definition,
type_decoders=[*(type_decoders or []), *DEFAULT_TYPE_DECODERS],
meta_data=meta_data,
data_dto=data_dto,
)

default = field_definition.default if field_definition.has_default else NODEFAULT
Expand All @@ -277,7 +278,12 @@ def _create_annotation(
field_definition: FieldDefinition,
type_decoders: TypeDecodersSequence,
meta_data: Meta | None = None,
data_dto: type[AbstractDTO] | None = None,
) -> Any:
# DTOs have already validated their data, so we can just use Any here
if field_definition.name == "data" and data_dto:
return Any

annotation = _normalize_annotation(field_definition=field_definition)

if annotation is Any:
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/test_signature/test_parsing.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from dataclasses import dataclass
from types import ModuleType
from typing import Any, Callable, Iterable, List, Optional, Sequence, Union
from unittest.mock import MagicMock

import msgspec
import pytest
from typing_extensions import Annotated

from litestar import get
from litestar._signature import SignatureModel
from litestar.dto import DataclassDTO
from litestar.params import Body, Parameter
from litestar.status_codes import HTTP_200_OK, HTTP_204_NO_CONTENT
from litestar.testing import TestClient, create_test_client
Expand Down Expand Up @@ -181,3 +184,30 @@ def handler(param: annotation) -> None: # pyright: ignore
response = client.get("/?param=foo&param=bar&param=123")
assert response.status_code == 500
assert "TypeError: Type unions may not contain more than one array-like" in response.text


def test_dto_data_typed_as_any() -> None:
"""DTOs already validate the payload, we don't need the signature model to do it too.
https://github.com/litestar-org/litestar/issues/2149
"""

@dataclass
class Test:
a: str

dto = DataclassDTO[Test]

def fn(data: Test) -> None:
pass

model = SignatureModel.create(
dependency_name_set=set(),
fn=fn,
data_dto=dto,
parsed_signature=ParsedSignature.from_fn(fn, signature_namespace={"Test": Test}),
type_decoders=[],
)
(field,) = msgspec.structs.fields(model)
assert field.name == "data"
assert field.type is Any

0 comments on commit 27fa9f1

Please sign in to comment.