def validate(self, path, schema, field, parent_type_name, **__): rtype = find_field_reduced_type(parent_type_name, field.name.value, schema) if not rtype: return ( [] ) # Handled by field_selections_on_objects_interfaces_and_unions_types rule # TODO maybe think about rule order/dependencies is_gql_composite_type = isinstance(rtype, GraphQLCompositeType) if not field.selection_set and is_gql_composite_type: return [ graphql_error_from_nodes( message= f"Field {field.name.value} of type {rtype.name} must have a selection of subfields.", nodes=field, path=path, extensions=self._extensions, ) ] if field.selection_set and not is_gql_composite_type: return [ graphql_error_from_nodes( message= f"Field {field.name.value} must not have a selection since type {rtype.name} has no subfields.", nodes=field, path=path, extensions=self._extensions, ) ] return []
def _validate_input_fields( self, arg, schema_type_name, schema_input_fields, object_node, errors, path, schema, ): # pylint: disable=too-many-locals for schema_input_field in schema_input_fields: if (schema_input_field not in [x.name.value for x in object_node.fields] and isinstance( schema_input_fields[schema_input_field].gql_type, GraphQLNonNull, ) and schema_input_fields[schema_input_field].default_value is None): errors.append( graphql_error_from_nodes( message= f"Missing non nullable Input Field < {schema_input_field} > for Input Object < {schema_type_name} >.", nodes=object_node, path=path, extensions=self._extensions, )) for query_field_node in object_node.fields: if query_field_node.name.value not in schema_input_fields: errors.append( graphql_error_from_nodes( message= f"Unknown Input Field < {query_field_node.name.value} > in < {schema_type_name} > Input Object", nodes=query_field_node, path=path, extensions=self._extensions, )) continue c_argument_schema_type = schema_input_fields[ query_field_node.name.value].graphql_type r_argument_schema_type = reduce_type(c_argument_schema_type) if isinstance(r_argument_schema_type, str): r_argument_schema_type = schema.find_type( r_argument_schema_type) errors = self._validate( r_argument_schema_type, c_argument_schema_type, arg, path, errors, schema, value_node=query_field_node.value, input_field=query_field_node, ) return errors
def ensure_valid_runtime_type( runtime_type_or_name: Union["GraphQLObjectType", str], execution_context: "ExecutionContext", return_type: "GraphQLAbstractType", field_nodes: List["FieldNodes"], info: "ResolveInfo", result: Any, ) -> "GraphQLObjectType": """ Validates and returns that the filled in runtime type is valid. :param runtime_type_or_name: name or GraphQLType of the runtime type :param execution_context: instance of the query execution context :param return_type: the GraphQLAbstractType instance of the object :param field_nodes: AST nodes related to the coerced field :param info: information related to the execution and the resolved field :param result: result to treat :type runtime_type_or_name: Union[GraphQLObjectType, str] :type execution_context: ExecutionContext :type return_type: GraphQLAbstractType :type field_nodes: List[FieldNode] :type info: ResolveInfo :type result: Any :return: the GraphQLObjectType representing the runtime type :rtype: GraphQLObjectType """ if isinstance(runtime_type_or_name, str): try: runtime_type = execution_context.schema.find_type( runtime_type_or_name) except KeyError: runtime_type = runtime_type_or_name else: runtime_type = runtime_type_or_name if not isinstance(runtime_type, GraphQLObjectType): raise graphql_error_from_nodes( f"Abstract type < {return_type.name} > must resolve to an object " "type at runtime for field " f"< {info.parent_type.name}.{info.field_name} > with value " f"< {result} >, received < {runtime_type} >. " f"Either the < {return_type.name} > type should implements a " "< @TypeResolver > or the " f"< {info.parent_type.name}.{info.field_name} > field resolver " "should implement a `type_resolver` attribute.", nodes=field_nodes, ) if not return_type.is_possible_type(runtime_type): raise graphql_error_from_nodes( f"Runtime object type < {runtime_type.name} > is not a possible " f"type for < {return_type.name} >.", nodes=field_nodes, ) return runtime_type
def _validate_selection_set(self, operation, selection_set, fragments, path): nb_selections = len(selection_set.selections) if nb_selections > 1: message = f"{f'Subcription {operation.name.value}' if operation.name else 'Anonymous Subscription'}" return [ graphql_error_from_nodes( message=f"{message} must select only one top level field.", nodes=[operation, selection_set], path=path, extensions=self._extensions, ) ] if nb_selections == 1: selected = selection_set.selections[0] if isinstance(selected, FragmentSpreadNode): frag = _find_fragment(fragments, selected.name.value) if not frag: return [] # Handled by another validator return self._validate_selection_set(operation, frag.selection_set, fragments, path) if isinstance(selected, InlineFragmentNode): return self._validate_selection_set(operation, selected.selection_set, fragments, path) return []
def _validate_is_possible( self, type_name, nodes, message, path, schema, locations=None ): # pylint: disable=too-many-locals errors = [] if not schema.has_type(type_name): return [] # Handled by another Validator (5.5.2.1) schema_type = schema.find_type(type_name) if not isinstance(schema_type, GraphQLCompositeType): return [] # Handled by another Validator (5.5.1.3) for index, node in enumerate(nodes): if not _validate_node( node, schema, schema_type.possible_types_set ): location = node.type_condition details = "" if message == "spread": details = f" via < {node.name.value} > Fragment " location = locations[index]["spread"] path = locations[index]["path"] errors.append( graphql_error_from_nodes( message=f"Can't {message} < {node.type_condition.name.value} >{details}on Type < {type_name} >.", nodes=location, path=path, extensions=self._extensions, ) ) return errors
def _validate_operation(self, operation, per_operation, per_fragment, schema): errors = [] for used_arg in _get_args_using_var(operation, per_operation, per_fragment): schema_argument = _find_schema_argument(used_arg, schema) variable_used = _find_variable_by_name( operation.variable_definitions, used_arg["arg"].value.name.value, ) if not schema_argument or not variable_used: continue # Handled by another validators if not _validate_usage(schema_argument, variable_used): errors.append( graphql_error_from_nodes( message= f"Can't use < ${variable_used.variable.name.value} / {variable_used.type} > for type < {schema_argument.gql_type} >.", nodes=[variable_used, used_arg["arg"].value], path=used_arg["path"], extensions=self._extensions, )) return errors
def _validate_input_object( self, arg, schema_argument_definition, object_node, errors, path, schema, ): if not isinstance(object_node, ObjectValueNode): errors.append( graphql_error_from_nodes( message= f"Value is not a valid < {schema_argument_definition.name} > type, Object expected", nodes=object_node, path=path, extensions=self._extensions, )) else: errors = self._validate_input_fields( arg, schema_argument_definition.name, schema_argument_definition.input_fields, object_node, errors, path, schema, ) return errors
def validate(self, operations=None, per_operation=None, per_fragment=None, **_): errors = [] if not operations: return [] # No operation if not per_operation: per_operation = {} if not per_fragment: per_fragment = {} for operation in operations: errors.extend([ graphql_error_from_nodes( message=_get_message(operation, k), nodes=[operation] + v, path=None, extensions=self._extensions, ) for k, v in _validate_operation(operation, per_operation, per_fragment).items() ]) return errors
def __init__(self, fragments, extensions): super().__init__() self.tartiflette_errors = [ graphql_error_from_nodes( message="Fragment Cylcle Detected", path=None, nodes=fragments, extensions=extensions, ) ]
def _to_errors(self, erronous_speads, path): errors = [] for spread_name, spreads in erronous_speads.items(): errors.append( graphql_error_from_nodes( message=f"Unknown Fragment for Spread < {spread_name} >.", nodes=spreads, path=path, extensions=self._extensions, )) return errors
def validate(self, directive, schema, path, **_): if not schema.has_directive(directive.name.value): return [ graphql_error_from_nodes( message=f"Unknow Directive < @{directive.name.value} >.", nodes=directive, path=path, extensions=self._extensions, ) ] return []
async def input_directives_coercer( parent_node: Union["VariableDefinitionNode", "InputValueDefinitionNode"], node: "Node", value: Any, ctx: Optional[Any], coercer: Callable, directives: Optional[Callable], path: Optional["Path"] = None, ) -> "CoercionResult": """ Executes the directives on the coerced value. :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 coercer: pre-computed coercer to use on the value :param directives: the directives to execute :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 coercer: Callable :type directives: Optional[Callable] :type path: Optional[Path] :return: the coercion result :rtype: CoercionResult """ coercion_result = await coercer(parent_node, node, value, ctx, path=path) if not directives: return coercion_result value, errors = coercion_result if errors: return coercion_result try: return CoercionResult(value=await directives( parent_node, value, ctx, context_coercer=ctx)) except Exception as raw_exception: # pylint: disable=broad-except return CoercionResult(errors=[ graphql_error_from_nodes( str(raw_exception), node, original_error=(raw_exception if not is_coercible_exception( raw_exception) else None), ) for raw_exception in (raw_exception.exceptions if isinstance( raw_exception, MultipleException) else [raw_exception]) ])
def validate(self, path, schema, fragment, **__): errors = [] if fragment.type_condition and not schema.has_type( fragment.type_condition.name.value): errors.append( graphql_error_from_nodes( message= f"Unknown type {fragment.type_condition.name.value}.", nodes=fragment, path=path, extensions=self._extensions, )) return errors
def validate(self, variable, path, schema, **__): var_type = get_wrapped_named_type(variable.type) if schema.has_type(var_type.name.value) and not isinstance( schema.find_type(var_type.name.value), GraphQLInputType ): return [ graphql_error_from_nodes( message=f"Variable {variable.variable.name.value} cannot be non-input type {var_type.name.value}.", path=path, nodes=variable, extensions=self._extensions, ) ] return []
def validate(self, definitions, path, **__): bad_nodes = [ x for x in definitions if not isinstance(x, ExecutableDefinitionNode) ] if bad_nodes: return [ graphql_error_from_nodes( message="Theses definitions are not executable.", path=path, nodes=bad_nodes, extensions=self._extensions, ) ] return []
def _validate_arguments(self, parent_node, schema_definition, path, message_suffix): errors = [] for schema_arg in schema_definition.arguments.values(): if (isinstance(schema_arg.graphql_type, GraphQLNonNull) and schema_arg.default_value is None and not find_nodes_by_name(parent_node.arguments, schema_arg.name)): errors.append( graphql_error_from_nodes( message= f"Missing mandatory argument < {schema_arg.name} > {message_suffix}", nodes=parent_node, path=path, extensions=self._extensions, )) return errors
def get_operation_root_type( self, operation: "OperationDefinitionNode") -> "GraphQLObjectType": """ Extracts the root type of the operation from the schema. :param operation: AST operation definition node from which retrieve the root type :type operation: OperationDefinitionNode :return: the GraphQLObjectType instance related to the operation definition :rtype: GraphQLObjectType """ try: return self._operation_types[operation.operation_type] except KeyError: raise graphql_error_from_nodes( "Schema is not configured for %ss." % operation.operation_type, nodes=operation, )
def validate(self, path, fragments, fragment_spreads=None, **__): errors = [] if not fragment_spreads: fragment_spreads = [] for fragment in fragments: if not find_nodes_by_name(fragment_spreads, fragment.name.value): errors.append( graphql_error_from_nodes( message= f"Fragment < {fragment.name.value} > is never used.", nodes=fragment, path=path, extensions=self._extensions, )) return errors
def validate(self, path, schema, field, parent_type_name, **__): graphql_type = find_field_reduced_type( parent_type_name, field.name.value, schema ) if field.name.value.startswith("__"): return [] if graphql_type is None: return [ graphql_error_from_nodes( message=f"Field {field.name.value} doesn't exist on {parent_type_name or 'Root'}", nodes=field, path=path, extensions=self._extensions, ) ] return []
def _validate_directive_arguments(self, query_node, path, schema): errors = [] if not schema.has_directive(query_node.name.value): return [] # Handled by another Validator schema_directive = schema.find_directive(query_node.name.value) for argument in query_node.arguments: if argument.name.value not in schema_directive.arguments: errors.append( graphql_error_from_nodes( message=f"Provided Argument < {argument.name.value} > doesn't exist on directive < @{schema_directive.name} >.", nodes=argument, path=path, extensions=self._extensions, ) ) return errors
def validate(self, path, operations, **__): bad_nodes = [] errors = [] if len(operations) > 1: for operation in operations: if operation.name is None: bad_nodes.append(operation) if bad_nodes: errors.append( graphql_error_from_nodes( message="Anonymous operation must be the only defined operation.", path=path, nodes=bad_nodes, extensions=self._extensions, ) ) return errors
def validate(self, path, schema, fragment, **__): errors = [] if (fragment.type_condition and schema.has_type(fragment.type_condition.name.value) and not isinstance( schema.find_type(fragment.type_condition.name.value), GraphQLCompositeType, )): message = (f"Fragment {fragment.name.value}" if not isinstance(fragment, InlineFragmentNode) else f"Inline Fragment") errors.append( graphql_error_from_nodes( message= f"{message} cannot condition on non composite type {fragment.type_condition.name.value}.", nodes=fragment, path=path, extensions=self._extensions, )) return errors
def validate(self, path, fragments, **__): errors = [] already_tested = [] for fragment in fragments: if fragment.name.value in already_tested: continue with_same_name = find_nodes_by_name(fragments, fragment.name.value) if len(with_same_name) > 1: already_tested.append(fragment.name.value) errors.append( graphql_error_from_nodes( message= f"Can't have multiple fragments named < {fragment.name.value} >.", path=path, nodes=with_same_name, extensions=self._extensions, )) return errors
def _validate_field_arguments(self, query_field, path, schema, parent_type_name): errors = [] schema_field = find_field(parent_type_name, query_field.name.value, schema) if not schema_field: return [] # Handled somewhere else. for query_field_argument in query_field.arguments: if query_field_argument.name.value not in schema_field.arguments: errors.append( graphql_error_from_nodes( message= f"Provided Argument < {query_field_argument.name.value} > doesn't exists on field < {parent_type_name}.{schema_field.name} >.", nodes=query_field_argument, path=path, extensions=self._extensions, )) return errors
def validate(self, path, operations, **__): errors = [] already_tested = [] for operation in operations: if not operation.name or operation.name.value in already_tested: continue with_same_name = find_nodes_by_name(operations, operation.name.value) if len(with_same_name) > 1: already_tested.append(operation.name.value) errors.append( graphql_error_from_nodes( message= f"Can't have multiple operations named < {operation.name.value} >.", path=path, nodes=with_same_name, extensions=self._extensions, )) return errors
def validate(self, path, input_fields, **__): errors = [] already_tested = [] for ifield in input_fields: if ifield.name.value in already_tested: continue with_same_name = find_nodes_by_name(input_fields, ifield.name.value) if len(with_same_name) > 1: already_tested.append(ifield.name.value) errors.append( graphql_error_from_nodes( message= f"Can't have multiple Input Field named < {ifield.name.value} >.", path=path, nodes=with_same_name, extensions=self._extensions, )) return errors
def validate(self, path, variable_definitions, **__): errors = [] already_tested = [] variables = [x.variable for x in variable_definitions] for variable in variables: if variable.name.value in already_tested: continue with_same_name = find_nodes_by_name(variables, variable.name.value) if len(with_same_name) > 1: already_tested.append(variable.name.value) errors.append( graphql_error_from_nodes( message= f"Can't have multiple variables named < {variable.name.value} >.", path=path, nodes=with_same_name, extensions=self._extensions, )) return errors
def validate(self, directives, path, **_): errors = [] already_tested = [] for directive in directives: if directive.name.value in already_tested: continue with_same_name = find_nodes_by_name( directives, directive.name.value ) if len(with_same_name) > 1: already_tested.append(directive.name.value) errors.append( graphql_error_from_nodes( message=f"Can't have multiple directives named < {directive.name.value} > in the same location.", path=path, nodes=with_same_name, extensions=self._extensions, ) ) return errors
def validate(self, node, path, schema, **_): errors = [] node_type = type(node) if isinstance(node, OperationDefinitionNode): node_type = node.operation_type.lower() for directive in node.directives: if not schema.has_directive(directive.name.value): continue # Handled by another validator (5.7.1) schema_directive = schema.find_directive(directive.name.value) if (_NODE_TO_DIRECTIVE_LOCATION_MAP[node_type] not in schema_directive.locations): errors.append( graphql_error_from_nodes( message= f"Directive < @{directive.name.value} > is not used in a valid location.", nodes=[node, directive], path=path, extensions=self._extensions, )) return errors
def _validate( self, r_argument_schema_type, c_argument_schema_type, arg, path, errors, schema, value_node=None, input_field=None, ): # pylint: disable=too-many-locals,too-many-arguments,too-many-branches,too-complex if value_node is None: value_node = arg.value if isinstance(value_node, VariableNode): # Handled by another Validator return errors if isinstance(c_argument_schema_type, GraphQLNonNull): if isinstance(value_node, NullValueNode): errors.append( graphql_error_from_nodes( message= f"Argument < {arg.name.value} > of non-null type < {c_argument_schema_type} > must not be null." if not input_field else f"Input Field < {input_field.name.value} > of non-null type < {c_argument_schema_type} > must not be null.", nodes=arg if not input_field else input_field, extensions=self._extensions, path=path, )) return errors errors = self._validate( r_argument_schema_type, c_argument_schema_type.gql_type, arg, path, errors, schema, value_node=value_node, input_field=input_field, ) return errors if isinstance(c_argument_schema_type, GraphQLList): if isinstance(value_node, NullValueNode): return errors arg_values = ([value_node] if not isinstance(value_node, ListValueNode) else value_node.values) for arg_value in arg_values: if isinstance(arg_value, VariableNode): continue # Handled by another validator errors = self._validate( r_argument_schema_type, c_argument_schema_type.gql_type, arg, path, errors, schema, value_node=arg_value, input_field=input_field, ) return errors if isinstance(value_node, NullValueNode): return errors # Because it's not non null, null node is okay if isinstance(r_argument_schema_type, GraphQLScalarType) and ( r_argument_schema_type.parse_literal(value_node) is UNDEFINED_VALUE): errors.append( graphql_error_from_nodes( message= f"Value {value_node.value} is not of correct type {r_argument_schema_type.name}", nodes=input_field or arg, extensions=self._extensions, path=path, )) return errors if isinstance(r_argument_schema_type, GraphQLInputObjectType): errors = self._validate_input_object( arg=arg, schema_argument_definition=r_argument_schema_type, object_node=value_node, errors=errors, path=path, schema=schema, ) return errors if isinstance(r_argument_schema_type, GraphQLEnumType) and value_node.value not in [ x.value for x in r_argument_schema_type.values ]: errors.append( graphql_error_from_nodes( message= f"Value {value_node.value} is not a valid value for enum {r_argument_schema_type.name}", nodes=arg, path=path, extensions=self._extensions, )) return errors return errors