Skip to content

Commit

Permalink
fix: Fix specialized connection aliases missing filters/ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
bellini666 committed Jun 7, 2024
1 parent 9454e99 commit 9233a5c
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 8 deletions.
9 changes: 5 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ django = ">=3.2"
asgiref = ">=3.8"
django-choices-field = { version = ">=2.2.2", optional = true }
django-debug-toolbar = { version = ">=3.4", optional = true }
strawberry-graphql = ">=0.227.1"
strawberry-graphql = ">=0.234.2"

[tool.poetry.group.dev.dependencies]
Markdown = "^3.3.7"
Expand Down
15 changes: 12 additions & 3 deletions strawberry_django/fields/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import functools
from typing import TYPE_CHECKING, Any, Optional, TypeVar
from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast

from django.db.models import ForeignKey
from strawberry import LazyType, relay
Expand All @@ -17,6 +17,7 @@
get_object_definition,
)
from strawberry.union import StrawberryUnion
from strawberry.utils.inspect import get_specialized_type_var_map

from strawberry_django.resolvers import django_resolver
from strawberry_django.utils.typing import (
Expand Down Expand Up @@ -80,16 +81,24 @@ def is_async(self) -> bool:
def django_type(self) -> type[WithStrawberryDjangoObjectDefinition] | None:
origin = self.type

if isinstance(origin, LazyType):
origin = origin.resolve_type()

object_definition = get_object_definition(origin)

if object_definition and issubclass(object_definition.origin, relay.Connection):
origin = object_definition.type_var_map.get("NodeType")
origin_specialized_type_var_map = (
get_specialized_type_var_map(cast(type, origin)) or {}
)
origin = origin_specialized_type_var_map.get("NodeType")

if origin is None:
origin = object_definition.type_var_map.get("NodeType")

if origin is None:
specialized_type_var_map = (
object_definition.specialized_type_var_map or {}
)

origin = specialized_type_var_map["NodeType"]

if isinstance(origin, LazyType):
Expand Down
Empty file added tests/relay/lazy/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions tests/relay/lazy/a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import TYPE_CHECKING

import strawberry
from strawberry import relay
from typing_extensions import Annotated, TypeAlias

import strawberry_django
from strawberry_django.relay import ListConnectionWithTotalCount

from .models import RelayAuthor

if TYPE_CHECKING:
from .b import BookConnection


@strawberry_django.type(RelayAuthor)
class AuthorType(relay.Node):
name: str
books: Annotated["BookConnection", strawberry.lazy("tests.relay.lazy.b")] = (
strawberry_django.connection()
)


AuthorConnection: TypeAlias = ListConnectionWithTotalCount[AuthorType]
32 changes: 32 additions & 0 deletions tests/relay/lazy/b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import TYPE_CHECKING

import strawberry
from strawberry import relay
from typing_extensions import Annotated, TypeAlias

import strawberry_django
from strawberry_django.relay import ListConnectionWithTotalCount

from .models import RelayBook

if TYPE_CHECKING:
from .a import AuthorType


@strawberry_django.filter(RelayBook)
class BookFilter:
name: str


@strawberry_django.order(RelayBook)
class BookOrder:
name: str


@strawberry_django.type(RelayBook, filters=BookFilter, order=BookOrder)
class BookType(relay.Node):
name: str
author: Annotated["AuthorType", strawberry.lazy("tests.relay.lazy.a")]


BookConnection: TypeAlias = ListConnectionWithTotalCount[BookType]
14 changes: 14 additions & 0 deletions tests/relay/lazy/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django.db import models


class RelayAuthor(models.Model):
name = models.CharField(max_length=100)


class RelayBook(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(
RelayAuthor,
on_delete=models.CASCADE,
related_name="books",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
type AuthorType implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
name: String!
books(
filters: BookFilter
order: BookOrder

"""Returns the items in the list that come before the specified cursor."""
before: String = null

"""Returns the items in the list that come after the specified cursor."""
after: String = null

"""Returns the first n items from the list."""
first: Int = null

"""Returns the items in the list that come after the specified cursor."""
last: Int = null
): BookTypeConnection!
}

"""A connection to a list of items."""
type AuthorTypeConnection {
"""Pagination data for this connection"""
pageInfo: PageInfo!

"""Contains the nodes in this connection"""
edges: [AuthorTypeEdge!]!

"""Total quantity of existing nodes."""
totalCount: Int
}

"""An edge in a connection."""
type AuthorTypeEdge {
"""A cursor for use in pagination"""
cursor: String!

"""The item at the end of the edge"""
node: AuthorType!
}

input BookFilter {
name: String!
AND: BookFilter
OR: BookFilter
NOT: BookFilter
DISTINCT: Boolean
}

input BookOrder {
name: String
}

type BookType implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
name: String!
author: AuthorType!
}

"""A connection to a list of items."""
type BookTypeConnection {
"""Pagination data for this connection"""
pageInfo: PageInfo!

"""Contains the nodes in this connection"""
edges: [BookTypeEdge!]!

"""Total quantity of existing nodes."""
totalCount: Int
}

"""An edge in a connection."""
type BookTypeEdge {
"""A cursor for use in pagination"""
cursor: String!

"""The item at the end of the edge"""
node: BookType!
}

"""
The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID.
"""
scalar GlobalID @specifiedBy(url: "https://relay.dev/graphql/objectidentification.htm")

"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
}

"""Information to aid in pagination."""
type PageInfo {
"""When paginating forwards, are there more items?"""
hasNextPage: Boolean!

"""When paginating backwards, are there more items?"""
hasPreviousPage: Boolean!

"""When paginating backwards, the cursor to continue."""
startCursor: String

"""When paginating forwards, the cursor to continue."""
endCursor: String
}

type Query {
booksConn(
filters: BookFilter
order: BookOrder

"""Returns the items in the list that come before the specified cursor."""
before: String = null

"""Returns the items in the list that come after the specified cursor."""
after: String = null

"""Returns the first n items from the list."""
first: Int = null

"""Returns the items in the list that come after the specified cursor."""
last: Int = null
): BookTypeConnection!
booksConn2(
filters: BookFilter
order: BookOrder

"""Returns the items in the list that come before the specified cursor."""
before: String = null

"""Returns the items in the list that come after the specified cursor."""
after: String = null

"""Returns the first n items from the list."""
first: Int = null

"""Returns the items in the list that come after the specified cursor."""
last: Int = null
): BookTypeConnection!
authorsConn(
"""Returns the items in the list that come before the specified cursor."""
before: String = null

"""Returns the items in the list that come after the specified cursor."""
after: String = null

"""Returns the first n items from the list."""
first: Int = null

"""Returns the items in the list that come after the specified cursor."""
last: Int = null
): AuthorTypeConnection!
authorsConn2(
"""Returns the items in the list that come before the specified cursor."""
before: String = null

"""Returns the items in the list that come after the specified cursor."""
after: String = null

"""Returns the first n items from the list."""
first: Int = null

"""Returns the items in the list that come after the specified cursor."""
last: Int = null
): AuthorTypeConnection!
}
28 changes: 28 additions & 0 deletions tests/relay/lazy/test_lazy_annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pathlib

import strawberry
from pytest_snapshot.plugin import Snapshot

import strawberry_django
from strawberry_django.relay import ListConnectionWithTotalCount

from .a import AuthorConnection, AuthorType
from .b import BookConnection, BookType

SNAPSHOTS_DIR = pathlib.Path(__file__).parent / "snapshots"


def test_schema(snapshot: Snapshot):
@strawberry.type
class Query:
books_conn: BookConnection = strawberry_django.connection()
books_conn2: ListConnectionWithTotalCount[BookType] = (
strawberry_django.connection()
)
authors_conn: AuthorConnection = strawberry_django.connection()
authors_conn2: ListConnectionWithTotalCount[AuthorType] = (
strawberry_django.connection()
)

schema = strawberry.Schema(query=Query)
snapshot.assert_match(str(schema), "authors_and_books_schema.gql")

0 comments on commit 9233a5c

Please sign in to comment.