class QueryEngine: """ GraphQL query engine. Usage: graphql = QueryEngine() graphql.addModel('user', user) result = graphql.query(''' { user(id: 4) { name } } ''') # result = {'user': {'name': 'Mark Zuckerberg'}} """ def __init__(self, **kwargs): self._models = {} self._parser = GraphQLParser() def addModel(self, name, model): self._models[name] = model def _getAST(self, query): return self._parser.parse(query) def query(self, query): ast = self._getAST(query)
def execute(self, document): """ Queries the schema in python. :param document: A GraphQL query string :return: JSON of returned data or errors e.g. :: query = ''' { users { name } } ''' schema.execute(query) Might return :: { "data": { "users": [ {"name": "Buffy Summers"}, {"name": "Willow Rosenberg"}, {"name": "Xander Harris"} ] } } """ parser = GraphQLParser() ast = parser.parse(document) queries = [ definition for definition in ast.definitions if isinstance(definition, Query) ] assert len(queries) == 1, "Exactly one query must be defined" fragments = { definition.name: definition for definition in ast.definitions if isinstance(definition, FragmentDefinition) } return { 'data': self.query_root( ast=queries[0], data=None, fragments=fragments, ).serialize(), }
def execute(self, document): """ Queries the schema in python. :param document: A GraphQL query string :return: JSON of returned data or errors e.g. :: query = ''' { users { name } } ''' schema.execute(query) Might return :: { "data": { "users": [ {"name": "Buffy Summers"}, {"name": "Willow Rosenberg"}, {"name": "Xander Harris"} ] } } """ parser = GraphQLParser() ast = parser.parse(document) query_ast = ast.definitions[0] if any(selection.name == '__schema' for selection in query_ast.selections): raise NotImplementedError( 'This version of django-graph-api does not support introspection' ) return { 'data': self.query_root(query_ast, None).serialize(), }
def get_completions(gql, idx, schema): """Creates AST from `gql` query string, finds out exactly where cursor is in string, and uses `schema` to get appropriate completions. Doesn't protect against exceptions. They should be handled by calling code. """ try: # at module import time this package is not available from graphql.parser import GraphQLParser from graphql.lexer import GraphQLLexer except ImportError: raise Exception('Install graphql-py with pip for GraphQL autocomplete') try: # monkey-patch this class, the `t_NULL` method breaks parsing delattr(GraphQLLexer, 't_NULL') except AttributeError: pass start, end = slurp_word(gql, idx) gql_parser = GraphQLParser() ast = gql_parser.parse(gql[:start] + placeholder + gql[end:], lexer=GraphQLLexer()) for query in ast.definitions: # get path if it exists path = placeholder_path(query, placeholder) if path is not None: break query_type, types = schema t = resolve_type(path, types, query_type) fields = types[t]['fields'] completions = [] for f in fields.values(): name = f['name'] args = [a['name'] + ':' for a in f['args']] args_string = '({})'.format(', '.join(args)) if args else '' type_name = resolve_field_type(f) completions.append([ '{}{}\t{}'.format(name, args_string, type_name), '{}{}'.format(name, args_string), ]) return (completions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS)
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 main(): moreDetails = False if len(sys.argv) > 1 and sys.argv[1] == 'True': moreDetails = True print( "You have chosen to get some more details of your schema coverage. This will be provided in a file in root " "called 'individualSchemaCoverage.csv'. The overview of the covered schema will be printed to a file " "called 'schemaCoverageDictionary.csv'.") with open('individualSchemaCoverage.csv', 'w') as csvfile: csvwriter = csv.writer(csvfile) csvwriter.writerow(['testID', 'individualCoverage']) templateLoader = jinja2.FileSystemLoader(searchpath="") templateEnv = jinja2.Environment(loader=templateLoader) template = templateEnv.get_template(cfg.test_template) parser = GraphQLParser() encoder = json.JSONEncoder() types = requests.post(cfg.graphql_url, data=encoder.encode(cfg.schema_query), headers={'content-type': 'application/json'}) schema = json.loads(types.content)['data']['__schema'] #jsonschema = json.dumps(schema) #jsonFile = open('schema.json', 'w+') #jsonFile.write(jsonschema) createDict = CreateDictionaries(schema) possValuesDict = createDict.possibleValuesDictionary() schemaCoverageDict = createDict.schemaCoverageDictionary() searcher = SchemaSearcher(schema, schemaCoverageDict) walker = AstWalker(searcher) createAssertions = CreateAssertions(possValuesDict) for f in os.listdir('queries/'): id = f.split('.json')[0] if id == '.DS_Store': continue testName = 'Q' + ''.join(id.split('-')) + 'Test' payload = open('queries/' + f).read() jsonPayload = "<<<'JSON'\n" + payload + "\nJSON" try: dict = json.loads(payload) except: print("Couldn't load " + id) continue try: astree = parser.parse(dict['query']) except: print('Something is wrong with test ' + id) continue mutation = False query = None # Checking there are no mutations in query for tree in astree.definitions: if type(tree) == graphql.ast.Mutation: print(id + ' contains mutations and will not be used') mutation = True break # Skipping current query if contains mutations if mutation: continue searcher.setId(id) # Checking other types in query for tree in astree.definitions: if type(tree) == graphql.ast.FragmentDefinition: success = createDict.createFragmentDictionary(tree, walker) if success: walker.fragmentDictionary = createDict.fragmentDictionary else: astree.definitions.append(tree) continue elif type(tree) == graphql.ast.Query or type(tree) == None: query = tree rootNode = walker.walk(query, None) if moreDetails: createSchemaDict = CreateDictionaries(schema) individualSchemaCoverageDict = createSchemaDict.schemaCoverageDictionary( ) schemaSearcher = SchemaSearcher(schema, individualSchemaCoverageDict) schemaWalker = AstWalker(schemaSearcher) schemaWalker.fragmentDictionary = createDict.fragmentDictionary schemaWalker.walk(query, None) with open('individualSchemaCoverage.csv', 'a') as csvfile: csvwriter = csv.writer(csvfile) csvwriter.writerow( [id, (schemaSearcher.calculateSchemaCoverage() * 100)]) variables = ['$a', '$b', '$c', '$d', '$e', '$f', '$g'] try: assertions = [] for node in rootNode: nodeAssertions = createAssertions.createAssertions( node, variables) for line in nodeAssertions: assertions.append(line) output = template.render(className=testName, query=jsonPayload, allAssertions=assertions, graphQLURL=cfg.graphql_url, authToken=cfg.authorization_token) testfile = open('testCases/' + testName + '.php', 'w') testfile.write(output) testfile.close() except: continue if moreDetails: with open('schemaCoverageDictionary.csv', 'w') as csvfile: csvwriter = csv.writer(csvfile) csvwriter.writerow( ['schemaTuple', 'visited', 'timesVisited', 'id']) for line in schemaCoverageDict: csvwriter.writerow([ line, schemaCoverageDict[line][1], schemaCoverageDict[line][0], schemaCoverageDict[line][2] ]) print("The schema coverage for the generated test suite is: " + str(searcher.calculateSchemaCoverage() * 100) + ' %' + " where mutations are: " + str(searcher.calculateMutations() * 100) + ' % of the schema and input objects are: ' + str(searcher.calculateInputTypes() * 100) + ' % of the schema.')
def get_graphql_tree(content): parser = GraphQLParser() ast = parser.parse(query) return ast.definitions[0].selections[0]