Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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)
    }
Ejemplo n.º 6
0
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)
Ejemplo n.º 8
0
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
    )
Ejemplo n.º 9
0
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,
    )