def perform_operation_validation(request): errors = [] operations = {} fragments = {} for definition in request.ast.definitions: if isinstance(definition, OperationDefinition): if definition.name in operations: raise GraphQLError('Non-unique operation name: {}'.format( definition.name)) else: operations[definition.name] = definition elif isinstance(definition, FragmentDefinition): if definition.name in fragments: raise GraphQLError('Non-unique fragment name: {}'.format( definition.name)) else: fragments[definition.name] = definition if request.operation_name: if request.operation_name not in operations: raise GraphQLError('No operation found called `{}`'.format( request.operation_name)) else: if len(operations) > 1: raise GraphQLError( 'Multiple operations provided but no operation name') elif len(operations) == 0: raise GraphQLError('At least one operation must be provided') return errors
def test_related_object_args(): document = ''' { episode (number: 4) { characters (types: [null]) { name } } } ''' request = Request(document, schema) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'types' on 'characters' is null"), ] document = ''' { episode (number: 4) { characters { appears_in { characters(types: [null]) { name } } } } } ''' request = Request(document, schema) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'types' on 'characters' is null"), ]
def get_value(self): raw_value = self.get_raw_value() if not self.null and raw_value is None: raise GraphQLError( 'Field {} returned null but is not nullable'.format(self.name)) if hasattr(self.type_, 'coerce_result'): try: return self.type_.coerce_result(raw_value) except ValueError: raise GraphQLError('Cannot coerce {} ({}) to {}'.format( type(raw_value).__name__, raw_value, self.type_.object_name)) return raw_value
def validate(self): """ Used to perform validation of a query before execution. Errors produced from validation can be accessed from ``request.errors``. If a Request object has been validated once, additional calls will not re-run validation. """ if self._validated: return self._validated = True try: self._errors.extend(perform_operation_validation(self)) if self._errors: # If the request is invalid, stop validation return self.query_root = self.schema.get_query_root(self) self._errors.extend(perform_argument_validation(self.query_root)) except Exception as e: if not isinstance(e, GraphQLError): e = GraphQLError(e) self.errors.append(e)
def fields(self): if not hasattr(self, '_fields'): self._fields = OrderedDict() selections = get_selections( selections=self.ast.selections, fragments=self.fragments, object_type=self.__class__, ) # Copy the field instances so that obj instances have # isolated field instances that they can modify safely. # Only copy field instances that are selected. # If the field doesn't exist, create a dummy field that returns None for selection in selections: try: field = copy.deepcopy( self._declared_fields[selection.name]) except KeyError: self.errors.append( GraphQLError('{} does not have field {}'.format( self.object_name, selection.name))) field = Field() field.type_ = MockScalar self._fields[selection.name] = field field.bind(selection=selection, obj=self) return self._fields
def validate_non_null_args(field_name, field): """ NonNull arguments are required and cannot be null """ errors = [] for arg_name, arg_type in field.arguments.items(): if not arg_type.null and arg_name not in field.selection_arguments: errors.append( GraphQLError( "Required argument '{}' on '{}' is missing".format( arg_name, field_name))) continue arg_value = field.selection_arguments.get(arg_name) if not non_null_arg_provided(arg_type, arg_value): errors.append( GraphQLError("Non-null argument '{}' on '{}' is null".format( arg_name, field_name))) return errors
def test_null_list_item(): document = ''' { nested(arg: [null]) } ''' request = Request(document, test_schema) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'arg' on 'nested' is null"), ]
def test_missing_int(): document = ''' { nested(arg: [[]]) } ''' request = Request(document, test_schema) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'arg' on 'nested' is null"), ]
def get_resolver_args(self): if not self._bound: raise GraphQLError( 'Usage exception: must bind Field to a selection and object first' ) resolver_args = {name: None for name in self.arguments} for name, value in self.selection_arguments.items(): arg_type = self.arguments.get(name) if not arg_type: continue try: arg_value = arg_type.coerce_input(value) resolver_args[name] = arg_value except ValueError: error = 'Query error: Argument {} expected a {} but got a {}'.format( name, type(arg_type), type(value)) raise GraphQLError(error) return resolver_args
def test_missing_arg(): document = ''' { nested } ''' request = Request(document, test_schema) request.validate() assert request.errors == [ GraphQLError("Required argument 'arg' on 'nested' is missing") ]
def __init__(self, document, schema, variables=None, operation_name=None): """ Creates a Request object that can be validated and executed. :param document: The query string to execute. e.g. ``"query episodeNames { episodes { name } }"`` :param schema: A Schema object to run the query against :param variables: A ``dict`` of variables to pass to the query (optional) :param operation_name: If the document contains multiple named queries, the name of the query to execute (optional) """ self.document = document self.variables = variables or {} self.operation_name = operation_name self.schema = schema self._validated = False self._errors = [] self.query_root = None parser = GraphQLParser() if not self.document: self._errors.append(GraphQLError('Must provide query string.')) else: try: self.ast = parser.parse(self.document) except (graphql_exceptions.LexerError, graphql_exceptions.SyntaxError) as e: self.ast = None self._errors.append( GraphQLError( 'Parse error: {}'.format(e), line=e.line, column=e.column, )) # Additional errors are meaningless if we couldn't parse the document if self._errors: self._validated = True
def execute(self): data = {} for name, field in self.fields.items(): try: value = field.get_value() self.errors.extend(field.errors) except Exception as e: value = None self.errors.append( GraphQLError('Error resolving {}: {}'.format(name, e))) data[name] = value return data, self.errors
def test_null_scalar(): document = ''' { episode(number: null) { name } } ''' request = Request(document, schema) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'number' on 'episode' is null"), ]
def test_missing_scalar(): document = ''' { episode { name } } ''' request = Request(document, schema) request.validate() assert request.errors == [ GraphQLError("Required argument 'number' on 'episode' is missing"), ]
def test_non_existent_field(starwars_data): document = ''' { episode (number: 4) { name other_field } } ''' request = Request(document, schema) data, errors = request.execute() assert data == {"episode": {"name": "A New Hope", "other_field": None}} assert errors == [ GraphQLError('Episode does not have field other_field'), ]
def test_null_int(): document = ''' { nested(arg: [[null]]) } ''' request = Request(document, test_schema) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'arg' on 'nested' is null"), ] document = ''' query nestedArgQuery($int: Int){ nested(arg: [[$int]]) } ''' variables = {'int': None} request = Request(document, test_schema, variables=variables) request.validate() assert request.errors == [ GraphQLError("Non-null argument 'arg' on 'nested' is null"), ]
def test_non_existent_episode(starwars_data): document = ''' { episode (number: 12) { name } } ''' request = Request(document, schema) data, errors = request.execute() assert data == {"episode": None} assert errors == [ GraphQLError( 'Error resolving episode: Episode matching query does not exist.'), ]
def test_blank_query(starwars_data): document = '' request = Request(document, schema) assert request.errors == [ GraphQLError('Must provide query string.'), ]