Can we generate client with Sync and ASync function
I am using the codegen to create a python client, in my schema I have subscriptions, query and mutations. The subscriptions should be Async but at the same time need the query and mutations to be sync. is there a way to do it ?
Hey, currently we don't support mixing of sync and async methods in generated client, but it can be done using plugins API and custom base client options, e.g:
Plugin:
import ast
from typing import Union, cast
from graphql import OperationDefinitionNode, OperationType
from ariadne_codegen.plugins.base import Plugin
class MixedClientPlugin(Plugin):
def generate_client_method(
self,
method_def: Union[ast.FunctionDef, ast.AsyncFunctionDef],
operation_definition: OperationDefinitionNode,
) -> Union[ast.FunctionDef, ast.AsyncFunctionDef]:
if operation_definition.operation == OperationType.SUBSCRIPTION or isinstance(
method_def, ast.FunctionDef
):
return method_def # we do not change anything for subscriptions or already sync methods
sync_method_def = ast.FunctionDef(
name=method_def.name,
args=method_def.args,
body=method_def.body,
decorator_list=method_def.decorator_list,
returns=method_def.returns,
lineno=method_def.lineno,
) # we create sync method with the same data as async source method
async_execute_assign = cast(
ast.Assign, method_def.body[-3]
) # ast for `response = await `self.execute(...)``
sync_method_def.body[-3] = ast.Assign(
targets=async_execute_assign.targets,
value=cast(ast.Await, async_execute_assign.value).value,
lineno=async_execute_assign.lineno,
) # we "unpack" await, `response = await self.execute(...)` becomes `response = self.execute(...)`
return sync_method_def
A compatible base client, should have async execute_ws and sync execute.
Assuming this base client is called CustomMixedBaseClient then configuration will look like this:
[tool.ariadne-codegen]
...
base_client_name = "CustomMixedBaseClient"
base_client_file_path = ".../custom_mixed_base_client.py"
async_client = true
plugins = ["....MixedClientPlugin"]
Shower though: what if base client's code was a simple template that we would render a final Python file from when client package is created? Template file could be Python file itself, but with simple template logic:
# ARIADNE: ASYNC
... This part will be included if async mode is enabled
# ENDARIADNE
# ARIADNE: SYNC
... This part will be included if sync mode is enabled
# ENDARIADNE
This can be achieved with a simple regex. We would still need to generate sync and async client methods at same time tho. Likely the sync methods would then be suffixed with _sync as is already convention in other GraphQL tooling we are dealing with.
Would be great to have sync and async client functions generated in one library.