def get_internal_fragment( selection_set: SelectionSetNode, return_type: GraphQLCompositeType, context: 'QueryPlanningContext', ) -> 'InternalFragment': # TODO: is key correct ? key = str(hash(selection_set)) # const key = JSON.stringify(selectionSet); if key not in context.internal_fragments: name = f'__QueryPlanFragment_{context.internal_fragment_count}' context.internal_fragment_count += 1 definition = FragmentDefinitionNode( name=NameNode(value=name), type_condition=NamedTypeNode(name=NameNode(value=return_type.name)), selection_set=selection_set, ) fragment_selection = SelectionSetNode( selections=[FragmentSpreadNode(name=NameNode(value=name))] ) context.internal_fragments[key] = InternalFragment( name=name, definition=definition, selection_set=fragment_selection ) return context.internal_fragments[key]
def operation_for_entities_fetch( selection_set: SelectionSetNode, variable_usages: VariableUsages, internal_fragments: set[FragmentDefinitionNode], ) -> DocumentNode: representations_variable = VariableNode(name=NameNode(value='representations')) return DocumentNode( definitions=list( chain( [ OperationDefinitionNode( operation=OperationType.QUERY, variable_definitions=list( chain( [ VariableDefinitionNode( variable=representations_variable, type=NonNullTypeNode( type=ListTypeNode( type=NonNullTypeNode( type=NamedTypeNode(name=NameNode(value='_Any')) ) ) ), ) ], map_fetch_node_to_variable_definitions(variable_usages), ) ), selection_set=SelectionSetNode( selections=[ FieldNode( name=NameNode(value='_entities'), arguments=[ ArgumentNode( name=NameNode( value=representations_variable.name.value ), value=representations_variable, ) ], selection_set=selection_set, ) ] ), ), ], internal_fragments, ) ) )
def args(self, **kwargs) -> "DSLField": r"""Set the arguments of a field The arguments are parsed to be stored in the AST of this field. .. note:: You can also call the field directly with your arguments. :code:`ds.Query.human(id=1000)` is equivalent to: :code:`ds.Query.human.args(id=1000)` :param \**kwargs: the arguments (keyword=value) :return: itself :raises KeyError: if any of the provided arguments does not exist for this field. """ assert self.ast_field.arguments is not None self.ast_field.arguments = FrozenList( self.ast_field.arguments + [ ArgumentNode( name=NameNode(value=name), value=ast_from_value(value, self._get_argument(name).type), ) for name, value in kwargs.items() ] ) log.debug(f"Added arguments {kwargs} in field {self!r})") return self
def get_arg_serializer(arg_type, known_serializers): if isinstance(arg_type, GraphQLNonNull): return get_arg_serializer(arg_type.of_type, known_serializers) if isinstance(arg_type, GraphQLInputField): return get_arg_serializer(arg_type.type, known_serializers) if isinstance(arg_type, GraphQLInputObjectType): if arg_type in known_serializers: return known_serializers[arg_type] known_serializers[arg_type] = None serializers = { k: get_arg_serializer(v, known_serializers) for k, v in arg_type.fields.items() } known_serializers[arg_type] = lambda value: ObjectValueNode( fields=FrozenList( ObjectFieldNode(name=NameNode(value=k), value=serializers[k](v)) for k, v in value.items())) return known_serializers[arg_type] if isinstance(arg_type, GraphQLList): inner_serializer = get_arg_serializer(arg_type.of_type, known_serializers) return partial(serialize_list, inner_serializer) if isinstance(arg_type, GraphQLEnumType): return lambda value: EnumValueNode(value=arg_type.serialize(value)) return lambda value: ast_from_value(arg_type.serialize(value), arg_type)
def test_variable_to_ast_type_passing_wrapping_type(): wrapping_type = GraphQLNonNull(GraphQLList(StarWarsSchema.get_type("ReviewInput"))) variable = DSLVariable("review_input") ast = variable.to_ast_type(wrapping_type) assert ast == NonNullTypeNode( type=ListTypeNode(type=NamedTypeNode(name=NameNode(value="ReviewInput"))) )
def dsl_gql( *operations: "DSLOperation", **operations_with_name: "DSLOperation" ) -> DocumentNode: r"""Given arguments instances of :class:`DSLOperation` containing GraphQL operations, generate a Document which can be executed later in a gql client or a gql session. Similar to the :func:`gql.gql` function but instead of parsing a python string to describe the request, we are using operations which have been generated dynamically using instances of :class:`DSLField`, generated by instances of :class:`DSLType` which themselves originated from a :class:`DSLSchema` class. :param \*operations: the GraphQL operations :type \*operations: DSLOperation (DSLQuery, DSLMutation, DSLSubscription) :param \**operations_with_name: the GraphQL operations with an operation name :type \**operations_with_name: DSLOperation (DSLQuery, DSLMutation, DSLSubscription) :return: a Document which can be later executed or subscribed by a :class:`Client <gql.client.Client>`, by an :class:`async session <gql.client.AsyncClientSession>` or by a :class:`sync session <gql.client.SyncClientSession>` :raises TypeError: if an argument is not an instance of :class:`DSLOperation` """ # Concatenate operations without and with name all_operations: Tuple["DSLOperation", ...] = ( *operations, *(operation for operation in operations_with_name.values()), ) # Set the operation name for name, operation in operations_with_name.items(): operation.name = name # Check the type for operation in all_operations: if not isinstance(operation, DSLOperation): raise TypeError( "Operations should be instances of DSLOperation " "(DSLQuery, DSLMutation or DSLSubscription).\n" f"Received: {type(operation)}." ) return DocumentNode( definitions=[ OperationDefinitionNode( operation=OperationType(operation.operation_type), selection_set=operation.selection_set, variable_definitions=FrozenList( operation.variable_definitions.get_ast_definitions() ), **({"name": NameNode(value=operation.name)} if operation.name else {}), ) for operation in all_operations ] )
def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]: """ This is a partial copy paste of the ast_from_value function in graphql-core utilities/ast_from_value.py Overwrite the if blocks that use recursion and add a new case to return a VariableNode when value is a DSLVariable Produce a GraphQL Value AST given a Python object. """ if isinstance(value, DSLVariable): return value.set_type(type_).ast_variable if is_non_null_type(type_): type_ = cast(GraphQLNonNull, type_) ast_value = ast_from_value(value, type_.of_type) if isinstance(ast_value, NullValueNode): return None return ast_value # only explicit None, not Undefined or NaN if value is None: return NullValueNode() # undefined if value is Undefined: return None # Convert Python list to GraphQL list. If the GraphQLType is a list, but the value # is not a list, convert the value using the list's item type. if is_list_type(type_): type_ = cast(GraphQLList, type_) item_type = type_.of_type if isinstance(value, Iterable) and not isinstance(value, str): maybe_value_nodes = (ast_from_value(item, item_type) for item in value) value_nodes = filter(None, maybe_value_nodes) return ListValueNode(values=FrozenList(value_nodes)) return ast_from_value(value, item_type) # Populate the fields of the input object by creating ASTs from each value in the # Python dict according to the fields in the input type. if is_input_object_type(type_): if value is None or not isinstance(value, Mapping): return None type_ = cast(GraphQLInputObjectType, type_) field_items = ( (field_name, ast_from_value(value[field_name], field.type)) for field_name, field in type_.fields.items() if field_name in value ) field_nodes = ( ObjectFieldNode(name=NameNode(value=field_name), value=field_value) for field_name, field_value in field_items if field_value ) return ObjectValueNode(fields=FrozenList(field_nodes)) return default_ast_from_value(value, type_)
def to_ast_type( self, type_: Union[GraphQLWrappingType, GraphQLNamedType] ) -> TypeNode: if is_wrapping_type(type_): if isinstance(type_, GraphQLList): return ListTypeNode(type=self.to_ast_type(type_.of_type)) elif isinstance(type_, GraphQLNonNull): return NonNullTypeNode(type=self.to_ast_type(type_.of_type)) type_ = assert_named_type(type_) return NamedTypeNode(name=NameNode(value=type_.name))
def args(self, **kwargs): added_args = [] for name, value in kwargs.items(): arg = self.field.args.get(name) arg_type_serializer = get_arg_serializer(arg.type) serialized_value = arg_type_serializer(value) added_args.append( ArgumentNode(name=NameNode(value=name), value=serialized_value)) ast_field = self.ast_field ast_field.arguments = FrozenList(ast_field.arguments + added_args) return self
def args(self, **kwargs): added_args = [] for name, value in kwargs.items(): arg = self.field.args.get(name) if not arg: raise KeyError( f"Argument {name} does not exist in {self.field}.") arg_type_serializer = get_arg_serializer(arg.type) serialized_value = arg_type_serializer(value) added_args.append( ArgumentNode(name=NameNode(value=name), value=serialized_value)) ast_field = self.ast_field ast_field.arguments = FrozenList(ast_field.arguments + added_args) return self
def alias(self, alias: str) -> "DSLField": """Set an alias .. note:: You can also pass the alias directly at the :meth:`select <gql.dsl.DSLField.select>` method. :code:`ds.Query.human.select(my_name=ds.Character.name)` is equivalent to: :code:`ds.Query.human.select(ds.Character.name.alias("my_name"))` :param alias: the alias :type alias: str :return: itself """ self.ast_field.alias = NameNode(value=alias) return self
def test_literal_object_input(): assert GraphQLJSON.parse_literal( ObjectValueNode( fields=[ ObjectFieldNode(name=NameNode(value='true'), value=BooleanValueNode(value=False)), ObjectFieldNode( name=NameNode(value='hello'), value=VariableNode(name=NameNode(value='world')) ), ObjectFieldNode( name=NameNode(value='array'), value=ListValueNode( values=[ IntValueNode(value=1), FloatValueNode(value=2.0), StringValueNode(value='3'), ] ), ), ObjectFieldNode(name=NameNode(value='maybe_null'), value=NullValueNode()), ObjectFieldNode( name=NameNode(value='obj'), value=ObjectValueNode( fields=[ ObjectFieldNode( name=NameNode(value='test'), value=VariableNode(name=NameNode(value='tenet')), ) ] ), ), ] ), { 'world': 'world', 'tenet': 'tenet', }, ) == { 'true': False, 'hello': 'world', 'array': [1, 2.0, '3'], 'maybe_null': None, 'obj': {'test': 'tenet'}, }
def __init__( self, name: str, graphql_type: Union[GraphQLObjectType, GraphQLInterfaceType], graphql_field: GraphQLField, ): """Initialize the DSLField. .. warning:: Don't instantiate this class yourself. Use attributes of the :class:`DSLType` instead. :param name: the name of the field :param graphql_type: the GraphQL type definition from the schema :param graphql_field: the GraphQL field definition from the schema """ self._type: Union[GraphQLObjectType, GraphQLInterfaceType] = graphql_type self.field: GraphQLField = graphql_field self.ast_field: FieldNode = FieldNode( name=NameNode(value=name), arguments=FrozenList() ) log.debug(f"Creating {self!r}")
def __init__(self, name: str): self.type: Optional[TypeNode] = None self.name = name self.ast_variable = VariableNode(name=NameNode(value=self.name))
def __init__(self, name, field): self.field = field self.ast_field = FieldNode(name=NameNode(value=name), arguments=FrozenList()) self.selection_set = None
def alias(self, alias): self.ast_field.alias = NameNode(value=alias) return self
from graphql_query_planner.query_plan import ( FetchNode, FlattenNode, ParallelNode, PlanNode, QueryPlan, ResponsePath, SequenceNode, trim_selection_nodes, ) from graphql_query_planner.shims import GraphQLField from graphql_query_planner.utilities.graphql_ import get_field_def, get_response_name from graphql_query_planner.utilities.multi_map import MultiMap from graphql_query_planner.utilities.predicates import is_not_null_or_undefined typename_field = FieldNode(name=NameNode(value='__typename')) @dataclass class OperationContext: schema: GraphQLSchema operation: OperationDefinitionNode fragments: 'FragmentMap' FragmentName = str FragmentMap = dict[FragmentName, FragmentDefinitionNode] @dataclass