コード例 #1
0
ファイル: engine.py プロジェクト: alexchamberlain/tartiflette
    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)
コード例 #2
0
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()
コード例 #3
0
def test_schema_bake_schema(clean_registry):
    clean_registry.register_sdl(
        "a",
        """
        type Query {
            lol: Int
        }
    """,
    )
    assert SchemaBakery.bake("a") is not None
コード例 #4
0
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
コード例 #5
0
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
コード例 #6
0
    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
コード例 #7
0
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()
コード例 #8
0
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
コード例 #9
0
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
コード例 #10
0
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
コード例 #11
0
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)
コード例 #12
0
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
コード例 #13
0
    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
        )
コード例 #14
0
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()
コード例 #15
0
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")
コード例 #16
0
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