Repository: strawberry-graphql/strawberry

Issue:
Pydantic converted types have no way to deprecate fields
Since we recreate the field object for the values that use simple fields, there isn't a clean way to add a deprecation_reason to a field from a pydantic class. The same is true for permission_classes and directives.


Discussion:

Resolving patch:
diff --git a/strawberry/experimental/pydantic/object_type.py b/strawberry/experimental/pydantic/object_type.py
--- a/strawberry/experimental/pydantic/object_type.py
+++ b/strawberry/experimental/pydantic/object_type.py
@@ -110,6 +110,7 @@ def _build_dataclass_creation_fields(
         strawberry_field = existing_fields[field.name]
     else:
         # otherwise we build an appropriate strawberry field that resolves it
+        existing_field = existing_fields.get(field.name)
         strawberry_field = StrawberryField(
             python_name=field.name,
             graphql_name=field.alias
@@ -120,6 +121,13 @@ def _build_dataclass_creation_fields(
             default_factory=get_default_factory_for_field(field),
             type_annotation=type_annotation,
             description=field.field_info.description,
+            deprecation_reason=(
+                existing_field.deprecation_reason if existing_field else None
+            ),
+            permission_classes=(
+                existing_field.permission_classes if existing_field else []
+            ),
+            directives=existing_field.directives if existing_field else (),
         )
 
     return DataclassCreationFields(


Test patch:
diff --git a/tests/experimental/pydantic/test_basic.py b/tests/experimental/pydantic/test_basic.py
--- a/tests/experimental/pydantic/test_basic.py
+++ b/tests/experimental/pydantic/test_basic.py
@@ -1,6 +1,6 @@
 import dataclasses
 from enum import Enum
-from typing import List, Optional, Union
+from typing import Any, List, Optional, Union
 
 import pytest
 
@@ -9,6 +9,7 @@
 import strawberry
 from strawberry.enum import EnumDefinition
 from strawberry.experimental.pydantic.exceptions import MissingFieldsListError
+from strawberry.schema_directive import Location
 from strawberry.type import StrawberryList, StrawberryOptional
 from strawberry.types.types import TypeDefinition
 from strawberry.union import StrawberryUnion
@@ -701,3 +702,96 @@ class User:
     assert field2.python_name == "password"
     assert isinstance(field2.type, StrawberryOptional)
     assert field2.type.of_type is str
+
+
+def test_deprecated_fields():
+    class User(pydantic.BaseModel):
+        age: int
+        password: Optional[str]
+        other: float
+
+    @strawberry.experimental.pydantic.type(User)
+    class UserType:
+        age: strawberry.auto = strawberry.field(deprecation_reason="Because")
+        password: strawberry.auto
+
+    definition: TypeDefinition = UserType._type_definition
+    assert definition.name == "UserType"
+
+    [field1, field2] = definition.fields
+
+    assert field1.python_name == "age"
+    assert field1.graphql_name is None
+    assert field1.type is int
+    assert field1.deprecation_reason == "Because"
+
+    assert field2.python_name == "password"
+    assert field2.graphql_name is None
+    assert isinstance(field2.type, StrawberryOptional)
+    assert field2.type.of_type is str
+
+
+def test_permission_classes():
+    class IsAuthenticated(strawberry.BasePermission):
+        message = "User is not authenticated"
+
+        def has_permission(
+            self, source: Any, info: strawberry.types.Info, **kwargs
+        ) -> bool:
+            return False
+
+    class User(pydantic.BaseModel):
+        age: int
+        password: Optional[str]
+        other: float
+
+    @strawberry.experimental.pydantic.type(User)
+    class UserType:
+        age: strawberry.auto = strawberry.field(permission_classes=[IsAuthenticated])
+        password: strawberry.auto
+
+    definition: TypeDefinition = UserType._type_definition
+    assert definition.name == "UserType"
+
+    [field1, field2] = definition.fields
+
+    assert field1.python_name == "age"
+    assert field1.graphql_name is None
+    assert field1.type is int
+    assert field1.permission_classes == [IsAuthenticated]
+
+    assert field2.python_name == "password"
+    assert field2.graphql_name is None
+    assert isinstance(field2.type, StrawberryOptional)
+    assert field2.type.of_type is str
+
+
+def test_field_directives():
+    @strawberry.schema_directive(locations=[Location.FIELD_DEFINITION])
+    class Sensitive:
+        reason: str
+
+    class User(pydantic.BaseModel):
+        age: int
+        password: Optional[str]
+        other: float
+
+    @strawberry.experimental.pydantic.type(User)
+    class UserType:
+        age: strawberry.auto = strawberry.field(directives=[Sensitive(reason="GDPR")])
+        password: strawberry.auto
+
+    definition: TypeDefinition = UserType._type_definition
+    assert definition.name == "UserType"
+
+    [field1, field2] = definition.fields
+
+    assert field1.python_name == "age"
+    assert field1.graphql_name is None
+    assert field1.type is int
+    assert field1.directives == [Sensitive(reason="GDPR")]
+
+    assert field2.python_name == "password"
+    assert field2.graphql_name is None
+    assert isinstance(field2.type, StrawberryOptional)
+    assert field2.type.of_type is str