def builds_a_schema_with_an_enum(): food_enum = GraphQLEnumType( "Food", { "VEGETABLES": GraphQLEnumValue( 1, description="Foods that are vegetables." ), "FRUITS": GraphQLEnumValue(2, description="Foods that are fruits."), "OILS": GraphQLEnumValue(3, description="Foods that are oils."), "DAIRY": GraphQLEnumValue(4, description="Foods that are dairy."), "MEAT": GraphQLEnumValue(5, description="Foods that are meat."), }, description="Varieties of food stuffs", ) schema = GraphQLSchema( GraphQLObjectType( "EnumFields", { "food": GraphQLField( food_enum, args={ "kind": GraphQLArgument( food_enum, description="what kind of food?" ) }, description="Repeats the arg you give it", ) }, ) ) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) second_introspection = introspection_from_schema(client_schema) hack_to_remove_standard_types(second_introspection) hack_to_remove_standard_types(introspection) assert second_introspection == introspection client_food_enum = client_schema.get_type("Food") # It's also an Enum type on the client. assert is_enum_type(client_food_enum) values = client_food_enum.values descriptions = {name: value.description for name, value in values.items()} assert descriptions == { "VEGETABLES": "Foods that are vegetables.", "FRUITS": "Foods that are fruits.", "OILS": "Foods that are oils.", "DAIRY": "Foods that are dairy.", "MEAT": "Foods that are meat.", } values = values.values() assert all(value.value is None for value in values) assert all(value.is_deprecated is False for value in values) assert all(value.deprecation_reason is None for value in values) assert all(value.ast_node is None for value in values)
def check_schema(server_schema): """Test that the client side introspection gives the same result. Given a server's schema, a client may query that server with introspection, and use the result to produce a client-side representation of the schema by using `build_client_schema`. If the client then runs the introspection query against the client-side schema, it should get a result identical to what was returned by the server. """ initial_introspection = introspection_from_schema(server_schema) client_schema = build_client_schema(initial_introspection) second_introspection = introspection_from_schema(client_schema) assert initial_introspection == second_introspection
def uses_built_in_scalars_when_possible(): custom_scalar = GraphQLScalarType('CustomScalar', serialize=lambda: None) schema = GraphQLSchema( GraphQLObjectType( 'Scalars', { 'int': GraphQLField(GraphQLInt), 'float': GraphQLField(GraphQLFloat), 'string': GraphQLField(GraphQLString), 'boolean': GraphQLField(GraphQLBoolean), 'id': GraphQLField(GraphQLID), 'custom': GraphQLField(custom_scalar) })) check_schema(schema) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) # Built-ins are used assert client_schema.get_type('Int') is GraphQLInt assert client_schema.get_type('Float') is GraphQLFloat assert client_schema.get_type('String') is GraphQLString assert client_schema.get_type('Boolean') is GraphQLBoolean assert client_schema.get_type('ID') is GraphQLID # Custom are built assert client_schema.get_type('CustomScalar') is not custom_scalar
def fails_on_very_deep_lists_more_than_7_levels(): schema = GraphQLSchema( GraphQLObjectType( 'Query', { 'foo': GraphQLField( GraphQLList( GraphQLList( GraphQLList( GraphQLList( GraphQLList( GraphQLList( GraphQLList( GraphQLList( GraphQLString))))))))) })) introspection = introspection_from_schema(schema) with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == ( 'Query fields cannot be resolved:' ' Decorated type deeper than introspection query.')
def can_use_client_schema_for_limited_execution(): custom_scalar = GraphQLScalarType("CustomScalar", serialize=lambda: None) schema = GraphQLSchema( GraphQLObjectType( "Query", { "foo": GraphQLField( GraphQLString, args={ "custom1": GraphQLArgument(custom_scalar), "custom2": GraphQLArgument(custom_scalar), }, ) }, )) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) class Data: foo = "bar" unused = "value" result = graphql_sync( client_schema, "query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }", Data(), variable_values={"v": "baz"}, ) assert result.data == {"foo": "bar"}
def uses_built_in_scalars_when_possible(): sdl = dedent(""" scalar CustomScalar type Query { int: Int float: Float string: String boolean: Boolean id: ID custom: CustomScalar } """) assert cycle_introspection(sdl) == sdl schema = build_schema(sdl) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) # Built-ins are used assert client_schema.get_type("Int") is GraphQLInt assert client_schema.get_type("Float") is GraphQLFloat assert client_schema.get_type("String") is GraphQLString assert client_schema.get_type("Boolean") is GraphQLBoolean assert client_schema.get_type("ID") is GraphQLID # Custom are built custom_scalar = schema.get_type("CustomScalar") assert client_schema.get_type("CustomScalar") is not custom_scalar
def can_use_client_schema_for_limited_execution(): schema = build_schema( """ scalar CustomScalar type Query { foo(custom1: CustomScalar, custom2: CustomScalar): String } """ ) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) class Data: foo = "bar" unused = "value" result = graphql_sync( client_schema, "query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }", root_value=Data(), variable_values={"v": "baz"}, ) assert result.data == {"foo": "bar"}
def can_build_invalid_schema(): schema = build_schema("type Query", assume_valid=True) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection, assume_valid=True) assert client_schema.to_kwargs()["assume_valid"] is True
def uses_built_in_scalars_when_possible(): custom_scalar = GraphQLScalarType("CustomScalar", serialize=lambda: None) schema = GraphQLSchema( GraphQLObjectType( "Scalars", { "int": GraphQLField(GraphQLInt), "float": GraphQLField(GraphQLFloat), "string": GraphQLField(GraphQLString), "boolean": GraphQLField(GraphQLBoolean), "id": GraphQLField(GraphQLID), "custom": GraphQLField(custom_scalar), }, )) check_schema(schema) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) # Built-ins are used assert client_schema.get_type("Int") is GraphQLInt assert client_schema.get_type("Float") is GraphQLFloat assert client_schema.get_type("String") is GraphQLString assert client_schema.get_type("Boolean") is GraphQLBoolean assert client_schema.get_type("ID") is GraphQLID # Custom are built assert client_schema.get_type("CustomScalar") is not custom_scalar
def recursive_interfaces(): sdl = """ type Query { foo: Foo } type Foo { foo: String } """ schema = build_schema(sdl, assume_valid=True) introspection = introspection_from_schema(schema) foo_type_introspection = introspection["__schema"]["types"][1] assert foo_type_introspection["name"] == "Foo" assert foo_type_introspection["interfaces"] == [] # we need to patch here since invalid interfaces cannot be built with Python foo_type_introspection["interfaces"] = [ {"kind": "OBJECT", "name": "Foo", "ofType": None} ] with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == ( "Foo interfaces cannot be resolved: " "Expected Foo to be a GraphQL Interface type." )
def can_use_client_schema_for_limited_execution(): custom_scalar = GraphQLScalarType('CustomScalar', serialize=lambda: None) schema = GraphQLSchema( GraphQLObjectType( 'Query', { 'foo': GraphQLField(GraphQLString, args={ 'custom1': GraphQLArgument(custom_scalar), 'custom2': GraphQLArgument(custom_scalar) }) })) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) class Data: foo = 'bar' unused = 'value' result = graphql_sync(client_schema, 'query Limited($v: CustomScalar) {' ' foo(custom1: 123, custom2: $v) }', Data(), variable_values={'v': 'baz'}) assert result.data == {'foo': 'bar'}
def fails_on_a_very_deep_non_null_more_than_7_levels(): schema = GraphQLSchema( GraphQLObjectType( "Query", { "foo": GraphQLField( GraphQLList( GraphQLNonNull( GraphQLList( GraphQLNonNull( GraphQLList( GraphQLNonNull( GraphQLList( GraphQLNonNull( GraphQLString))))))))) }, )) introspection = introspection_from_schema(schema) with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == ( "Query fields cannot be resolved:" " Decorated type deeper than introspection query.")
def recursive_union(): sdl = """ type Query { foo: Foo } union Foo """ schema = build_schema(sdl, assume_valid=True) introspection = introspection_from_schema(schema) foo_type_introspection = introspection["__schema"]["types"][1] assert foo_type_introspection["name"] == "Foo" assert foo_type_introspection["kind"] == "UNION" assert foo_type_introspection["possibleTypes"] == [] # we need to patch here since invalid unions cannot be built with Python foo_type_introspection["possibleTypes"] = [ {"kind": "UNION", "name": "Foo", "ofType": None} ] with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == ( "Foo types cannot be resolved:" " Expected Foo to be a GraphQL Object type." )
def cycle_introspection(sdl_string): """Test that the client side introspection gives the same result. This function does a full cycle of going from a string with the contents of the SDL, build in-memory GraphQLSchema from it, produce a client-side representation of the schema by using "build_client_schema" and then finally printing that that schema into the SDL. """ server_schema = build_schema(sdl_string) initial_introspection = introspection_from_schema(server_schema) client_schema = build_client_schema(initial_introspection) # If the client then runs the introspection query against the client-side schema, # it should get a result identical to what was returned by the server second_introspection = introspection_from_schema(client_schema) assert initial_introspection == second_introspection return print_schema(client_schema)
def throws_when_type_reference_is_missing_name(): introspection = introspection_from_schema(dummy_schema) assert introspection["__schema"]["queryType"]["name"] == "Query" del introspection["__schema"]["queryType"]["name"] with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == "Unknown type reference: {}"
def builds_a_schema_with_an_enum(): food_enum = GraphQLEnumType('Food', { 'VEGETABLES': GraphQLEnumValue(1, description='Foods that are vegetables.'), 'FRUITS': GraphQLEnumValue(2, description='Foods that are fruits.'), 'OILS': GraphQLEnumValue(3, description='Foods that are oils.'), 'DAIRY': GraphQLEnumValue(4, description='Foods that are dairy.'), 'MEAT': GraphQLEnumValue(5, description='Foods that are meat.') }, description='Varieties of food stuffs') schema = GraphQLSchema( GraphQLObjectType( 'EnumFields', { 'food': GraphQLField(food_enum, args={ 'kind': GraphQLArgument( food_enum, description='what kind of food?') }, description='Repeats the arg you give it') })) check_schema(schema) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) client_food_enum = client_schema.get_type('Food') # It's also an Enum type on the client. assert isinstance(client_food_enum, GraphQLEnumType) values = client_food_enum.values descriptions = { name: value.description for name, value in values.items() } assert descriptions == { 'VEGETABLES': 'Foods that are vegetables.', 'FRUITS': 'Foods that are fruits.', 'OILS': 'Foods that are oils.', 'DAIRY': 'Foods that are dairy.', 'MEAT': 'Foods that are meat.' } values = values.values() assert all(value.value is None for value in values) assert all(value.is_deprecated is False for value in values) assert all(value.deprecation_reason is None for value in values) assert all(value.ast_node is None for value in values)
def cycle_introspection(sdl_string): """Test that the client side introspection gives the same result. This function does a full cycle of going from a string with the contents of the SDL, build in-memory GraphQLSchema from it, produce a client-side representation of the schema by using "build_client_schema" and then return that schema printed as SDL. """ options = dict(specified_by_url=True, directive_is_repeatable=True) server_schema = build_schema(sdl_string) initial_introspection = introspection_from_schema(server_schema, **options) client_schema = build_client_schema(initial_introspection) # If the client then runs the introspection query against the client-side schema, # it should get a result identical to what was returned by the server second_introspection = introspection_from_schema(client_schema, **options) # If the client then runs the introspection query against the client-side # schema, it should get a result identical to what was returned by the server. assert initial_introspection == second_introspection return print_schema(client_schema)
def legacy_support_for_interfaces_with_null_as_interfaces_field(): introspection = introspection_from_schema(dummy_schema) some_interface_introspection = next( type_ for type_ in introspection["__schema"]["types"] if type_["name"] == "SomeInterface") assert some_interface_introspection["interfaces"] == [] some_interface_introspection["interfaces"] = None client_schema = build_client_schema(introspection) assert print_schema(client_schema) == print_schema(dummy_schema)
def include_standard_type_only_if_it_is_used(): schema = build_schema(""" type Query { foo: String } """) introspection = introspection_from_schema(schema) client_schema = build_client_schema(introspection) assert client_schema.get_type("Int") is None assert client_schema.get_type("Float") is None assert client_schema.get_type("ID") is None
def converts_a_simple_schema_without_description(): introspection = introspection_from_schema(schema, descriptions=False) assert introspection_to_sdl(introspection) == dedent(""" schema { query: Simple } type Simple { string: String } """)
def throws_when_missing_directive_args(): introspection = introspection_from_schema(dummy_schema) some_directive_introspection = introspection["__schema"]["directives"][0] assert some_directive_introspection["name"] == "SomeDirective" assert some_directive_introspection["args"] == [] del some_directive_introspection["args"] with raises( TypeError, match="^Introspection result missing directive args:" r" {'name': 'SomeDirective', .*}\.$", ): build_client_schema(introspection)
def builds_a_schema_without_the_query_type(): sdl = dedent(""" type Query { foo: String } """) schema = build_schema(sdl) introspection = introspection_from_schema(schema) del introspection["__schema"]["queryType"] client_schema = build_client_schema(introspection) assert client_schema.query_type is None assert print_schema(client_schema) == sdl
def converts_a_simple_schema(): introspection = introspection_from_schema(schema) assert introspection_to_sdl(introspection) == dedent(''' schema { query: Simple } """This is a simple type""" type Simple { """This is a string field""" string: String } ''')
def fails_on_a_very_deep_non_null_more_than_7_levels(): schema = build_schema(""" type Query { foo: [[[[String!]!]!]!] } """) introspection = introspection_from_schema(schema) with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == ( "Query fields cannot be resolved:" " Decorated type deeper than introspection query.")
def throws_when_missing_enum_values(): introspection = introspection_from_schema(dummy_schema) some_enum_introspection = next( type_ for type_ in introspection["__schema"]["types"] if type_["name"] == "SomeEnum") assert some_enum_introspection["enumValues"] del some_enum_introspection["enumValues"] with raises( TypeError, match="^Introspection result missing enumValues:" r" {'kind': 'ENUM', 'name': 'SomeEnum', .*}\.$", ): build_client_schema(introspection)
def throws_when_missing_kind(): introspection = introspection_from_schema(dummy_schema) query_type_introspection = next( type_ for type_ in introspection["__schema"]["types"] if type_["name"] == "Query") assert query_type_introspection["kind"] == "OBJECT" del query_type_introspection["kind"] with raises( TypeError, match=r"^Invalid or incomplete introspection result\." " Ensure that a full introspection query is used" r" in order to build a client schema: {'name': 'Query', .*}\.$", ): build_client_schema(introspection)
def throws_when_missing_directive_locations(): introspection = introspection_from_schema(dummy_schema) some_directive_introspection = introspection["__schema"][ "directives"][0] assert some_directive_introspection["name"] == "SomeDirective" assert some_directive_introspection["locations"] == ["QUERY"] del some_directive_introspection["locations"] # type: ignore with raises( TypeError, match="^Introspection result missing directive locations:" r" {'name': 'SomeDirective', .*}\.$", ): build_client_schema(introspection)
def throws_when_referenced_unknown_type(): introspection = introspection_from_schema(dummy_schema) introspection["__schema"]["types"] = [ type_ for type_ in introspection["__schema"]["types"] if type_["name"] != "Query" ] with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value) == ( "Invalid or incomplete schema, unknown type: Query." " Ensure that a full introspection query is used" " in order to build a client schema.")
def throws_when_missing_input_fields(): introspection = introspection_from_schema(dummy_schema) some_input_object_introspection = next( type_ for type_ in introspection["__schema"]["types"] if type_["name"] == "SomeInputObject") assert some_input_object_introspection["inputFields"] del some_input_object_introspection["inputFields"] with raises( TypeError, match="^Introspection result missing inputFields:" r" {'kind': 'INPUT_OBJECT', 'name': 'SomeInputObject', .*}\.$", ): build_client_schema(introspection)
def throws_when_missing_directive_args(): introspection = introspection_from_schema(dummy_schema) some_directive_introspection = introspection["__schema"]["directives"][0] assert some_directive_introspection["name"] == "SomeDirective" assert some_directive_introspection["args"] == [] del some_directive_introspection["args"] with raises(TypeError) as exc_info: build_client_schema(introspection) assert str(exc_info.value).startswith( "Introspection result missing directive args:" " {'name': 'SomeDirective'," )