File: serialize_variable_values.py

package info (click to toggle)
python-gql 4.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,900 kB
  • sloc: python: 21,677; makefile: 54
file content (130 lines) | stat: -rw-r--r-- 4,261 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from typing import Any, Dict, Optional

from graphql import (
    DocumentNode,
    GraphQLEnumType,
    GraphQLError,
    GraphQLInputObjectType,
    GraphQLList,
    GraphQLNonNull,
    GraphQLScalarType,
    GraphQLSchema,
    GraphQLType,
    GraphQLWrappingType,
    OperationDefinitionNode,
    type_from_ast,
)
from graphql.pyutils import inspect


def _get_document_operation(
    document: DocumentNode, operation_name: Optional[str] = None
) -> OperationDefinitionNode:
    """Returns the operation which should be executed in the document.

    Raises a GraphQLError if a single operation cannot be retrieved.
    """

    operation: Optional[OperationDefinitionNode] = None

    for definition in document.definitions:
        if isinstance(definition, OperationDefinitionNode):
            if operation_name is None:
                if operation:
                    raise GraphQLError(
                        "Must provide operation name"
                        " if query contains multiple operations."
                    )
                operation = definition
            elif definition.name and definition.name.value == operation_name:
                operation = definition

    if not operation:
        if operation_name is not None:
            raise GraphQLError(f"Unknown operation named '{operation_name}'.")

        # The following line should never happen normally as the document is
        # already verified before calling this function.
        raise GraphQLError("Must provide an operation.")  # pragma: no cover

    return operation


def serialize_value(type_: GraphQLType, value: Any) -> Any:
    """Given a GraphQL type and a Python value, return the serialized value.

    This method will serialize the value recursively, entering into
    lists and dicts.

    Can be used to serialize Enums and/or Custom Scalars in variable values.

    :param type_: the GraphQL type
    :param value: the provided value
    """

    if value is None:
        if isinstance(type_, GraphQLNonNull):
            # raise GraphQLError(f"Type {type_.of_type.name} Cannot be None.")
            raise GraphQLError(f"Type {inspect(type_)} Cannot be None.")
        else:
            return None

    if isinstance(type_, GraphQLWrappingType):
        inner_type = type_.of_type

        if isinstance(type_, GraphQLNonNull):
            return serialize_value(inner_type, value)

        elif isinstance(type_, GraphQLList):
            return [serialize_value(inner_type, v) for v in value]

    elif isinstance(type_, (GraphQLScalarType, GraphQLEnumType)):
        return type_.serialize(value)

    elif isinstance(type_, GraphQLInputObjectType):
        return {
            field_name: serialize_value(field.type, value[field_name])
            for field_name, field in type_.fields.items()
            if field_name in value
        }

    raise GraphQLError(f"Impossible to serialize value with type: {inspect(type_)}.")


def serialize_variable_values(
    schema: GraphQLSchema,
    document: DocumentNode,
    variable_values: Dict[str, Any],
    operation_name: Optional[str] = None,
) -> Dict[str, Any]:
    """Given a GraphQL document and a schema, serialize the Dictionary of
    variable values.

    Useful to serialize Enums and/or Custom Scalars in variable values.

    :param schema: the GraphQL schema
    :param document: the document representing the query sent to the backend
    :param variable_values: the dictionnary of variable values which needs
        to be serialized.
    :param operation_name: the optional operation_name for the query.
    """

    parsed_variable_values: Dict[str, Any] = {}

    # Find the operation in the document
    operation = _get_document_operation(document, operation_name=operation_name)

    # Serialize every variable value defined for the operation
    for var_def_node in operation.variable_definitions:
        var_name = var_def_node.variable.name.value
        var_type = type_from_ast(schema, var_def_node.type)

        if var_name in variable_values:

            assert var_type is not None

            var_value = variable_values[var_name]

            parsed_variable_values[var_name] = serialize_value(var_type, var_value)

    return parsed_variable_values