258 lines
6.7 KiB
Python
258 lines
6.7 KiB
Python
from typing import Any, Dict, List, NamedTuple, Optional, Union
|
|
|
|
from graphql import (
|
|
get_named_type,
|
|
resolve_thunk,
|
|
GraphQLArgument,
|
|
GraphQLArgumentMap,
|
|
GraphQLBoolean,
|
|
GraphQLField,
|
|
GraphQLFieldResolver,
|
|
GraphQLInt,
|
|
GraphQLList,
|
|
GraphQLNonNull,
|
|
GraphQLObjectType,
|
|
GraphQLString,
|
|
ThunkMapping,
|
|
)
|
|
|
|
from graphql import GraphQLNamedOutputType
|
|
|
|
try:
|
|
from typing import Protocol
|
|
except ImportError: # Python < 3.8
|
|
from typing_extensions import Protocol # type: ignore
|
|
|
|
__all__ = [
|
|
"backward_connection_args",
|
|
"connection_args",
|
|
"connection_definitions",
|
|
"forward_connection_args",
|
|
"page_info_type",
|
|
"Connection",
|
|
"ConnectionArguments",
|
|
"ConnectionConstructor",
|
|
"ConnectionCursor",
|
|
"ConnectionType",
|
|
"Edge",
|
|
"EdgeConstructor",
|
|
"EdgeType",
|
|
"GraphQLConnectionDefinitions",
|
|
"PageInfo",
|
|
"PageInfoConstructor",
|
|
"PageInfoType",
|
|
]
|
|
|
|
|
|
# Returns a GraphQLArgumentMap appropriate to include on a field
|
|
# whose return type is a connection type with forward pagination.
|
|
forward_connection_args: GraphQLArgumentMap = {
|
|
"after": GraphQLArgument(
|
|
GraphQLString,
|
|
description="Returns the items in the list"
|
|
" that come after the specified cursor.",
|
|
),
|
|
"first": GraphQLArgument(
|
|
GraphQLInt,
|
|
description="Returns the first n items from the list.",
|
|
),
|
|
}
|
|
|
|
# Returns a GraphQLArgumentMap appropriate to include on a field
|
|
# whose return type is a connection type with backward pagination.
|
|
backward_connection_args: GraphQLArgumentMap = {
|
|
"before": GraphQLArgument(
|
|
GraphQLString,
|
|
description="Returns the items in the list"
|
|
" that come before the specified cursor.",
|
|
),
|
|
"last": GraphQLArgument(
|
|
GraphQLInt, description="Returns the last n items from the list."
|
|
),
|
|
}
|
|
|
|
# Returns a GraphQLArgumentMap appropriate to include on a field
|
|
# whose return type is a connection type with bidirectional pagination.
|
|
connection_args = {**forward_connection_args, **backward_connection_args}
|
|
|
|
|
|
class GraphQLConnectionDefinitions(NamedTuple):
|
|
edge_type: GraphQLObjectType
|
|
connection_type: GraphQLObjectType
|
|
|
|
|
|
"""A type alias for cursors in this implementation."""
|
|
ConnectionCursor = str
|
|
|
|
|
|
"""A type describing the arguments a connection field receives in GraphQL.
|
|
|
|
The following kinds of arguments are expected (all optional):
|
|
|
|
before: ConnectionCursor
|
|
after: ConnectionCursor
|
|
first: int
|
|
last: int
|
|
"""
|
|
ConnectionArguments = Dict[str, Any]
|
|
|
|
|
|
def connection_definitions(
|
|
node_type: Union[GraphQLNamedOutputType, GraphQLNonNull[GraphQLNamedOutputType]],
|
|
name: Optional[str] = None,
|
|
resolve_node: Optional[GraphQLFieldResolver] = None,
|
|
resolve_cursor: Optional[GraphQLFieldResolver] = None,
|
|
edge_fields: Optional[ThunkMapping[GraphQLField]] = None,
|
|
connection_fields: Optional[ThunkMapping[GraphQLField]] = None,
|
|
) -> GraphQLConnectionDefinitions:
|
|
"""Return GraphQLObjectTypes for a connection with the given name.
|
|
|
|
The nodes of the returned object types will be of the specified type.
|
|
"""
|
|
name = name or get_named_type(node_type).name
|
|
|
|
edge_type = GraphQLObjectType(
|
|
name + "Edge",
|
|
description="An edge in a connection.",
|
|
fields=lambda: {
|
|
"node": GraphQLField(
|
|
node_type,
|
|
resolve=resolve_node,
|
|
description="The item at the end of the edge",
|
|
),
|
|
"cursor": GraphQLField(
|
|
GraphQLNonNull(GraphQLString),
|
|
resolve=resolve_cursor,
|
|
description="A cursor for use in pagination",
|
|
),
|
|
**resolve_thunk(edge_fields or {}),
|
|
},
|
|
)
|
|
|
|
connection_type = GraphQLObjectType(
|
|
name + "Connection",
|
|
description="A connection to a list of items.",
|
|
fields=lambda: {
|
|
"pageInfo": GraphQLField(
|
|
GraphQLNonNull(page_info_type),
|
|
description="Information to aid in pagination.",
|
|
),
|
|
"edges": GraphQLField(
|
|
GraphQLList(edge_type), description="A list of edges."
|
|
),
|
|
**resolve_thunk(connection_fields or {}),
|
|
},
|
|
)
|
|
|
|
return GraphQLConnectionDefinitions(edge_type, connection_type)
|
|
|
|
|
|
class PageInfoType(Protocol):
|
|
@property
|
|
def startCursor(self) -> Optional[ConnectionCursor]:
|
|
...
|
|
|
|
def endCursor(self) -> Optional[ConnectionCursor]:
|
|
...
|
|
|
|
def hasPreviousPage(self) -> bool:
|
|
...
|
|
|
|
def hasNextPage(self) -> bool:
|
|
...
|
|
|
|
|
|
class PageInfoConstructor(Protocol):
|
|
def __call__(
|
|
self,
|
|
*,
|
|
startCursor: Optional[ConnectionCursor],
|
|
endCursor: Optional[ConnectionCursor],
|
|
hasPreviousPage: bool,
|
|
hasNextPage: bool,
|
|
) -> PageInfoType:
|
|
...
|
|
|
|
|
|
class PageInfo(NamedTuple):
|
|
"""A type designed to be exposed as `PageInfo` over GraphQL."""
|
|
|
|
startCursor: Optional[ConnectionCursor]
|
|
endCursor: Optional[ConnectionCursor]
|
|
hasPreviousPage: bool
|
|
hasNextPage: bool
|
|
|
|
|
|
class EdgeType(Protocol):
|
|
@property
|
|
def node(self) -> Any:
|
|
...
|
|
|
|
@property
|
|
def cursor(self) -> ConnectionCursor:
|
|
...
|
|
|
|
|
|
class EdgeConstructor(Protocol):
|
|
def __call__(self, *, node: Any, cursor: ConnectionCursor) -> EdgeType:
|
|
...
|
|
|
|
|
|
class Edge(NamedTuple):
|
|
"""A type designed to be exposed as a `Edge` over GraphQL."""
|
|
|
|
node: Any
|
|
cursor: ConnectionCursor
|
|
|
|
|
|
class ConnectionType(Protocol):
|
|
@property
|
|
def edges(self) -> List[EdgeType]:
|
|
...
|
|
|
|
@property
|
|
def pageInfo(self) -> PageInfoType:
|
|
...
|
|
|
|
|
|
class ConnectionConstructor(Protocol):
|
|
def __call__(
|
|
self,
|
|
*,
|
|
edges: List[EdgeType],
|
|
pageInfo: PageInfoType,
|
|
) -> ConnectionType:
|
|
...
|
|
|
|
|
|
class Connection(NamedTuple):
|
|
"""A type designed to be exposed as a `Connection` over GraphQL."""
|
|
|
|
edges: List[Edge]
|
|
pageInfo: PageInfo
|
|
|
|
|
|
# The common page info type used by all connections.
|
|
page_info_type = GraphQLObjectType(
|
|
"PageInfo",
|
|
description="Information about pagination in a connection.",
|
|
fields=lambda: {
|
|
"hasNextPage": GraphQLField(
|
|
GraphQLNonNull(GraphQLBoolean),
|
|
description="When paginating forwards, are there more items?",
|
|
),
|
|
"hasPreviousPage": GraphQLField(
|
|
GraphQLNonNull(GraphQLBoolean),
|
|
description="When paginating backwards, are there more items?",
|
|
),
|
|
"startCursor": GraphQLField(
|
|
GraphQLString,
|
|
description="When paginating backwards, the cursor to continue.",
|
|
),
|
|
"endCursor": GraphQLField(
|
|
GraphQLString,
|
|
description="When paginating forwards, the cursor to continue.",
|
|
),
|
|
},
|
|
)
|