values.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. from typing import Any, Callable, Collection, Dict, List, Optional, Union, cast
  2. from ..error import GraphQLError
  3. from ..language import (
  4. DirectiveNode,
  5. EnumValueDefinitionNode,
  6. ExecutableDefinitionNode,
  7. FieldNode,
  8. FieldDefinitionNode,
  9. InputValueDefinitionNode,
  10. NullValueNode,
  11. SchemaDefinitionNode,
  12. SelectionNode,
  13. TypeDefinitionNode,
  14. TypeExtensionNode,
  15. VariableDefinitionNode,
  16. VariableNode,
  17. print_ast,
  18. )
  19. from ..pyutils import inspect, print_path_list, Undefined
  20. from ..type import (
  21. GraphQLDirective,
  22. GraphQLField,
  23. GraphQLInputType,
  24. GraphQLSchema,
  25. is_input_type,
  26. is_non_null_type,
  27. )
  28. from ..utilities.coerce_input_value import coerce_input_value
  29. from ..utilities.type_from_ast import type_from_ast
  30. from ..utilities.value_from_ast import value_from_ast
  31. __all__ = ["get_argument_values", "get_directive_values", "get_variable_values"]
  32. CoercedVariableValues = Union[List[GraphQLError], Dict[str, Any]]
  33. def get_variable_values(
  34. schema: GraphQLSchema,
  35. var_def_nodes: Collection[VariableDefinitionNode],
  36. inputs: Dict[str, Any],
  37. max_errors: Optional[int] = None,
  38. ) -> CoercedVariableValues:
  39. """Get coerced variable values based on provided definitions.
  40. Prepares a dict of variable values of the correct type based on the provided
  41. variable definitions and arbitrary input. If the input cannot be parsed to match
  42. the variable definitions, a GraphQLError will be raised.
  43. """
  44. errors: List[GraphQLError] = []
  45. def on_error(error: GraphQLError) -> None:
  46. if max_errors is not None and len(errors) >= max_errors:
  47. raise GraphQLError(
  48. "Too many errors processing variables,"
  49. " error limit reached. Execution aborted."
  50. )
  51. errors.append(error)
  52. try:
  53. coerced = coerce_variable_values(schema, var_def_nodes, inputs, on_error)
  54. if not errors:
  55. return coerced
  56. except GraphQLError as e:
  57. errors.append(e)
  58. return errors
  59. def coerce_variable_values(
  60. schema: GraphQLSchema,
  61. var_def_nodes: Collection[VariableDefinitionNode],
  62. inputs: Dict[str, Any],
  63. on_error: Callable[[GraphQLError], None],
  64. ) -> Dict[str, Any]:
  65. coerced_values: Dict[str, Any] = {}
  66. for var_def_node in var_def_nodes:
  67. var_name = var_def_node.variable.name.value
  68. var_type = type_from_ast(schema, var_def_node.type)
  69. if not is_input_type(var_type):
  70. # Must use input types for variables. This should be caught during
  71. # validation, however is checked again here for safety.
  72. var_type_str = print_ast(var_def_node.type)
  73. on_error(
  74. GraphQLError(
  75. f"Variable '${var_name}' expected value of type '{var_type_str}'"
  76. " which cannot be used as an input type.",
  77. var_def_node.type,
  78. )
  79. )
  80. continue
  81. var_type = cast(GraphQLInputType, var_type)
  82. if var_name not in inputs:
  83. if var_def_node.default_value:
  84. coerced_values[var_name] = value_from_ast(
  85. var_def_node.default_value, var_type
  86. )
  87. elif is_non_null_type(var_type): # pragma: no cover else
  88. var_type_str = inspect(var_type)
  89. on_error(
  90. GraphQLError(
  91. f"Variable '${var_name}' of required type '{var_type_str}'"
  92. " was not provided.",
  93. var_def_node,
  94. )
  95. )
  96. continue
  97. value = inputs[var_name]
  98. if value is None and is_non_null_type(var_type):
  99. var_type_str = inspect(var_type)
  100. on_error(
  101. GraphQLError(
  102. f"Variable '${var_name}' of non-null type '{var_type_str}'"
  103. " must not be null.",
  104. var_def_node,
  105. )
  106. )
  107. continue
  108. def on_input_value_error(
  109. path: List[Union[str, int]], invalid_value: Any, error: GraphQLError
  110. ) -> None:
  111. invalid_str = inspect(invalid_value)
  112. prefix = f"Variable '${var_name}' got invalid value {invalid_str}"
  113. if path:
  114. prefix += f" at '{var_name}{print_path_list(path)}'"
  115. on_error(
  116. GraphQLError(
  117. prefix + "; " + error.message,
  118. var_def_node,
  119. original_error=error.original_error,
  120. )
  121. )
  122. coerced_values[var_name] = coerce_input_value(
  123. value, var_type, on_input_value_error
  124. )
  125. return coerced_values
  126. def get_argument_values(
  127. type_def: Union[GraphQLField, GraphQLDirective],
  128. node: Union[FieldNode, DirectiveNode],
  129. variable_values: Optional[Dict[str, Any]] = None,
  130. ) -> Dict[str, Any]:
  131. """Get coerced argument values based on provided definitions and nodes.
  132. Prepares a dict of argument values given a list of argument definitions and list
  133. of argument AST nodes.
  134. """
  135. coerced_values: Dict[str, Any] = {}
  136. arg_node_map = {arg.name.value: arg for arg in node.arguments or []}
  137. for name, arg_def in type_def.args.items():
  138. arg_type = arg_def.type
  139. argument_node = arg_node_map.get(name)
  140. if argument_node is None:
  141. if arg_def.default_value is not Undefined:
  142. coerced_values[arg_def.out_name or name] = arg_def.default_value
  143. elif is_non_null_type(arg_type): # pragma: no cover else
  144. raise GraphQLError(
  145. f"Argument '{name}' of required type '{arg_type}'"
  146. " was not provided.",
  147. node,
  148. )
  149. continue # pragma: no cover
  150. value_node = argument_node.value
  151. is_null = isinstance(argument_node.value, NullValueNode)
  152. if isinstance(value_node, VariableNode):
  153. variable_name = value_node.name.value
  154. if variable_values is None or variable_name not in variable_values:
  155. if arg_def.default_value is not Undefined:
  156. coerced_values[arg_def.out_name or name] = arg_def.default_value
  157. elif is_non_null_type(arg_type): # pragma: no cover else
  158. raise GraphQLError(
  159. f"Argument '{name}' of required type '{arg_type}'"
  160. f" was provided the variable '${variable_name}'"
  161. " which was not provided a runtime value.",
  162. value_node,
  163. )
  164. continue # pragma: no cover
  165. is_null = variable_values[variable_name] is None
  166. if is_null and is_non_null_type(arg_type):
  167. raise GraphQLError(
  168. f"Argument '{name}' of non-null type '{arg_type}' must not be null.",
  169. value_node,
  170. )
  171. coerced_value = value_from_ast(value_node, arg_type, variable_values)
  172. if coerced_value is Undefined:
  173. # Note: `values_of_correct_type` validation should catch this before
  174. # execution. This is a runtime check to ensure execution does not
  175. # continue with an invalid argument value.
  176. raise GraphQLError(
  177. f"Argument '{name}' has invalid value {print_ast(value_node)}.",
  178. value_node,
  179. )
  180. coerced_values[arg_def.out_name or name] = coerced_value
  181. return coerced_values
  182. NodeWithDirective = Union[
  183. EnumValueDefinitionNode,
  184. ExecutableDefinitionNode,
  185. FieldDefinitionNode,
  186. InputValueDefinitionNode,
  187. SelectionNode,
  188. SchemaDefinitionNode,
  189. TypeDefinitionNode,
  190. TypeExtensionNode,
  191. ]
  192. def get_directive_values(
  193. directive_def: GraphQLDirective,
  194. node: NodeWithDirective,
  195. variable_values: Optional[Dict[str, Any]] = None,
  196. ) -> Optional[Dict[str, Any]]:
  197. """Get coerced argument values based on provided nodes.
  198. Prepares a dict of argument values given a directive definition and an AST node
  199. which may contain directives. Optionally also accepts a dict of variable values.
  200. If the directive does not exist on the node, returns None.
  201. """
  202. directives = node.directives
  203. if directives:
  204. directive_name = directive_def.name
  205. for directive in directives:
  206. if directive.name.value == directive_name:
  207. return get_argument_values(directive_def, directive, variable_values)
  208. return None