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 validate_graphql_request(payload: str): """Method to parse http request payload verify it is a valid GraphQL query or mutation and return a GraphQL document.""" try: return GraphQLParser().parse(payload) except Exception: raise RequestException(400, 'Invalid GraphQL payload.')
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) 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
class GraphQLParseTest(TestCase): parser = GraphQLParser() def test_shorthand(self): self.assertEqual( self.parser.parse('{ me { name } }'), Document(definitions=[ Query(selections=[ Field(selections=[Field(name='name')], name='me') ]) ])) self.assertEqual( self.parser.parse(""" { user(id: 4) { id name profilePic avatar: profilePic(width: 30, height: 30) } } """), Document(definitions=[ Query(selections=[ Field(selections=[ Field(name='id'), Field(name='name'), Field(name='profilePic'), Field(alias='avatar', name='profilePic', arguments=[ Argument(name='width', value=30), Argument(name='height', value=30) ]) ], name='user', arguments=[Argument(name='id', value=4)]) ]) ])) def test_mutation_shorthand(self): self.assertEqual( self.parser.parse(""" mutation { likeStory(storyID: 12345) { story { likeCount } } } """), Document(definitions=[ Mutation(selections=[ Field(selections=[ Field(name='story', selections=[Field(name='likeCount')]), ], name='likeStory', arguments=[Argument(name='storyID', value=12345)]) ]) ])) def test_with_fragments(self): self.assertEqual( self.parser.parse(""" query withNestedFragments { user(id: 4) { friends(first: 10) { ...friendFields } mutualFriends(first: 10) { ...friendFields } } } fragment friendFields on User { id name ...standardProfilePic } fragment standardProfilePic on User { profilePic(size: "small") } """), Document(definitions=[ Query( name='withNestedFragments', selections=[ Field(selections=[ Field(selections=[ FragmentSpread(name='friendFields') ], name='friends', arguments=[Argument(name='first', value=10) ]), Field(selections=[ FragmentSpread(name='friendFields') ], name='mutualFriends', arguments=[Argument(name='first', value=10)]) ], name='user', arguments=[Argument(name='id', value=4)]) ]), FragmentDefinition(type_condition=NamedType(name='User'), name='friendFields', selections=[ Field(name='id'), Field(name='name'), FragmentSpread( name='standardProfilePic') ]), FragmentDefinition( type_condition=NamedType(name='User'), name='standardProfilePic', selections=[ Field(name='profilePic', arguments=[Argument(name='size', value='small')]) ]) ])) def test_shorthand_query_with_fragments(self): self.assertEqual( self.parser.parse(""" { hero { name ...DroidFields } } fragment DroidFields on Droid { primaryFunction } """), Document(definitions=[ Query(selections=[ Field(name='hero', selections=[ Field(name='name'), FragmentSpread(name='DroidFields'), ]), ]), FragmentDefinition(type_condition=NamedType(name='Droid'), name='DroidFields', selections=[Field(name='primaryFunction')]), ])) def test_shorthand_vs_query(self): self.assertEqual( self.parser.parse(""" query { hero { name } } """), self.parser.parse(""" { hero { name } } """), ) def test_variables(self): self.assertEqual( self.parser.parse(""" query withVariable($userId: Int = 0, $userName: String) { user(id: $userId, name: $userName) { nick } } """), Document(definitions=[ Query(name='withVariable', variable_definitions=[ VariableDefinition(name='userId', type=NamedType(name='Int'), default_value=0), VariableDefinition(name='userName', type=NamedType(name='String')) ], selections=[ Field(selections=[Field(name='nick')], name='user', arguments=[ Argument( name='id', value=Variable(name='userId'), ), Argument(name='name', value=Variable(name='userName')) ]) ]) ])) def test_arguments(self): self.assertEqual( self.parser.parse(""" { episodes (number: null, isPrequel: false) { id } } """), Document(definitions=[ Query(selections=[ Field(selections=[Field(name='id')], name='episodes', arguments=[ Argument(name='number', value=None), Argument(name='isPrequel', value=False) ]) ]) ])) def test_with_subscription(self): self.assertEqual( self.parser.parse(""" subscription onSomething($deviceId: ID!) { onSomething(deviceId: $deviceId,) { deviceId deviceType datapoints { id } } } """), Document(definitions=[ Subscription(name="onSomething", selections=[ Field(name="onSomething", arguments=[ Argument(name="deviceId", value=Variable( name="deviceId")) ], selections=[ Field(name="deviceId"), Field(name="deviceType"), Field(name="datapoints", selections=[Field(name="id")]) ]) ], variable_definitions=[ VariableDefinition(name="deviceId", type=NonNullType( type=NamedType( name="ID"))) ]) ]))
def __init__(self, **kwargs): self._models = {} self._parser = GraphQLParser()
class GraphQLParseTest(TestCase): parser = GraphQLParser() def test_shorthand(self): self.assertEqual( self.parser.parse('{ me { name } }'), Document(definitions=[ Query(selections=[ Field(selections=[Field(name='name')], name='me') ]) ])) self.assertEqual( self.parser.parse(""" { user(id: 4) { id name profilePic avatar: profilePic(width: 30, height: 30) } } """), Document(definitions=[ Query(selections=[ Field(selections=[ Field(name='id'), Field(name='name'), Field(name='profilePic'), Field(alias='avatar', name='profilePic', arguments=[ Argument(name='width', value=30), Argument(name='height', value=30) ]) ], name='user', arguments=[Argument(name='id', value=4)]) ]) ])) def test_with_fragments(self): self.assertEqual( self.parser.parse(""" query withNestedFragments { user(id: 4) { friends(first: 10) { ...friendFields } mutualFriends(first: 10) { ...friendFields } } } fragment friendFields on User { id name ...standardProfilePic } fragment standardProfilePic on User { profilePic(size: "small") } """), Document(definitions=[ Query( name='withNestedFragments', selections=[ Field(selections=[ Field(selections=[ FragmentSpread(name='friendFields') ], name='friends', arguments=[Argument(name='first', value=10) ]), Field(selections=[ FragmentSpread(name='friendFields') ], name='mutualFriends', arguments=[Argument(name='first', value=10)]) ], name='user', arguments=[Argument(name='id', value=4)]) ]), FragmentDefinition(type_condition=NamedType(name='User'), name='friendFields', selections=[ Field(name='id'), Field(name='name'), FragmentSpread( name='standardProfilePic') ]), FragmentDefinition( type_condition=NamedType(name='User'), name='standardProfilePic', selections=[ Field(name='profilePic', arguments=[Argument(name='size', value='small')]) ]) ])) def test_shorthand_query_with_fragments(self): self.assertEqual( self.parser.parse(""" { hero { name ...DroidFields } } fragment DroidFields on Droid { primaryFunction } """), Document(definitions=[ Query(selections=[ Field(name='hero', selections=[ Field(name='name'), FragmentSpread(name='DroidFields'), ]), ]), FragmentDefinition(type_condition=NamedType(name='Droid'), name='DroidFields', selections=[Field(name='primaryFunction')]), ])) def test_variables(self): self.assertEqual( self.parser.parse(""" query withVariable($userId: Int = 0, $userName: String) { user(id: $userId, name: $userName) { nick } } """), Document(definitions=[ Query(name='withVariable', variable_definitions=[ VariableDefinition(name='userId', type=NamedType(name='Int'), default_value=0), VariableDefinition(name='userName', type=NamedType(name='String')) ], selections=[ Field(selections=[Field(name='nick')], name='user', arguments=[ Argument( name='id', value=Variable(name='userId'), ), Argument(name='name', value=Variable(name='userName')) ]) ]) ]))
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]