async def cook( self, sdl: Union[str, List[str]], error_coercer: Callable[[Exception], dict] = None, custom_default_resolver: Optional[Callable] = None, modules: Optional[Union[str, List[str]]] = None, schema_name: str = "default", ): """ Cook the tartiflette, basicly prepare the engine by binding it to given modules using the schema_name as a key. You wont be able to execute a request if the engine wasn't cooked. Arguments: sdl {Union[str, List[str]]} -- The SDL to work with. Keyword Arguments: schema_name {str} -- The name of the SDL (default: {"default"}) error_coercer {Callable[[Exception, dict], dict]} -- An optional callable in charge of transforming a couple Exception/error into an error dict (default: {default_error_coercer}) custom_default_resolver {Optional[Callable]} -- An optional callable that will replace the tartiflette default_resolver (Will be called like a resolver for each UNDECORATED field) (default: {None}) modules {Optional[Union[str, List[str]]]} -- An optional list of string containing the name of the modules you want the engine to import, usually this modules contains your Resolvers, Directives, Scalar or Subscription code (default: {None}) """ if not modules: modules = [] if isinstance(modules, str): modules = [modules] self._error_coercer = error_coercer_factory(error_coercer or default_error_coercer) self._modules, modules_sdl = await _import_modules( modules, schema_name) SchemaRegistry.register_sdl(schema_name, sdl, modules_sdl) self._schema = SchemaBakery.bake(schema_name, custom_default_resolver)
async def test_schema_validate_object_follow_interfaces( full_sdl, expected_error, clean_registry): _, full_sdl = await _import_builtins([], full_sdl, "A") clean_registry.register_sdl("A", full_sdl) generated_schema = SchemaBakery._preheat("A") try: generated_schema.find_type("Brand").coerce_output = lambda x: x generated_schema.find_type("Brand").coerce_input = lambda x: x generated_schema.find_type("Brand").parse_literal = lambda x: x except KeyError: pass try: generated_schema.find_type("Part").coerce_output = lambda x: x generated_schema.find_type("Part").coerce_input = lambda x: x generated_schema.find_type("Part").parse_literal = lambda x: x except KeyError: pass if expected_error: with pytest.raises(GraphQLSchemaError): await generated_schema.bake() else: await generated_schema.bake()
def test_schema_bake_schema(clean_registry): clean_registry.register_sdl( "a", """ type Query { lol: Int } """, ) assert SchemaBakery.bake("a") is not None
def test_schema_validate_union_is_acceptable(full_sdl, expected_error, expected_value, clean_registry): clean_registry.register_sdl("a", full_sdl) generated_schema = SchemaBakery._preheat("a", None) if expected_error: with pytest.raises(GraphQLSchemaError): generated_schema.validate() else: assert generated_schema.validate() == expected_value
async def test_schema_bake_schema(clean_registry): _, full_sdl = await _import_builtins( [], """ type Query { lol: Int }""", "a", ) clean_registry.register_sdl("a", full_sdl) assert SchemaBakery.bake("a") is not None
async def cook( self, sdl: Union[str, List[str]] = None, error_coercer: Callable[[Exception], dict] = None, custom_default_resolver: Optional[Callable] = None, modules: Optional[Union[str, List[str]]] = None, schema_name: str = None, ): """ Cook the tartiflette, i.e. prepare the engine by binding it to given modules using the schema_name as a key. You won't be able to execute a request if the engine hasn't been cooked. Has no effect if the engine has already been cooked. Keyword Arguments: sdl {Union[str, List[str]]} -- The SDL to work with. schema_name {str} -- The name of the SDL (default: {"default"}) error_coercer {Callable[[Exception, dict], dict]} -- An optional callable in charge of transforming a couple Exception/error into an error dict (default: {default_error_coercer}) custom_default_resolver {Optional[Callable]} -- An optional callable that will replace the tartiflette default_resolver (Will be called like a resolver for each UNDECORATED field) (default: {None}) modules {Optional[Union[str, List[str]]]} -- An optional list of string containing the name of the modules you want the engine to import, usually this modules contains your Resolvers, Directives, Scalar or Subscription code (default: {None}) """ if self._cooked: return if modules is None: modules = self._modules or [] if isinstance(modules, str): modules = [modules] sdl = sdl or self._sdl if not sdl: raise Exception("Please provide a SDL") schema_name = schema_name or self._schema_name or "default" custom_default_resolver = (custom_default_resolver or self._custom_default_resolver) if custom_default_resolver and not iscoroutinefunction( custom_default_resolver): raise Exception(f"Given custom_default_resolver " f"{custom_default_resolver} " f"is not a coroutine function") self._error_coercer = error_coercer_factory(error_coercer or self._error_coercer or default_error_coercer) self._modules, modules_sdl = await _import_modules( modules, schema_name) SchemaRegistry.register_sdl(schema_name, sdl, modules_sdl) self._schema = SchemaBakery.bake(schema_name, custom_default_resolver) self._cooked = True
async def test_schema_validate_union_is_acceptable(full_sdl, expected_error, clean_registry): _, full_sdl = await _import_builtins([], full_sdl, "a") clean_registry.register_sdl("a", full_sdl) generated_schema = SchemaBakery._preheat("a") if expected_error: with pytest.raises(GraphQLSchemaError): await generated_schema.bake() else: await generated_schema.bake()
async def test_schema_validate_non_empty_object(full_sdl, expected_error, expected_value, clean_registry): _, full_sdl = await _import_builtins([], full_sdl, "a") clean_registry.register_sdl("a", full_sdl) generated_schema = SchemaBakery._preheat("a") if expected_error: with pytest.raises(GraphQLSchemaError): generated_schema.validate() else: assert generated_schema.validate() == expected_value
def test_schema_has_type(clean_registry, type_name, expected): clean_registry.register_sdl( "a", """ type User { name: String } type Query { viewer: User } """, ) schema = SchemaBakery.bake("a") assert schema.has_type(type_name) is expected
async def test_schema_has_type(clean_registry, type_name, expected): _, full_sdl = await _import_builtins( [], """ type User { name: String } type Query { viewer: User } """, "a", ) clean_registry.register_sdl("a", full_sdl) schema = SchemaBakery.bake("a") assert schema.has_type(type_name) is expected
def test_schema_bake_schema_exclude_builtins_scalars(clean_registry, exclude_date_scalar): exclude_builtins_scalars = ["Date"] if exclude_date_scalar else None clean_registry.register_sdl( "exclude", """ type Query { lol: Int } """, exclude_builtins_scalars=exclude_builtins_scalars, ) schema = SchemaBakery.bake( "exclude", exclude_builtins_scalars=exclude_builtins_scalars) assert schema is not None assert (len([ scalar for scalar in SchemaRegistry._schemas["exclude"]["scalars"].values() if scalar.name == "Date" ]) == 0 if exclude_date_scalar else 1)
def test_schema_validate_object_follow_interfaces(full_sdl, expected_error, expected_value, clean_registry): clean_registry.register_sdl("A", full_sdl) generated_schema = SchemaBakery._preheat("A", None) try: generated_schema.find_type("Brand").coerce_output = lambda x: x generated_schema.find_type("Brand").coerce_input = lambda x: x except KeyError: pass try: generated_schema.find_type("Part").coerce_output = lambda x: x generated_schema.find_type("Part").coerce_input = lambda x: x except KeyError: pass if expected_error: with pytest.raises(GraphQLSchemaError): generated_schema.validate() else: assert generated_schema.validate() == expected_value
def __init__( self, sdl: Union[str, List[str]], schema_name: str = "default", error_coercer: Callable[[Exception], dict] = default_error_coercer, custom_default_resolver: Optional[Callable] = None, exclude_builtins_scalars: Optional[List[str]] = None, modules: Optional[Union[str, List[str]]] = None, ) -> None: """Create an engine by analyzing the SDL and connecting it with the imported Resolver, Mutation, Subscription, Directive and Scalar linking them through the schema_name. Then using `await an_engine.execute(query)` will resolve your GQL requests. Arguments: sdl {Union[str, List[str]]} -- The SDL to work with. Keyword Arguments: schema_name {str} -- The name of the SDL (default: {"default"}) error_coercer {Callable[[Exception], dict]} -- An optional callable in charge of transforming an Exception into an error dict (default: {default_error_coercer}) custom_default_resolver {Optional[Callable]} -- An optional callable that will replace the tartiflette default_resolver (Will be called like a resolver for each UNDECORATED field) (default: {None}) exclude_builtins_scalars {Optional[List[str]]} -- An optional list of string containing the names of the builtin scalar you don't want to be automatically included, usually it's Date, DateTime or Time scalars (default: {None}) modules {Optional[Union[str, List[str]]]} -- An optional list of string containing the name of the modules you want the engine to import, usually this modules contains your Resolvers, Directives, Scalar or Subscription code (default: {None}) """ if isinstance(modules, str): modules = [modules] self._modules = _import_modules(modules) self._error_coercer = error_coercer self._parser = TartifletteRequestParser() SchemaRegistry.register_sdl(schema_name, sdl, exclude_builtins_scalars) self._schema = SchemaBakery.bake( schema_name, custom_default_resolver, exclude_builtins_scalars )
async def test_build_schema(monkeypatch, clean_registry): from tartiflette.resolver.factory import ResolverExecutorFactory from tartiflette.engine import _import_builtins resolver_excutor = Mock() monkeypatch.setattr( ResolverExecutorFactory, "get_resolver_executor", Mock(return_value=resolver_excutor), ) from tartiflette.schema.bakery import SchemaBakery from tartiflette.types.argument import GraphQLArgument from tartiflette.types.field import GraphQLField from tartiflette.types.input_object import GraphQLInputObjectType from tartiflette.types.interface import GraphQLInterfaceType from tartiflette.types.list import GraphQLList from tartiflette.types.non_null import GraphQLNonNull from tartiflette.types.object import GraphQLObjectType from tartiflette.types.union import GraphQLUnionType schema_sdl = """ schema @enable_cache { query: RootQuery mutation: RootMutation subscription: RootSubscription } union Group = Foo | Bar | Baz interface Something { oneField: [Int] anotherField: [String] aLastOne: [[Date!]]! } input UserInfo { name: String dateOfBirth: [Date] graphQLFan: Boolean! } # directive @partner(goo: Anything) on ENUM_VALUE \"\"\" This is a docstring for the Test Object Type. \"\"\" type Test implements Unknown & Empty{ \"\"\" This is a field description :D \"\"\" field(input: InputObject): String! anotherField: [Int] fieldWithDefaultValueArg(test: String = "default"): ID simpleField: Date } """ _, schema_sdl = await _import_builtins([], schema_sdl, "G") _, schema_E = await _import_builtins([], "", "E") clean_registry.register_sdl("G", schema_sdl) clean_registry.register_sdl("E", schema_E) generated_schema = SchemaBakery._preheat("G") expected_schema = SchemaBakery._preheat("E") expected_schema.query_type = "RootQuery" expected_schema.mutation_type = "RootMutation" expected_schema.subscription_type = "RootSubscription" expected_schema.add_definition( GraphQLUnionType(name="Group", gql_types=["Foo", "Bar", "Baz"])) expected_schema.add_definition( GraphQLInterfaceType( name="Something", fields=OrderedDict( oneField=GraphQLField(name="oneField", gql_type=GraphQLList(gql_type="Int")), anotherField=GraphQLField( name="anotherField", gql_type=GraphQLList(gql_type="String"), ), aLastOne=GraphQLField( name="aLastOne", gql_type=GraphQLNonNull(gql_type=GraphQLList( gql_type=GraphQLList(gql_type=GraphQLNonNull( gql_type="Date")))), ), ), )) expected_schema.add_definition( GraphQLInputObjectType( name="UserInfo", fields=OrderedDict([ ("name", GraphQLArgument(name="name", gql_type="String")), ( "dateOfBirth", GraphQLArgument( name="dateOfBirth", gql_type=GraphQLList(gql_type="Date"), ), ), ( "graphQLFan", GraphQLArgument( name="graphQLFan", gql_type=GraphQLNonNull(gql_type="Boolean"), ), ), ]), )) expected_schema.add_definition( GraphQLObjectType( name="Test", fields=OrderedDict([ ( "field", GraphQLField( name="field", gql_type=GraphQLNonNull(gql_type="String"), arguments=OrderedDict(input=GraphQLArgument( name="input", gql_type="InputObject")), ), ), ( "anotherField", GraphQLField( name="anotherField", gql_type=GraphQLList(gql_type="Int"), ), ), ( "fieldWithDefaultValueArg", GraphQLField( name="fieldWithDefaultValueArg", gql_type="ID", arguments=OrderedDict(test=GraphQLArgument( name="test", gql_type="String", default_value="default", )), ), ), ( "simpleField", GraphQLField(name="simpleField", gql_type="Date"), ), ]), interfaces=["Unknown", "Empty"], )) assert 5 < len(generated_schema._gql_types) assert len(expected_schema._gql_types) == len(generated_schema._gql_types) monkeypatch.undo()
async def test_schema_object_get_field_name(clean_registry): schema_sdl = """ schema { query: RootQuery mutation: Mutation subscription: Subscription } union Group = Foo | Bar | Baz interface Something { oneField: [Int] anotherField: [String] aLastOne: [[Date!]]! } input UserInfo { name: String dateOfBirth: [Date] graphQLFan: Boolean! } type RootQuery { defaultField: Int } # Query has been replaced by RootQuery as entrypoint type Query { nonDefaultField: String } \"\"\" This is a docstring for the Test Object Type. \"\"\" type Test { \"\"\" This is a field description :D \"\"\" field(input: InputObject): String! anotherField: [Int] fieldWithDefaultValueArg(test: String = "default"): ID simpleField: Date } """ _, schema_sdl = await _import_builtins([], schema_sdl, "default") clean_registry.register_sdl("A", schema_sdl) generated_schema = SchemaBakery._preheat("A") with pytest.raises(ImproperlyConfigured): generated_schema.get_field_by_name("Invalid.Field.name") with pytest.raises(ImproperlyConfigured): generated_schema.get_field_by_name("") with pytest.raises(ImproperlyConfigured): generated_schema.get_field_by_name("unknownField") # Happy path assert (generated_schema.get_field_by_name("Query.nonDefaultField") is not None) assert (generated_schema.get_field_by_name("RootQuery.defaultField") is not None) assert generated_schema.get_field_by_name("Test.field") is not None assert generated_schema.get_field_by_name("Test.simpleField") is not None # Sad path with pytest.raises(UnknownSchemaFieldResolver): assert generated_schema.get_field_by_name("Something.unknownField")
async def test_resolver_decorator(clean_registry): schema_sdl = """ schema { query: RootQuery mutation: Mutation subscription: Subscription } type Foo { a: String } type Bar { b: String } type Baz { c: String } union Group = Foo | Bar | Baz interface Something { oneField: [Int] anotherField: [String] aLastOne: [[Date!]]! } input UserInfo { name: String dateOfBirth: [Date] graphQLFan: Boolean! } type RootQuery { defaultField: Int } # Query has been replaced by RootQuery as entrypoint type Query { nonDefaultField: String } \"\"\" This is a docstring for the Test Object Type. \"\"\" type Test { \"\"\" This is a field description :D \"\"\" field(input: UserInfo): String! anotherField: [Int] fieldWithDefaultValueArg(test: String = "default"): ID simpleField: Date } """ clean_registry.register_sdl("default", schema_sdl) mock_one = Mock() mock_two = Mock() @Resolver("Test.field") async def func_field_resolver(*args, **kwargs): mock_one() return @Resolver("RootQuery.defaultField") async def func_default_resolver(*args, **kwargs): mock_two() return with pytest.raises(NonAwaitableResolver): @Resolver("Test.simpleField") def func_default_resolver(*args, **kwargs): pass generated_schema = SchemaBakery.bake("default") assert (generated_schema.get_field_by_name("Test.field").resolver is not None) assert callable(generated_schema.get_field_by_name("Test.field").resolver) assert mock_one.called is False assert ( generated_schema.get_field_by_name("RootQuery.defaultField").resolver is not None) assert callable( generated_schema.get_field_by_name("RootQuery.defaultField").resolver) assert mock_two.called is False