Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can we generate client with Sync and ASync function #252

Open
imadmoussa1 opened this issue Dec 15, 2023 · 3 comments
Open

Can we generate client with Sync and ASync function #252

imadmoussa1 opened this issue Dec 15, 2023 · 3 comments

Comments

@imadmoussa1
Copy link

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 ?

@imadmoussa1 imadmoussa1 changed the title Can we generate with Sync and ASync function Can we generate client with Sync and ASync function Dec 15, 2023
@mat-sop
Copy link
Contributor

mat-sop commented Dec 15, 2023

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"]

@rafalp
Copy link
Contributor

rafalp commented Apr 25, 2024

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.

@debegr92
Copy link

Would be great to have sync and async client functions generated in one library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants