Skip to content

Commit

Permalink
fix: Make sure that async fields always return Awaitables (#646)
Browse files Browse the repository at this point in the history
  • Loading branch information
bellini666 authored Oct 19, 2024
1 parent 92c1221 commit 31bb262
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 2 deletions.
5 changes: 3 additions & 2 deletions strawberry_django/fields/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from strawberry.annotation import StrawberryAnnotation
from strawberry.types.fields.resolver import StrawberryResolver
from strawberry.types.info import Info # noqa: TCH002
from strawberry.utils.await_maybe import await_maybe

from strawberry_django import optimizer
from strawberry_django.arguments import argument
Expand Down Expand Up @@ -215,10 +216,10 @@ def get_result(
if isinstance(attr, FileDescriptor) and not result:
result = None

if is_awaitable:
if is_awaitable or self.is_async:

async def async_resolver():
resolved = await result # type: ignore
resolved = await await_maybe(result)

if isinstance(resolved, BaseManager):
resolved = resolve_base_manager(resolved)
Expand Down
272 changes: 272 additions & 0 deletions tests/test_field_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
from collections.abc import Awaitable
from typing import Any, Union

import pytest
import strawberry
from strawberry import BasePermission, Info

import strawberry_django
from strawberry_django.optimizer import DjangoOptimizerExtension
from tests import models


@pytest.mark.django_db(transaction=True)
async def test_with_async_permission(db):
class AsyncPermission(BasePermission):
async def has_permission( # type: ignore
self,
source: Any,
info: Info,
**kwargs: Any,
) -> Union[bool, Awaitable[bool]]:
return True

@strawberry_django.type(models.Fruit)
class Fruit:
name: strawberry.auto

@strawberry_django.type(models.Color)
class Color:
name: strawberry.auto
fruits: list[Fruit] = strawberry_django.field(
permission_classes=[AsyncPermission]
)

@strawberry.type(name="Query")
class Query:
colors: list[Color] = strawberry_django.field()

red = await models.Color.objects.acreate(name="Red")
yellow = await models.Color.objects.acreate(name="Yellow")

await models.Fruit.objects.acreate(name="Apple", color=red)
await models.Fruit.objects.acreate(name="Banana", color=yellow)
await models.Fruit.objects.acreate(name="Strawberry", color=red)

schema = strawberry.Schema(query=Query)
query = """
query {
colors {
name
fruits {
name
}
}
}
"""

result = await schema.execute(query)
assert result.errors is None
assert result.data == {
"colors": [
{
"name": "Red",
"fruits": [
{"name": "Apple"},
{"name": "Strawberry"},
],
},
{
"name": "Yellow",
"fruits": [{"name": "Banana"}],
},
]
}


@pytest.mark.django_db(transaction=True)
async def test_with_async_permission_and_optimizer(db):
class AsyncPermission(BasePermission):
async def has_permission( # type: ignore
self,
source: Any,
info: Info,
**kwargs: Any,
) -> Union[bool, Awaitable[bool]]:
return True

@strawberry_django.type(models.Fruit)
class Fruit:
name: strawberry.auto

@strawberry_django.type(models.Color)
class Color:
name: strawberry.auto
fruits: list[Fruit] = strawberry_django.field(
permission_classes=[AsyncPermission]
)

@strawberry.type(name="Query")
class Query:
colors: list[Color] = strawberry_django.field()

red = await models.Color.objects.acreate(name="Red")
yellow = await models.Color.objects.acreate(name="Yellow")

await models.Fruit.objects.acreate(name="Apple", color=red)
await models.Fruit.objects.acreate(name="Banana", color=yellow)
await models.Fruit.objects.acreate(name="Strawberry", color=red)

schema = strawberry.Schema(
query=Query,
extensions=[DjangoOptimizerExtension()],
)
query = """
query {
colors {
name
fruits {
name
}
}
}
"""

result = await schema.execute(query)
assert result.errors is None
assert result.data == {
"colors": [
{
"name": "Red",
"fruits": [
{"name": "Apple"},
{"name": "Strawberry"},
],
},
{
"name": "Yellow",
"fruits": [{"name": "Banana"}],
},
]
}


@pytest.mark.django_db(transaction=True)
def test_with_sync_permission(db):
class AsyncPermission(BasePermission):
def has_permission(
self,
source: Any,
info: Info,
**kwargs: Any,
) -> Union[bool, Awaitable[bool]]:
return True

@strawberry_django.type(models.Fruit)
class Fruit:
name: strawberry.auto

@strawberry_django.type(models.Color)
class Color:
name: strawberry.auto
fruits: list[Fruit] = strawberry_django.field(
permission_classes=[AsyncPermission]
)

@strawberry.type(name="Query")
class Query:
colors: list[Color] = strawberry_django.field()

red = models.Color.objects.create(name="Red")
yellow = models.Color.objects.create(name="Yellow")

models.Fruit.objects.create(name="Apple", color=red)
models.Fruit.objects.create(name="Banana", color=yellow)
models.Fruit.objects.create(name="Strawberry", color=red)

schema = strawberry.Schema(query=Query)
query = """
query {
colors {
name
fruits {
name
}
}
}
"""

result = schema.execute_sync(query)
assert result.errors is None
assert result.data == {
"colors": [
{
"name": "Red",
"fruits": [
{"name": "Apple"},
{"name": "Strawberry"},
],
},
{
"name": "Yellow",
"fruits": [{"name": "Banana"}],
},
]
}


@pytest.mark.django_db(transaction=True)
def test_with_sync_permission_and_optimizer(db):
class AsyncPermission(BasePermission):
def has_permission(
self,
source: Any,
info: Info,
**kwargs: Any,
) -> Union[bool, Awaitable[bool]]:
return True

@strawberry_django.type(models.Fruit)
class Fruit:
name: strawberry.auto

@strawberry_django.type(models.Color)
class Color:
name: strawberry.auto
fruits: list[Fruit] = strawberry_django.field(
permission_classes=[AsyncPermission]
)

@strawberry.type(name="Query")
class Query:
colors: list[Color] = strawberry_django.field()

red = models.Color.objects.create(name="Red")
yellow = models.Color.objects.create(name="Yellow")

models.Fruit.objects.create(name="Apple", color=red)
models.Fruit.objects.create(name="Banana", color=yellow)
models.Fruit.objects.create(name="Strawberry", color=red)

schema = strawberry.Schema(
query=Query,
extensions=[DjangoOptimizerExtension()],
)
query = """
query {
colors {
name
fruits {
name
}
}
}
"""

result = schema.execute_sync(query)
assert result.errors is None
assert result.data == {
"colors": [
{
"name": "Red",
"fruits": [
{"name": "Apple"},
{"name": "Strawberry"},
],
},
{
"name": "Yellow",
"fruits": [{"name": "Banana"}],
},
]
}

0 comments on commit 31bb262

Please sign in to comment.