def test_does_not_consider_fragment_names(): body = '''schema { query: Foo } fragment Foo on Type { field } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified query type "Foo" not found in document' in str(excinfo.value)
def test_requires_a_schema_definition(): body = """ type Hello { bar: Bar } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert "Must provide a schema definition." == str(excinfo.value)
def test_requires_a_schema_definition(): body = ''' type Hello { bar: Bar } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Must provide a schema definition.' == str(excinfo.value)
def test_does_not_consider_fragment_names(): body = """schema { query: Foo } fragment Foo on Type { field } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified query type "Foo" not found in document' in str( excinfo.value)
def test_unknown_type_in_union_list(): body = ''' schema { query: Hello } union TestUnion = Bar type Hello { testUnion: TestUnion } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Type "Bar" not found in document' in str(excinfo.value)
def test_unknown_type_in_union_list(): body = """ schema { query: Hello } union TestUnion = Bar type Hello { testUnion: TestUnion } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Type "Bar" not found in document' in str(excinfo.value)
def test_unknown_type_referenced(): body = ''' schema { query: Hello } type Hello { bar: Bar } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Type "Bar" not found in document' in str(excinfo.value)
def test_does_not_consider_query_names(): body = ''' schema { query: Foo } type Hello { str: String } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified query type "Foo" not found in document' in str(excinfo.value)
def test_unknown_type_referenced(): body = """ schema { query: Hello } type Hello { bar: Bar } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Type "Bar" not found in document' in str(excinfo.value)
def test_requires_a_query_type(): body = ''' schema { mutation: Hello } type Hello { bar: Bar } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Must provide schema definition with query type.' == str(excinfo.value)
def test_unknown_query_type(): body = ''' schema { query: Wat } type Hello { str: String } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified query type "Wat" not found in document' in str(excinfo.value)
def test_incorrect_directive_locations_in_schema(self): """Ensure appropriate errors are raised if nonexistent directive is provided.""" schema_with_extra_directive = ''' schema { query: RootSchemaQuery } directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on FIELD directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD directive @nonexistent on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' parsed_schema_with_extra_directive = build_ast_schema(parse(schema_with_extra_directive)) query = '''{ Animal { name @output(out_name: "animal_name") } }''' with self.assertRaises(GraphQLValidationError): graphql_to_ir(parsed_schema_with_extra_directive, query)
def cycle_output(body): """This function does a full cycle of going from a string with the contents of the DSL, parsed in a schema AST, materializing that schema AST into an in-memory GraphQLSchema, and then finally printing that GraphQL into the DSL""" ast = parse(body) schema = build_ast_schema(ast) return "\n" + print_schema(schema)
def test_unknown_mutation_type(): body = """ schema { query: Hello mutation: Wat } type Hello { str: String } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified mutation type "Wat" not found in document' in str(excinfo.value)
def test_missing_directives_in_schema(self): """Ensure that validators properly identifiy missing directives in the schema. The schema should contain all directives that are supported by the graphql compiler, even if they might not be used in the query. Hence we raise an error when the following directive is not declared in the schema: directive @recurse(depth: Int!) on FIELD. """ incomplete_schema_text = ''' schema { query: RootSchemaQuery } directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on FIELD directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' incomplete_schema = build_ast_schema(parse(incomplete_schema_text)) query = '''{ Animal { name @output(out_name: "animal_name") } }''' with self.assertRaises(GraphQLValidationError): graphql_to_ir(incomplete_schema, query)
def parse_schema_to_gql(schema_str=None, directory=None): gqls = Source(schema_str) gqld = parse(gqls) schema = build_ast_schema(gqld) q_tpl = '{} {}{} {{\n{}\n}}' if directory: path = pathlib.Path(directory) if not path.is_absolute(): path = pathlib.Path(path.cwd(), path) pathlib.Path(path, 'query').mkdir(parents=True, exist_ok=True) pathlib.Path(path, 'mutation').mkdir(parents=True, exist_ok=True) pathlib.Path(path, 'subscription').mkdir(parents=True, exist_ok=True) print(path) if schema.get_query_type(): for field in schema.get_query_type().fields: qstr, arg_dict = gen_query(schema, field, 'Query') vars_types_str = vars_to_types_str(arg_dict) if vars_types_str: vars_types_str = '({})'.format(vars_types_str) q_str = q_tpl.format('query', field, vars_types_str, qstr) if directory: file_path = pathlib.Path(path, 'query', "{}.graphql".format(field)) print(file_path) with open(file_path, 'w+') as f: f.write(q_str) else: print(q_str)
def test_incorrect_directive_locations_in_schema(self): """Ensure appropriate errors are raised if nonexistent directive is provided.""" schema_with_extra_directive = ''' schema { query: RootSchemaQuery } directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on FIELD directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD directive @nonexistent on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' parsed_schema_with_extra_directive = build_ast_schema( parse(schema_with_extra_directive)) query = '''{ Animal { name @output(out_name: "animal_name") } }''' with self.assertRaises(GraphQLValidationError): graphql_to_ir(parsed_schema_with_extra_directive, query)
def test_does_not_consider_query_names(): body = """ schema { query: Foo } type Hello { str: String } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified query type "Foo" not found in document' in str( excinfo.value)
def get_schema(): """Get a schema object for testing.""" ast = parse(SCHEMA_TEXT) schema = build_ast_schema(ast) amend_custom_scalar_types(schema, CUSTOM_SCALAR_TYPES) # Mutates the schema. return schema
def _load_schema(): """Load the GraphQL schema from the schema file.""" with open(path.join(path.dirname(__file__), 'game_of_graphql.graphql')) as f: schema_text = ''.join(f.readlines()) ast = parse(schema_text) return build_ast_schema(ast)
def test_directives_with_incorrect_arguments(self): """Ensure that proper errors are raised if directives are provided with incorrect args.""" # Change @filter arg from String! to Int! schema_with_incorrect_args = ''' schema { query: RootSchemaQuery } directive @filter(op_name: Int!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on INLINE_FRAGMENT directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' parsed_incorrect_schema = build_ast_schema( parse(schema_with_incorrect_args)) query = '''{ Animal { name @output(out_name: "animal_name") } }''' with self.assertRaises(GraphQLValidationError): graphql_to_ir(parsed_incorrect_schema, query)
def test_directives_with_incorrect_arguments(self): """Ensure that proper errors are raised if directives are provided with incorrect args.""" # Change @filter arg from String! to Int! schema_with_incorrect_args = ''' schema { query: RootSchemaQuery } directive @filter(op_name: Int!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on INLINE_FRAGMENT directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' parsed_incorrect_schema = build_ast_schema(parse(schema_with_incorrect_args)) query = '''{ Animal { name @output(out_name: "animal_name") } }''' with self.assertRaises(GraphQLValidationError): graphql_to_ir(parsed_incorrect_schema, query)
def test_unknown_query_type(): body = """ schema { query: Wat } type Hello { str: String } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified query type "Wat" not found in document' in str( excinfo.value)
def test_requires_a_query_type(): body = """ schema { mutation: Hello } type Hello { bar: Bar } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert "Must provide schema definition with query type." == str( excinfo.value)
def build_schema_from_type_definitions(type_defs: str) -> GraphQLSchema: document = parse(type_defs) if not document_has_schema(document): schema_definition = build_default_schema(document) document.definitions.append(schema_definition) return build_ast_schema(document)
def test_unknown_mutation_type(): body = ''' schema { query: Hello mutation: Wat } type Hello { str: String } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified mutation type "Wat" not found in document' in str( excinfo.value)
def test_allows_only_a_single_schema_definition(): body = """ schema { query: Hello } schema { query: Hello } type Hello { bar: Bar } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert "Must provide only one schema definition." == str(excinfo.value)
def test_allows_only_a_single_schema_definition(): body = ''' schema { query: Hello } schema { query: Hello } type Hello { bar: Bar } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Must provide only one schema definition.' == str(excinfo.value)
def test_allows_only_a_single_query_type(): body = """ schema { query: Hello query: Yellow } type Hello { bar: Bar } type Yellow { isColor: Boolean } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert "Must provide only one query type in schema." == str(excinfo.value)
def test_allows_only_a_single_query_type(): body = ''' schema { query: Hello query: Yellow } type Hello { bar: Bar } type Yellow { isColor: Boolean } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Must provide only one query type in schema.' == str(excinfo.value)
def test_allows_only_a_single_subscription_type(): body = """ schema { query: Hello subscription: Hello subscription: Yellow } type Hello { bar: Bar } type Yellow { isColor: Boolean } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert "Must provide only one subscription type in schema." == str(excinfo.value)
def test_unknown_subscription_type(): body = ''' schema { query: Hello mutation: Wat subscription: Awesome } type Hello { str: String } type Wat { str: String } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified subscription type "Awesome" not found in document' in str(excinfo.value)
def get_schema_with_macros(macro_registry): """Get a new GraphQLSchema with fields where macro edges can be used. Preconditions: 1. No macro in the registry has the same name as a field on the vertex where it applies. 2. Members of a union type do not have outgoing macros with the same name. An easy way to satisfy the preconditions is to create the macro_registry using create_macro_registry, and only update it with register_macro_edge, which does all the necessary validation. Postconditions: 1. Every GraphQLQuery that uses macros from this registry appropriately should successfully type-check against the schema generated from this function. 2. A GraphQLQuery that uses macros not present in the registry, or uses valid macros but on types they are not defined at should fail schema validation with the schema generated from this function. 3. This function is total -- A valid macro registry should not fail to create a GraphQL schema with macros. Args: macro_registry: MacroRegistry object containing a schema and macro descriptors we want to add to the schema. Returns: GraphQLSchema with additional fields where macroe edges can be used. """ # The easiest way to manipulate the schema is through its AST. The easiest # way to get an AST is to print it and parse it. schema_ast = parse(print_schema(macro_registry.schema_without_macros)) definitions_by_name = {} for definition in schema_ast.definitions: if isinstance(definition, (ObjectTypeDefinition, InterfaceTypeDefinition)): definitions_by_name[definition.name.value] = definition for class_name, macros_for_class in six.iteritems( macro_registry.macro_edges_at_class): for macro_edge_name, macro_edge_descriptor in six.iteritems( macros_for_class): list_type_at_target = ListType( NamedType(Name(macro_edge_descriptor.target_class_name))) arguments = [] directives = [Directive(Name(MacroEdgeDirective.name))] definitions_by_name[class_name].fields.append( FieldDefinition(Name(macro_edge_name), arguments, list_type_at_target, directives=directives)) return build_ast_schema(schema_ast)
def test_allows_only_a_single_subscription_type(): body = ''' schema { query: Hello subscription: Hello subscription: Yellow } type Hello { bar: Bar } type Yellow { isColor: Boolean } ''' doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Must provide only one subscription type in schema.' == str( excinfo.value)
def test_unknown_subscription_type(): body = """ schema { query: Hello mutation: Wat subscription: Awesome } type Hello { str: String } type Wat { str: String } """ doc = parse(body) with raises(Exception) as excinfo: build_ast_schema(doc) assert 'Specified subscription type "Awesome" not found in document' in str( excinfo.value)
def test_maintains_skip_and_include_directives(): body = """ schema { query: Hello } type Hello { str: String } """ schema = build_ast_schema(parse(body)) assert len(schema.get_directives()) == 3 assert schema.get_directive("skip") == GraphQLSkipDirective assert schema.get_directive("include") == GraphQLIncludeDirective assert schema.get_directive("deprecated") == GraphQLDeprecatedDirective
def test_maintains_skip_and_include_directives(): body = ''' schema { query: Hello } type Hello { str: String } ''' schema = build_ast_schema(parse(body)) assert len(schema.get_directives()) == 3 assert schema.get_directive('skip') == GraphQLSkipDirective assert schema.get_directive('include') == GraphQLIncludeDirective assert schema.get_directive('deprecated') == GraphQLDeprecatedDirective
def test_input_types_are_read(): schema = build_ast_schema(parse(""" schema { query: Query } type Query { field(input: Input): Int } input Input { id: Int } """)) input_type = schema.get_type("Input") assert input_type.fields["id"].type == GraphQLInt
def test_input_types_can_be_recursive(): schema = build_ast_schema(parse(""" schema { query: Query } type Query { field(input: Input): Int } input Input { id: Input } """)) input_type = schema.get_type("Input") assert input_type.fields["id"].type == input_type
def test_input_types_can_be_recursive(): schema = build_ast_schema( parse(""" schema { query: Query } type Query { field(input: Input): Int } input Input { id: Input } """)) input_type = schema.get_type("Input") assert input_type.fields["id"].type == input_type
def test_input_types_are_read(): schema = build_ast_schema( parse(""" schema { query: Query } type Query { field(input: Input): Int } input Input { id: Int } """)) input_type = schema.get_type("Input") assert input_type.fields["id"].type == GraphQLInt
def test_overriding_include_directive_excludes_built_in_one(): body = ''' schema { query: Hello } directive @include on FIELD type Hello { str: String } ''' schema = build_ast_schema(parse(body)) assert len(schema.get_directives()) == 3 assert schema.get_directive('skip') == GraphQLSkipDirective assert schema.get_directive('deprecated') == GraphQLDeprecatedDirective assert schema.get_directive('include') != GraphQLIncludeDirective assert schema.get_directive('include') is not None
def test_overriding_skip_directive_excludes_built_in_one(): body = """ schema { query: Hello } directive @skip on FIELD type Hello { str: String } """ schema = build_ast_schema(parse(body)) assert len(schema.get_directives()) == 3 assert schema.get_directive("skip") != GraphQLSkipDirective assert schema.get_directive("skip") is not None assert schema.get_directive("include") == GraphQLIncludeDirective assert schema.get_directive("deprecated") == GraphQLDeprecatedDirective
def test_overriding_directives_excludes_specified(): body = """ schema { query: Hello } directive @skip on FIELD directive @include on FIELD directive @deprecated on FIELD_DEFINITION type Hello { str: String } """ schema = build_ast_schema(parse(body)) assert len(schema.get_directives()) == 3 assert schema.get_directive("skip") != GraphQLSkipDirective assert schema.get_directive("skip") is not None assert schema.get_directive("include") != GraphQLIncludeDirective assert schema.get_directive("include") is not None assert schema.get_directive("deprecated") != GraphQLDeprecatedDirective assert schema.get_directive("deprecated") is not None
def cycle_output(body): ast = parse(body) schema = build_ast_schema(ast) return '\n' + print_schema(schema)
def test_directives_on_wrong_fields(self): """Ensure appropriate errors are raised if any directives are on the wrong location.""" # Change @tag from FIELD to INLINE_FRAGMENT schema_with_wrong_directive_on_inline_fragment = ''' schema { query: RootSchemaQuery } directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on INLINE_FRAGMENT directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' # Remove INLINE_FRAGMENT from @filter schema_with_directive_missing_location = ''' schema { query: RootSchemaQuery } directive @filter(op_name: String!, value: [String!]!) on FIELD directive @tag(tag_name: String!) on FIELD directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' # Change @output_source from FIELD to FIELD | INLINE_FRAGMENT schema_with_directive_missing_location = ''' schema { query: RootSchemaQuery } directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on FIELD directive @output(out_name: String!) on FIELD directive @output_source on FIELD | INLINE_FRAGMENT directive @optional on FIELD directive @fold on FIELD directive @recurse(depth: Int!) on FIELD type Animal { name: String } type RootSchemaQuery { Animal: Animal } ''' incorrect_schemas = [ schema_with_wrong_directive_on_inline_fragment, schema_with_directive_missing_location, schema_with_directive_missing_location, ] query = '''{ Animal { name @output(out_name: "animal_name") } }''' for schema in incorrect_schemas: parsed_incorrect_schema = build_ast_schema(parse(schema)) with self.assertRaises(GraphQLValidationError): graphql_to_ir(parsed_incorrect_schema, query)
def get_schema(): """Get a schema object for testing.""" # This schema isn't meant to be a paragon of good schema design. # Instead, it aims to capture as many real-world edge cases as possible, # without requiring a massive number of types and interfaces. schema_text = ''' schema { query: RootSchemaQuery } directive @recurse(depth: Int!) on FIELD directive @filter(op_name: String!, value: [String!]!) on FIELD | INLINE_FRAGMENT directive @tag(tag_name: String!) on FIELD directive @output(out_name: String!) on FIELD directive @output_source on FIELD directive @optional on FIELD directive @fold on FIELD scalar Decimal scalar DateTime scalar Date interface Entity { name: String alias: [String] description: String uuid: ID in_Entity_Related: [Entity] out_Entity_Related: [Entity] } type Animal implements Entity { name: String color: String description: String alias: [String] birthday: Date net_worth: Decimal uuid: ID out_Animal_ParentOf: [Animal] in_Animal_ParentOf: [Animal] out_Animal_OfSpecies: [Species] out_Animal_FedAt: [Event] out_Animal_BornAt: [BirthEvent] out_Animal_ImportantEvent: [EventOrBirthEvent] in_Entity_Related: [Entity] out_Entity_Related: [Entity] out_Animal_LivesIn: [Location] } type Location { name: String uuid: ID in_Animal_LivesIn: [Animal] } type Species implements Entity { name: String description: String alias: [String] limbs: Int uuid: ID out_Species_Eats: [FoodOrSpecies] in_Species_Eats: [Species] in_Animal_OfSpecies: [Animal] in_Entity_Related: [Entity] out_Entity_Related: [Entity] } type Food implements Entity { name: String origin: String description: String alias: [String] uuid: ID in_Species_Eats: [Species] in_Entity_Related: [Entity] out_Entity_Related: [Entity] } union FoodOrSpecies = Food | Species type Event implements Entity { name: String alias: [String] description: String uuid: ID event_date: DateTime in_Animal_FedAt: [Animal] in_Animal_ImportantEvent: [Animal] in_Entity_Related: [Entity] out_Entity_Related: [Entity] } # Assume that in the database, the below type is actually a subclass of Event. type BirthEvent implements Entity { name: String alias: [String] description: String uuid: ID event_date: DateTime in_Animal_FedAt: [Animal] in_Animal_BornAt: [Animal] in_Animal_ImportantEvent: [Animal] in_Entity_Related: [Entity] out_Entity_Related: [Entity] } # Because of the above, the base type for this union is Event. union EventOrBirthEvent = Event | BirthEvent type RootSchemaQuery { Animal: Animal BirthEvent: BirthEvent Entity: Entity Event: Event Food: Food Species: Species Location: Location } ''' ast = parse(schema_text) return build_ast_schema(ast)
def build_schema(type_defs: str) -> GraphQLSchema: ast_schema = parse(type_defs) return build_ast_schema(ast_schema)
def get_schema(): """Get a schema object for testing.""" ast = parse(SCHEMA_TEXT) schema = build_ast_schema(ast) return schema