def prepare_graphql_query_for(schema_str: str, query_str: str) -> QueryContext: """ Given a schema and a query against it, prepare everything needed to do low-level testing Args: schema_str: GraphQL Schema string query_str: User query string Returns: schema: compiled schema query: compiled query execution_context: the low-level object for query execution info: ResolveInfo to be passed to resolvers """ # Prepare the schema and the query document schema = graphql.build_schema(schema_str) query = graphql.parse(query_str) # Validate graphql.validate(schema, query) # Prepare ResolveInfo for the top-level object (query) execution_context: graphql.ExecutionContext = graphql.ExecutionContext.build( schema=schema, document=query) # type: ignore[assignment] info = build_resolve_info_for(schema, query, execution_context) # Done return QueryContext(schema=schema, query=query, execution_context=execution_context, info=info)
def load_and_validate(path, fobj=None): if fobj is None: with open(path, 'rt', encoding='utf-8') as fobj: provider, code, has_code = read_code(fobj) else: provider, code, has_code = read_code(fobj) # graphql can't handle empty files if has_code: gast = graphql.parse(graphql.Source(code, path)) else: gast = graphql.language.DocumentNode(definitions=[]) if provider is None: # FIXME: Lump this with above raise MissingProviderError(path) schema = query_for_schema(provider) errors = graphql.validate(schema, gast) if not errors: # Just automatically compute type and ref annotations. We'll probably need it. annotate(gast, schema) return provider, gast, schema, errors
def execute_gql_query(): query, variables, operation_name = get_query() validation_errors = validate( schema=get_schema(), document_ast=parse(query), rules=( depth_limit_validator( max_depth=cint(frappe.local.conf.get("frappe_graphql_depth_limit")) or 10 ), ) ) if validation_errors: output = frappe._dict(errors=validation_errors) else: output = execute( query=query, variables=variables, operation_name=operation_name ) frappe.clear_messages() frappe.local.response = output if len(output.get("errors", [])): frappe.db.rollback() log_error(query, variables, operation_name, output) frappe.local.response["http_status_code"] = get_max_http_status_code(output.get("errors")) errors = [] for err in output.errors: if isinstance(err, GraphQLError): err = err.formatted errors.append(err) output.errors = errors
def test_should_not_allow_fragments(validation_schema): sub = 'subscription S5{ ...testFragment }\ fragment testFragment on Subscription{ test2 }' errors = validate(validation_schema, parse(sub), [SubscriptionHasSingleRootField]) assert len(errors) == 1 assert errors[0].message == 'Apollo subscriptions do not support\
def parse( self, query: str, full_fragments: str = "", should_validate: bool = True, is_fragment: bool = False, ) -> ParsedQuery: query_document_ast = parse("".join([full_fragments, query])) document_ast = parse(query) if not is_fragment: operation = get_operation_ast(document_ast) if not operation.name: raise AnonymousQueryError() if should_validate: errors = validate( self.schema, query_document_ast, [rule for rule in specified_rules if rule is not NoUnusedFragmentsRule], ) if errors: raise InvalidQueryError(errors) type_info = TypeInfo(self.schema) visitor = FieldToTypeMatcherVisitor(self.schema, type_info, query) visit(document_ast, TypeInfoVisitor(type_info, visitor)) result = visitor.parsed return result
async def _aio_query(self, query): query = graphql.parse(query) errors = graphql.validate(self.schema, query) if errors: r = graphql.execution.ExecutionResult() r.errors = errors return r async def field_resolver(parent, resolve_info, **args): field = resolve_info.field_name if parent is not None and field in parent: return default_field_resolver(parent, resolve_info, **args) ep = self.data.getEndPointForResourceName(field) rspec = None kwargs = ep.get_kwargs_from_graphql(parent, resolve_info, args) if ep.isCollection: args = {k: [v] for k, v in args.items()} rspec = self.data.resultspec_from_jsonapi( args, ep.rtype.entityType, True) return await self._aio_ep_get(ep, kwargs, rspec) # Execute res = await graphql.execute( self.schema, query, field_resolver=field_resolver, ) return res
def query(self, q): try: source = Source(q, name='GraphQL request') ast = parse(source) validation_errors = validate(self.schema, ast) if validation_errors: return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) try: return execute(self.__schema, ast, root_value=None, variable_values={}, operation_name=None, context_value={ 'query': q, 'introspection': 'introspection' in q.lower() }, middleware=self.__middleware, executor=self.__executor) except Exception as e: return ExecutionResult(errors=[e], invalid=True)
async def call( self, document: GraphQLSchema, request: typing.Any = None, variables: typing.Dict[str, typing.Any] = None ) -> ExecutionResult: """Preform a query against the schema. This is meant to be called in an asyncio.loop, if you are using a web framework that is synchronous use the `call_sync` method. """ validation_errors = validate(self.schema, document) if validation_errors: return ExecutionResult(data=None, errors=validation_errors) context = self.get_context(request) result = execute( schema=self.schema, document=document, context_value=context, variable_values=variables, middleware=self.middleware, ) if inspect.isawaitable(result): return await result return result
def validate(self, document): if not self.schema: raise Exception( "Cannot validate the document locally, you need to pass a schema." ) validation_errors = validate(self.schema, document) if validation_errors: raise validation_errors[0]
def validate(self, document): assert ( self.schema ), "Cannot validate the document locally, you need to pass a schema." validation_errors = validate(self.schema, document) if validation_errors: raise validation_errors[0]
def run_query(query: str): document = parse(query) return validate( schema=schema.graphql_schema, document_ast=document, rules=(DisableIntrospection, ), )
def execute_graphql_request(self, request): ''' THIS IS IMPLEMENTED IN A SUB-OPTIMAL MANNER. DO NOT... A.) Judge Me. B.) Use unless you accept that the performance here probably is miserable. ''' subqueries = self.parse_body(request) results = [] for subquery in subqueries: query, variables, operation_name, _id = self.get_graphql_params(subquery) if not query: raise HttpError(HttpResponseBadRequest('Must provide query string.')) source = Source(query, name='GraphQL request') try: document_ast = parse(source) validation_errors = validate(self.schema, document_ast) if validation_errors: # TODO: Do not return here. We should handle this per subquery. return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) if request.method.lower() == 'get': operation_ast = get_operation_ast(document_ast, operation_name) if operation_ast and operation_ast.operation != 'query': raise HttpError(HttpResponseNotAllowed( ['POST'], 'Can only perform a {} operation from a POST request.'.format(operation_ast.operation) )) try: result = self.execute( document_ast, root_value=self.get_root_value(request), variable_values=variables, operation_name=operation_name, context_value=self.get_context(request) ) # TODO: This is really optimistic. # We may have status, we may have errors, etc... # payload should be set according to graphql spec, not # simply "IT WORKED Spec". results.append({ "id": _id, "payload": { "data": result.data, } }) except Exception as e: return ExecutionResult(errors=[e], invalid=True) return results
def run_custom_validation(schema: GraphQLSchema, query: str, rules: Sequence[RuleType]) -> Sequence[GraphQLError]: try: document_ast = parse(query) except: # pylint: disable=bare-except # Query does not validate return [] else: return _graphql.validate(schema, document_ast, rules)
def validate(self, document: DocumentNode): """:meta private:""" assert ( self.schema ), "Cannot validate the document locally, you need to pass a schema." validation_errors = validate(self.schema, document) if validation_errors: raise validation_errors[0]
def execute_graphql_request(self, method, query, variables, operation_name, show_graphiql=False): if not query: if show_graphiql: raise Return(None) raise HTTPError(400, 'Must provide query string.') if not self.document: try: backend = self.get_backend() self.document = backend.document_from_string( self.schema, query) except Exception as e: raise Return(ExecutionResult(errors=[e], invalid=True)) try: validation_errors = validate(self.schema, self.document.document_ast) except Exception as e: raise Return(ExecutionResult(errors=[e], invalid=True)) if validation_errors: raise Return( ExecutionResult( errors=validation_errors, invalid=True, )) if method.lower() == 'get': operation_type = self.document.get_operation_type(operation_name) if operation_type and operation_type != "query": if show_graphiql: raise Return(None) raise HTTPError( 405, 'Can only perform a {} operation from a POST request.'. format(operation_type)) try: result = yield self.execute(self.document.document_ast, root=self.get_root(), variables=variables, operation_name=operation_name, context=self.context, middleware=self.get_middleware(), executor=self.executor or TornadoExecutor(), return_promise=True) except Exception as e: raise Return(ExecutionResult(errors=[e], invalid=True)) raise Return(result)
def execute_query(self, query: str) -> ExecutionResult: try: ast = get_ast(query) except GraphQLSyntaxError as e: return ExecutionResult(errors=[e], invalid=True) validation_errors = validate(self.schema, ast) if validation_errors: return ExecutionResult(errors=validation_errors, invalid=True) return execute(self.schema, ast)
def inner(schema, query): if isinstance(schema, str): parsed_schema = cached_build_schema(schema) else: parsed_schema = schema query_ast = graphql.parse(query) errors = graphql.validate(parsed_schema, query_ast) for error in errors: print(error) assert not errors, query return query_ast
def graphql_sync( # pylint: disable=too-complex,too-many-locals schema: GraphQLSchema, data: Any, *, context_value: Optional[Any] = None, root_value: Optional[RootValue] = None, debug: bool = False, logger: Optional[str] = None, validation_rules=None, error_formatter: ErrorFormatter = format_error, middleware: Middleware = None, **kwargs, ) -> GraphQLResult: try: validate_data(data) query, variables, operation_name = ( data["query"], data.get("variables"), data.get("operationName"), ) document = parse(query) if validation_rules: errors = _graphql.validate(schema, document, validation_rules) if errors: return handle_graphql_errors(errors, logger=logger, error_formatter=error_formatter, debug=debug) if callable(root_value): root_value = root_value(context_value, document) result = _graphql.graphql_sync( schema, query, root_value=root_value, context_value=context_value, variable_values=variables, operation_name=operation_name, middleware=middleware, **kwargs, ) except GraphQLError as error: return handle_graphql_errors([error], logger=logger, error_formatter=error_formatter, debug=debug) else: return handle_query_result(result, logger=logger, error_formatter=error_formatter, debug=debug)
async def _ws_on_start( self, data: Any, operation_id: str, websocket: WebSocket, subscriptions: Dict[str, AsyncGenerator], ): query = data["query"] variable_values = data.get("variables") operation_name = data.get("operationName") context_value = await self._get_context_value(websocket) errors: List[GraphQLError] = [] operation: Optional[OperationDefinitionNode] = None document: Optional[DocumentNode] = None try: document = parse(query) operation = get_operation_ast(document, operation_name) errors = validate(self.schema.graphql_schema, document) except GraphQLError as e: errors = [e] if not errors: if operation and operation.operation == OperationType.SUBSCRIPTION: errors = await self._start_subscription( websocket, operation_id, subscriptions, document, context_value, variable_values, operation_name, ) else: errors = await self._handle_query_over_ws( websocket, operation_id, subscriptions, document, context_value, variable_values, operation_name, ) if errors: await websocket.send_json({ "type": GQL_ERROR, "id": operation_id, "payload": self.error_formatter(errors[0]), })
def __init__(self, schema, request_string, execute_params=None): self.schema = schema self.request_string = request_string self.execute_params = execute_params or {} try: self.document_ast = parse(request_string) validation_errors = validate(schema, self.document_ast) if validation_errors: self.errors = validation_errors except Exception as e: self.document_ast = None self.errors = [e]
def execute_query(self, query: str) -> ExecutionResult: source = Source(query) try: document_ast = parse(source=source) except GraphQLSyntaxError as e: return ExecutionResult(errors=[e], invalid=True) validation_errors = validate(self.schema, document_ast) if validation_errors: return ExecutionResult(errors=validation_errors, invalid=True) return execute(self.schema, document_ast)
def execute_graphql_request(self, request, data, query, variables, operation_name, show_graphiql=False): if os.getenv('DEBUG_ESCAPE_GRAPHQL', False) == 'True': return self.escaped_execute_graphql_request( request, data, query, variables, operation_name, show_graphiql) if not query: if show_graphiql: return None raise HttpError( HttpResponseBadRequest('Must provide query string.')) source = Source(query, name='GraphQL request') try: document_ast = parse(source) validation_errors = validate(self.schema, document_ast) if validation_errors: return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) if request.method.lower() == 'get': operation_ast = get_operation_ast(document_ast, operation_name) if operation_ast and operation_ast.operation != 'query': if show_graphiql: return None raise HttpError( HttpResponseNotAllowed( ['POST'], 'Can only perform a {} operation from a POST request.'. format(operation_ast.operation))) try: return self.execute( document_ast, root_value=self.get_root_value(request), variable_values=variables, operation_name=operation_name, context_value=self.get_context(request), middleware=self.get_middleware(request), executor=self.executor, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True)
async def subscribe( # pylint: disable=too-complex, too-many-locals schema: GraphQLSchema, data: Any, *, context_value: Optional[Any] = None, root_value: Optional[RootValue] = None, debug: bool = False, logger: Optional[str] = None, validation_rules=None, error_formatter: ErrorFormatter = format_error, **kwargs, ) -> SubscriptionResult: try: validate_data(data) query, variables, operation_name = ( data["query"], data.get("variables"), data.get("operationName"), ) document = parse(query) if validation_rules: errors = _graphql.validate(schema, document, validation_rules) if errors: for error in errors: log_error(error, logger) return False, [ error_formatter(error, debug) for error in errors ] if callable(root_value): root_value = root_value(context_value, document) result = await _graphql.subscribe( schema, document, root_value=root_value, context_value=context_value, variable_values=variables, operation_name=operation_name, **kwargs, ) except GraphQLError as error: log_error(error, logger) return False, [error_formatter(error, debug)] else: if isinstance(result, ExecutionResult): errors = cast(List[GraphQLError], result.errors) for error_ in errors: # mypy issue #5080 log_error(error_, logger) return False, [error_formatter(error, debug) for error in errors] return True, cast(AsyncGenerator, result)
def execute_graphql_request(self, method, query, variables, operation_name, show_graphiql=False): if not query: if show_graphiql: raise Return(None) raise HTTPError(400, 'Must provide query string.') source = Source(query, name='GraphQL request') try: document_ast = parse(source) validation_errors = validate(self.schema, document_ast) except Exception as e: raise Return(ExecutionResult(errors=[e], invalid=True)) if validation_errors: raise Return( ExecutionResult( errors=validation_errors, invalid=True, )) if method.lower() == 'get': operation_ast = get_operation_ast(document_ast, operation_name) if operation_ast and operation_ast.operation != 'query': if show_graphiql: raise Return(None) raise HTTPError( 405, 'Can only perform a {} operation from a POST request.'. format(operation_ast.operation)) try: result = yield self.execute(document_ast, root_value=self.root_value, variable_values=variables, operation_name=operation_name, context_value=self.context, middleware=self.middleware, executor=self.executor or TornadoExecutor(), return_promise=True) except Exception as e: raise Return(ExecutionResult(errors=[e], invalid=True)) raise Return(result)
def execute_graphql_request( self, request, data, query, variables, operation_name, ): if not query: raise HttpError( HttpResponseBadRequest("Must provide query string.")) with opentracing.global_tracer().start_active_span( "graphql_query") as scope: span = scope.span span.set_tag(opentracing.tags.COMPONENT, "GraphQL") try: document = parse(query) except GraphQLError as e: return ExecutionResult(errors=[e], data=dict(invalid=True)) if request.method.lower() == "get": operation_ast = get_operation_ast(document, operation_name) if operation_ast and operation_ast.operation != OperationType.QUERY: raise HttpError( HttpResponseNotAllowed( ["POST"], "Can only perform a {} operation from a POST request." .format(operation_ast.operation.value), )) validation_errors = validate(self.schema.graphql_schema, document) if validation_errors: return ExecutionResult(data=None, errors=validation_errors) try: with connection.execute_wrapper(tracing_wrapper): return self.schema.execute( source=query, root_value=self.get_root_value(request), variable_values=variables, operation_name=operation_name, context_value=self.get_context(request), middleware=self.get_middleware(request), ) except GraphQLError as e: span.set_tag(opentracing.tags.ERROR, True) return ExecutionResult(errors=[e])
async def subscribe(self, query, *args, **kwargs): """Execute a GraphQL subscription on the schema asynchronously.""" # Do parsing try: document = parse(query) except GraphQLError as error: return ExecutionResult(data=None, errors=[error]) # Do validation validation_errors = validate(self.graphql_schema, document) if validation_errors: return ExecutionResult(data=None, errors=validation_errors) # Execute the query kwargs = normalize_execute_kwargs(kwargs) return await subscribe(self.graphql_schema, document, *args, **kwargs)
async def execute_graphql_request(self, request, data, query, variables, operation_name, show_graphiql=False): if not query: if show_graphiql: return None raise HttpError( SanicException('Must provide query string.', status_code=400)) try: source = Source(query, name='GraphQL request') ast = parse(source) validation_errors = validate(self.schema, ast) if validation_errors: return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) if request.method.lower() == 'get': operation_ast = get_operation_ast(ast, operation_name) if operation_ast and operation_ast.operation != 'query': if show_graphiql: return None raise HttpError( SanicException( 'Can only perform a {} operation from a POST request.'. format(operation_ast.operation), status_code=405, )) try: return await self.execute(ast, root_value=self.get_root_value(request), variable_values=variables or {}, operation_name=operation_name, context_value=self.get_context(request), middleware=self.get_middleware(request), executor=self.get_executor(request)) except Exception as e: return ExecutionResult(errors=[e], invalid=True)
def parse(self, query: str, should_validate: bool = True) -> ParsedQuery: document_ast = parse(query) operation = get_operation_ast(document_ast) if not operation.name: raise AnonymousQueryError() if should_validate: errors = validate(self.schema, document_ast) if errors: raise InvalidQueryError(errors) type_info = TypeInfo(self.schema) visitor = FieldToTypeMatcherVisitor(self.schema, type_info, query) visit(document_ast, TypeInfoVisitor(type_info, visitor)) result = visitor.parsed return result
def run_query(query: str, max_depth: int, ignore=None): document = parse(query) result = None def callback(query_depths): nonlocal result result = query_depths errors = validate( schema=schema.graphql_schema, document_ast=document, rules=(depth_limit_validator(max_depth=max_depth, ignore=ignore, callback=callback), ), ) return errors, result
def run_query(query: str, max_depth: int, ignore=None): document = parse(query) result = None def callback(query_depths): nonlocal result result = query_depths validation_rule = create_validator(max_depth, ignore, callback) errors = validate( schema._schema, document, rules=(specified_rules + [validation_rule]), ) return errors, result
def execute_graphql_request(self, data, query, variables, operation_name, show_graphiql=False): if not query: if show_graphiql: return None raise HttpError(BadRequest('Must provide query string.')) try: source = Source(query, name='GraphQL request') ast = parse(source) validation_errors = validate(self.schema, ast) if validation_errors: return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) if request.method.lower() == 'get': operation_ast = get_operation_ast(ast, operation_name) if operation_ast and operation_ast.operation != 'query': if show_graphiql: return None raise HttpError( MethodNotAllowed( ['POST'], 'Can only perform a {} operation from a POST request.'. format(operation_ast.operation))) try: return self.execute(ast, root_value=self.get_root_value(request), variable_values=variables or {}, operation_name=operation_name, context_value=self.get_context(request), executor=self.executor) except Exception as e: return ExecutionResult(errors=[e], invalid=True)
def execute_graphql(source, variables, operation_name=None): schema = build_schema(request.env) try: document_ast = parse(source) validation_errors = validate(schema, document_ast) if validation_errors: return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) return execute( document_ast, variable_values=variables, operation_name=operation_name, context_value=request )
def execute_graphql_request(self, request, data, query, variables, operation_name, show_graphiql=False): if not query: if show_graphiql: return None raise HttpError(HttpResponseBadRequest('Must provide query string.')) source = Source(query, name='GraphQL request') try: document_ast = parse(source) validation_errors = validate(self.schema, document_ast) if validation_errors: return ExecutionResult( errors=validation_errors, invalid=True, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True) if request.method.lower() == 'get': operation_ast = get_operation_ast(document_ast, operation_name) if operation_ast and operation_ast.operation != 'query': if show_graphiql: return None raise HttpError(HttpResponseNotAllowed( ['POST'], 'Can only perform a {} operation from a POST request.'.format(operation_ast.operation) )) try: return self.execute( document_ast, root_value=self.get_root_value(request), variable_values=variables, operation_name=operation_name, context_value=self.get_context(request), middleware=self.get_middleware(request), executor=self.executor, ) except Exception as e: return ExecutionResult(errors=[e], invalid=True)
def validation_errors(self, ast): return validate(self.get_schema(), ast)
def expect_valid(schema, query_string): errors = validate(schema, parse(query_string)) assert not errors