async def execute_fields_serially( execution_context: "ExecutionContext", parent_type: "GraphQLObjectType", source_value: Any, path: Optional["Path"], fields: Dict[str, List["FieldNode"]], ) -> Dict[str, Any]: """ Implements the "Evaluating selection sets" section of the spec for "write" mode. :param execution_context: instance of the query execution context :param parent_type: GraphQLObjectType of the field's parent :param source_value: default root value or field parent value :param path: the path traveled until this resolver :param fields: dictionary of collected fields :type execution_context: ExecutionContext :type parent_type: GraphQLObjectType :type source_value: Any :type path: Optional[Path] :type fields: Dict[str, List[FieldNode]] :return: the computed fields value :rtype: Dict[str, Any] """ results = {} for entry_key, field_nodes in fields.items(): result = await resolve_field( execution_context, parent_type, source_value, field_nodes, Path(path, entry_key), ) if not is_invalid_value(result): results[entry_key] = result return results
def _parse_field(field_ast: dict, validators: "Validators", path: "Path") -> "FieldNode": """ Creates and returns a FieldNode instance from a field's JSON AST libgraphqlparser representation. :param field_ast: field's JSON AST libgraphqlparser representation :type field_ast: dict :param validators: the Validators object that will be used to validate these arguments :param path: a Path object that contains the current field path :type validators: Validators :type path: Path :return: a FieldNode instance equivalent to the JSON AST representation :rtype: FieldNode """ name = _parse_name(field_ast["name"]) path = Path(prev=path, key=name.value) parent_type_name = validators.ctx["parent_type_name"] validators.ctx["parent_type_name"] = get_schema_field_type_name( parent_type_name, name.value, validators.schema) validators.ctx["in_directive"] = False validators.ctx["current_field_name"] = f"{parent_type_name}.{name}" field = FieldNode( alias=_parse_name(field_ast["alias"]) if field_ast["alias"] else None, name=name, arguments=_parse_arguments(field_ast["arguments"], validators, path), directives=_parse_directives(field_ast["directives"], validators, path), selection_set=_parse_selection_set(field_ast["selectionSet"], validators, path), location=_parse_location(field_ast["loc"]), ) validators.ctx["parent_type_name"] = parent_type_name validators.validate(rule="directives-are-in-valid-locations", node=field, path=path) validators.validate( rule="field-selections-on-objects-interfaces-and-unions-types", field=field, path=path, ) validators.validate(rule="leaf-field-selections", field=field, path=path) validators.validate(rule="values-of-correct-type", node=field, path=path) validators.validate(rule="argument-names", node=field, path=path) validators.validate(rule="required-arguments", node=field, path=path) return field
async def list_coercer( parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"], node: "Node", value: Any, ctx: Optional[Any], inner_coercer: Callable, path: Optional["Path"] = None, ) -> "CoercionResult": """ Computes the value of a list. :param parent_node: the root parent AST node :param node: the AST node to treat :param value: the raw value to compute :param ctx: context passed to the query execution :param inner_coercer: the pre-computed coercer to use on each value in the list :param path: the path traveled until this coercer :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode] :type node: Node :type value: Any :type ctx: Optional[Any] :type inner_coercer: Callable :type path: Optional[Path] :return: the coercion result :rtype: CoercionResult """ # pylint: disable=too-many-locals if isinstance(value, list): results = [ await inner_coercer(parent_node, node, item_value, ctx, path=Path(path, index)) for index, item_value in enumerate(value) ] errors = [] coerced_values = [] for coerced_value, coerced_errors in results: if coerced_errors: errors.extend(coerced_errors) elif not errors: coerced_values.append(coerced_value) return CoercionResult(value=coerced_values, errors=errors) coerced_item_value, coerced_item_errors = await inner_coercer(parent_node, node, value, ctx, path=path) return CoercionResult(value=[coerced_item_value], errors=coerced_item_errors)
async def list_coercer( result: Any, info: "ResolveInfo", execution_context: "ExecutionContext", field_nodes: List["FieldNode"], path: "Path", item_type: "GraphQLOutputType", inner_coercer: Callable, ) -> List[Any]: """ Computes the value of a list. :param result: resolved value :param info: information related to the execution and the resolved field :param execution_context: instance of the query execution context :param field_nodes: AST nodes related to the resolved field :param path: the path traveled until this resolver :param item_type: GraphQLType of list items :param inner_coercer: the pre-computed coercer to use on the result :type result: Any :type info: ResolveInfo :type execution_context: ExecutionContext :type field_nodes: List[FieldNode] :type path: Path :type item_type: GraphQLOutputType :type inner_coercer: Callable :return: the computed value :rtype: List[Any] """ # pylint: disable=too-many-locals if not isinstance(result, list): raise TypeError("Expected Iterable, but did not find one for field " f"{info.parent_type.name}.{info.field_name}.") results = [] for index, item in enumerate(result): try: value = await complete_value_catching_error( item, info, execution_context, field_nodes, Path(path, index), item_type, inner_coercer, ) except Exception as e: # pylint: disable=broad-except value = e results.append(value) exceptions = extract_exceptions_from_results(results) if exceptions: raise exceptions return results
async def execute_fields( execution_context: "ExecutionContext", parent_type: "GraphQLObjectType", source_value: Any, path: Optional["Path"], fields: Dict[str, List["FieldNode"]], is_introspection_context: bool = False, ) -> Dict[str, Any]: """ Implements the "Evaluating selection sets" section of the spec for "read" mode. :param execution_context: instance of the query execution context :param parent_type: GraphQLObjectType of the field's parent :param source_value: default root value or field parent value :param path: the path traveled until this resolver :param fields: dictionary of collected fields :param is_introspection_context: determines whether or not the resolved field is in a context of an introspection query :type execution_context: ExecutionContext :type parent_type: GraphQLObjectType :type source_value: Any :type path: Optional[Path] :type fields: Dict[str, List[FieldNode]] :type is_introspection_context: bool :return: the computed fields value :rtype: Dict[str, Any] """ results = await asyncio.gather( *[ resolve_field( execution_context, parent_type, source_value, field_nodes, Path(path, entry_key), is_introspection_context, ) for entry_key, field_nodes in fields.items() ], return_exceptions=True, ) exceptions = extract_exceptions_from_results(results) if exceptions: raise exceptions return { entry_key: result for entry_key, result in zip(fields, results) if not is_invalid_value(result) }
async def input_object_coercer( parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"], node: "Node", value: Any, ctx: Optional[Any], input_object_type: "GraphQLInputObjectType", path: Optional["Path"] = None, ) -> "CoercionResult": """ Computes the value of an input object. :param parent_node: the root parent AST node :param node: the AST node to treat :param value: the raw value to compute :param ctx: context passed to the query execution :param input_object_type: the GraphQLInputObjectType instance of the input object :param path: the path traveled until this coercer :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode] :type node: Node :type value: Any :type ctx: Optional[Any] :type input_object_type: GraphQLInputObjectType :type path: Optional[Path] :return: the coercion result :rtype: CoercionResult """ # pylint: disable=too-many-locals if not isinstance(value, dict): return CoercionResult(errors=[ coercion_error( f"Expected type < {input_object_type.name} > to be an object", node, path, ) ]) input_fields = input_object_type.input_fields results = await asyncio.gather(*[ input_field_value_coercer( parent_node, node, value.get(input_field_name, UNDEFINED_VALUE), ctx, input_field, path=Path(path, input_field_name), ) for input_field_name, input_field in input_fields.items() ]) errors = [] coerced_values = {} for input_field_name, input_field_result in zip(input_fields, results): if is_invalid_value(input_field_result): continue input_field_value, input_field_errors = input_field_result if input_field_errors: errors.extend(input_field_errors) elif not errors: coerced_values[input_field_name] = input_field_value for input_field_name in value: if input_field_name not in input_fields: errors.append( coercion_error( f"Field < {input_field_name} > is not defined by type " f"< {input_object_type.name} >", node, path, did_you_mean( get_close_matches(input_field_name, input_fields.keys(), n=5)), )) return CoercionResult(value=coerced_values, errors=errors)
async def input_object_coercer( parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"], node: Union["ValueNode", "VariableNode"], ctx: Optional[Any], input_object_type: "GraphQLInputObjectType", variables: Optional[Dict[str, Any]] = None, path: Optional["Path"] = None, ) -> "CoercionResult": """ Computes the value of an input object. :param parent_node: the root parent AST node :param node: the AST node to treat :param ctx: context passed to the query execution :param input_object_type: the GraphQLInputObjectType instance of the input object :param variables: the variables provided in the GraphQL request :param path: the path traveled until this coercer :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode] :type node: Union[ValueNode, VariableNode] :type ctx: Optional[Any] :type input_object_type: GraphQLInputObjectType :type variables: Optional[Dict[str, Any]] :type path: Optional[Path] :return: the computed value :rtype: CoercionResult """ # pylint: disable=too-many-locals if not isinstance(node, ObjectValueNode): return CoercionResult(value=UNDEFINED_VALUE) field_nodes = { field_node.name.value: field_node for field_node in node.fields } input_fields = input_object_type.input_fields results = await asyncio.gather( *[ input_field_value_coercer( input_field, parent_node, field_nodes.get(input_field_name, UNDEFINED_VALUE), ctx, variables, path=Path(path, input_field_name), ) for input_field_name, input_field in input_fields.items() ] ) errors = [] coerced_values = {} for input_field_name, input_field_result in zip(input_fields, results): if input_field_result is SKIP_FIELD: continue if is_invalid_value(input_field_result): return CoercionResult(value=UNDEFINED_VALUE) input_field_value, input_field_errors = input_field_result if is_invalid_value(input_field_value): return CoercionResult(value=UNDEFINED_VALUE) if input_field_errors: errors.extend(input_field_errors) elif not errors: coerced_values[input_field_name] = input_field_value return CoercionResult(value=coerced_values, errors=errors)
async def list_coercer( parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"], node: Union["ValueNode", "VariableNode"], ctx: Optional[Any], is_non_null_item_type: bool, inner_coercer: Callable, variables: Optional[Dict[str, Any]] = None, path: Optional["Path"] = None, ) -> "CoercionResult": """ Computes the value of a list. :param parent_node: the root parent AST node :param node: the AST node to treat :param ctx: context passed to the query execution :param is_non_null_item_type: determines whether or not the inner value is nullable :param inner_coercer: the pre-computed coercer to use on each value in the list :param variables: the variables provided in the GraphQL request :type parent_node: Union[VariableDefinitionNode, InputValueDefinitionNode] :type node: Union[ValueNode, VariableNode] :type ctx: Optional[Any] :type is_non_null_item_type: bool :type inner_coercer: Callable :type variables: Optional[Dict[str, Any]] :return: the computed value :rtype: CoercionResult """ # pylint: disable=too-many-locals if isinstance(node, ListValueNode): results = await asyncio.gather( *[ list_item_coercer( parent_node, item_node, ctx, is_non_null_item_type, inner_coercer, variables, path=Path(path, index), ) for index, item_node in enumerate(node.values) ] ) errors = [] coerced_values = [] for coerced_result in results: if is_invalid_value(coerced_result): return CoercionResult(value=UNDEFINED_VALUE) coerced_value, coerced_errors = coerced_result if is_invalid_value(coerced_value): return CoercionResult(value=UNDEFINED_VALUE) if coerced_errors: errors.extend(coerced_errors) elif not errors: coerced_values.append(coerced_value) return CoercionResult(value=coerced_values, errors=errors) coerced_item_value, coerced_item_errors = await inner_coercer( parent_node, node, ctx, variables=variables, path=path ) if is_invalid_value(coerced_item_value): return CoercionResult(value=UNDEFINED_VALUE) return CoercionResult( value=[coerced_item_value], errors=coerced_item_errors )
async def create_source_event_stream( schema: "GraphQLSchema", document: "DocumentNode", response_builder: Callable, root_value: Optional[Any], context: Optional[Any], variables: Optional[Dict[str, Any]], operation_name: Optional[str], ) -> Union[AsyncIterable[Dict[str, Any]], Dict[str, Any]]: """ Resolves the subscription source event stream. :param schema: the GraphQLSchema instance linked to the engine :param document: the DocumentNode instance linked to the GraphQL request :param response_builder: callable in charge of returning the formatted GraphQL response :param root_value: an initial value corresponding to the root type being executed :param context: value that can contain everything you need and that will be accessible from the resolvers :param variables: the variables provided in the GraphQL request :param operation_name: the operation name to execute :type schema: GraphQLSchema :type document: DocumentNode :type response_builder: Callable :type root_value: Optional[Any] :type context: Optional[Any] :type variables: Optional[Dict[str, Any]] :type operation_name: str :return: an error or an async iterable :rtype: Union[AsyncIterable[Dict[str, Any]], Dict[str, Any]] """ # pylint: disable=too-many-locals execution_context, errors = await build_execution_context( schema, document, root_value, context, variables, operation_name) if errors: return await response_builder(errors=errors) operation_root_type = schema.get_operation_root_type( execution_context.operation) fields = await collect_fields( execution_context, operation_root_type, execution_context.operation.selection_set, ) response_name = list(fields.keys())[0] field_nodes = fields[response_name] field_name = field_nodes[0].name.value field_definition = get_field_definition(schema, operation_root_type, field_name) if not field_definition: raise Exception( f"The subscription field < {field_name} > is not defined.") if not field_definition.subscribe: raise Exception( "Can't execute a subscription query on a field which doesn't " "provide a source event stream with < @Subscription >.") info = build_resolve_info( execution_context, field_definition, field_nodes, operation_root_type, Path(None, response_name), ) return field_definition.subscribe( root_value, await coerce_arguments( field_definition.arguments, field_nodes[0], execution_context.variable_values, execution_context.context, ), execution_context.context, info, )