tartiflette/tartiflette-aiohttp

Provides a context generator parameter

Maximilien-R opened this issue · 2 comments

I think it could be a good idea to add a new context_factory optional parameter to the register_graphql_handlers function.

The aim of this function would be to generate a new context on each request instead of the actual executor_context parameter which only allow to provide a context at engine and endpoints initialization time.

This parameter could take a (async?) callable which could implements the following implementation:

from typing import Any, Dict


def context_factory(req: "aiohttp.web.Request", context: Dict[str, Any]) -> Dict[str, Any]:
    """
    Generates a new context.
    :param req: the incoming aiohttp request instance
    :param context: the value filled in through the `executor_context` parameter
    :type req: aiohttp.web.Request
    :type context: Dict[str, Any]
    :return: the context for the incoming request
    :rtype: Dict[str, Any]
    """
    pass

To be retro-compatible, the default implementation could be:

from copy import copy
from typing import Any, Dict


def default_context_factory(req: "aiohttp.web.Request", context: Dict[str, Any]) -> Dict[str, Any]:
    """
    Generates a new context.
    :param req: the incoming aiohttp request instance
    :param context: the value filled in through the `executor_context` parameter
    :type req: aiohttp.web.Request
    :type context: Dict[str, Any]
    :return: the context for the incoming request
    :rtype: Dict[str, Any]
    """
    return copy(context)

Also, having access to the request through this callable could allow to build a more complex et useful context by reading headers for example.

Finally, both executor_context and context_factory could be used at the same time to build a dynamic/static context:

from typing import Any, Dict


def custom_context_factory(req: "aiohttp.web.Request", context: Dict[str, Any]) -> Dict[str, Any]:
    """
    Generates a new context.
    :param req: the incoming aiohttp request instance
    :param context: the value filled in through the `executor_context` parameter
    :type req: aiohttp.web.Request
    :type context: Dict[str, Any]
    :return: the context for the incoming request
    :rtype: Dict[str, Any]
    """
    return {
        **context,
        # request contextualized data
        "authorization_token": req.headers.get("Authorization"),
    }


register_graphql_handlers(
    app=web.Application(),
    engine_sdl="/sdl",
    executor_context={
        "redis_client": RedisCient(),  # not request contextualized
    },
    context_factory=custom_context_factory,
)

@abusi @tsunammis what do you think of it?

abusi commented

Liking it very much. Go for it.

Should it be async? Its always a plus, isn't? 😆 Or maybe we should handle both with a check before calling the factory?