123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- from typing import Any, Callable, NamedTuple, Optional, Union
- from graphql_relay.utils.base64 import base64, unbase64
- from graphql import (
- GraphQLArgument,
- GraphQLNonNull,
- GraphQLID,
- GraphQLField,
- GraphQLInterfaceType,
- GraphQLList,
- GraphQLResolveInfo,
- GraphQLTypeResolver,
- )
- __all__ = [
- "from_global_id",
- "global_id_field",
- "node_definitions",
- "to_global_id",
- "GraphQLNodeDefinitions",
- "ResolvedGlobalId",
- ]
- class GraphQLNodeDefinitions(NamedTuple):
- node_interface: GraphQLInterfaceType
- node_field: GraphQLField
- nodes_field: GraphQLField
- def node_definitions(
- fetch_by_id: Callable[[str, GraphQLResolveInfo], Any],
- type_resolver: Optional[GraphQLTypeResolver] = None,
- ) -> GraphQLNodeDefinitions:
- """
- Given a function to map from an ID to an underlying object, and a function
- to map from an underlying object to the concrete GraphQLObjectType it
- corresponds to, constructs a `Node` interface that objects can implement,
- and a field object to be used as a `node` root field.
- If the type_resolver is omitted, object resolution on the interface will be
- handled with the `is_type_of` method on object types, as with any GraphQL
- interface without a provided `resolve_type` method.
- """
- node_interface = GraphQLInterfaceType(
- "Node",
- description="An object with an ID",
- fields=lambda: {
- "id": GraphQLField(
- GraphQLNonNull(GraphQLID), description="The id of the object."
- )
- },
- resolve_type=type_resolver,
- )
- # noinspection PyShadowingBuiltins
- node_field = GraphQLField(
- node_interface,
- description="Fetches an object given its ID",
- args={
- "id": GraphQLArgument(
- GraphQLNonNull(GraphQLID), description="The ID of an object"
- )
- },
- resolve=lambda _obj, info, id: fetch_by_id(id, info),
- )
- nodes_field = GraphQLField(
- GraphQLNonNull(GraphQLList(node_interface)),
- description="Fetches objects given their IDs",
- args={
- "ids": GraphQLArgument(
- GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLID))),
- description="The IDs of objects",
- )
- },
- resolve=lambda _obj, info, ids: [fetch_by_id(id_, info) for id_ in ids],
- )
- return GraphQLNodeDefinitions(node_interface, node_field, nodes_field)
- class ResolvedGlobalId(NamedTuple):
- type: str
- id: str
- def to_global_id(type_: str, id_: Union[str, int]) -> str:
- """
- Takes a type name and an ID specific to that type name, and returns a
- "global ID" that is unique among all types.
- """
- return base64(f"{type_}:{GraphQLID.serialize(id_)}")
- def from_global_id(global_id: str) -> ResolvedGlobalId:
- """
- Takes the "global ID" created by to_global_id, and returns the type name and ID
- used to create it.
- """
- global_id = unbase64(global_id)
- if ":" not in global_id:
- return ResolvedGlobalId("", global_id)
- return ResolvedGlobalId(*global_id.split(":", 1))
- def global_id_field(
- type_name: Optional[str] = None,
- id_fetcher: Optional[Callable[[Any, GraphQLResolveInfo], str]] = None,
- ) -> GraphQLField:
- """
- Creates the configuration for an id field on a node, using `to_global_id` to
- construct the ID from the provided typename. The type-specific ID is fetched
- by calling id_fetcher on the object, or if not provided, by accessing the `id`
- attribute of the object, or the `id` if the object is a dict.
- """
- def resolve(obj: Any, info: GraphQLResolveInfo, **_args: Any) -> str:
- type_ = type_name or info.parent_type.name
- id_ = (
- id_fetcher(obj, info)
- if id_fetcher
- else (obj["id"] if isinstance(obj, dict) else obj.id)
- )
- return to_global_id(type_, id_)
- return GraphQLField(
- GraphQLNonNull(GraphQLID), description="The ID of an object", resolve=resolve
- )
|