Skip to content

change transports prototype using GraphQLRequest #551

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 105 additions & 119 deletions gql/client.py

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion gql/graphql_request.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dataclasses import dataclass
from typing import Any, Dict, Optional

from graphql import DocumentNode, GraphQLSchema
from graphql import DocumentNode, GraphQLSchema, print_ast

from .utilities import serialize_variable_values

Expand Down Expand Up @@ -35,3 +35,16 @@ def serialize_variable_values(self, schema: GraphQLSchema) -> "GraphQLRequest":
),
operation_name=self.operation_name,
)

@property
def payload(self) -> Dict[str, Any]:
query_str = print_ast(self.document)
payload: Dict[str, Any] = {"query": query_str}

if self.operation_name:
payload["operationName"] = self.operation_name

if self.variable_values:
payload["variables"] = self.variable_values

return payload
50 changes: 14 additions & 36 deletions gql/transport/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from aiohttp.client_reqrep import Fingerprint
from aiohttp.helpers import BasicAuth
from aiohttp.typedefs import LooseCookies, LooseHeaders
from graphql import DocumentNode, ExecutionResult, print_ast
from graphql import ExecutionResult
from multidict import CIMultiDictProxy

from ..graphql_request import GraphQLRequest
Expand Down Expand Up @@ -164,25 +164,13 @@ async def close(self) -> None:

self.session = None

def _build_payload(self, req: GraphQLRequest) -> Dict[str, Any]:
query_str = print_ast(req.document)
payload: Dict[str, Any] = {"query": query_str}

if req.operation_name:
payload["operationName"] = req.operation_name

if req.variable_values:
payload["variables"] = req.variable_values

return payload

def _prepare_batch_request(
self,
reqs: List[GraphQLRequest],
extra_args: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:

payload = [self._build_payload(req) for req in reqs]
payload = [req.payload for req in reqs]

post_args = {"json": payload}

Expand All @@ -198,15 +186,15 @@ def _prepare_batch_request(

def _prepare_request(
self,
req: GraphQLRequest,
request: GraphQLRequest,
extra_args: Optional[Dict[str, Any]] = None,
upload_files: bool = False,
) -> Dict[str, Any]:

payload = self._build_payload(req)
payload = request.payload

if upload_files:
post_args = self._prepare_file_uploads(req, payload)
post_args = self._prepare_file_uploads(request, payload)
else:
post_args = {"json": payload}

Expand All @@ -228,11 +216,11 @@ def _prepare_request(
return post_args

def _prepare_file_uploads(
self, req: GraphQLRequest, payload: Dict[str, Any]
self, request: GraphQLRequest, payload: Dict[str, Any]
) -> Dict[str, Any]:

# If the upload_files flag is set, then we need variable_values
variable_values = req.variable_values
variable_values = request.variable_values
assert variable_values is not None

# If we upload files, we will extract the files present in the
Expand Down Expand Up @@ -359,36 +347,28 @@ def _raise_invalid_result(self, result_text: str, reason: str) -> None:

async def execute(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
*,
extra_args: Optional[Dict[str, Any]] = None,
upload_files: bool = False,
) -> ExecutionResult:
"""Execute the provided document AST against the configured remote server
"""Execute the provided request against the configured remote server
using the current session.
This uses the aiohttp library to perform a HTTP POST request asynchronously
to the remote server.

Don't call this coroutine directly on the transport, instead use
:code:`execute` on a client or a session.

:param document: the parsed GraphQL request
:param variable_values: An optional Dict of variable values
:param operation_name: An optional Operation name for the request
:param request: GraphQL request as a
:class:`GraphQLRequest <gql.GraphQLRequest>` object.
:param extra_args: additional arguments to send to the aiohttp post method
:param upload_files: Set to True if you want to put files in the variable values
:returns: an ExecutionResult object.
"""

req = GraphQLRequest(
document=document,
variable_values=variable_values,
operation_name=operation_name,
)

post_args = self._prepare_request(
req,
request,
extra_args,
upload_files,
)
Expand Down Expand Up @@ -434,9 +414,7 @@ async def execute_batch(

def subscribe(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> AsyncGenerator[ExecutionResult, None]:
"""Subscribe is not supported on HTTP.

Expand Down
19 changes: 5 additions & 14 deletions gql/transport/appsync_websockets.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from typing import Any, Dict, Optional, Tuple, Union, cast
from urllib.parse import urlparse

from graphql import DocumentNode, ExecutionResult, print_ast
from graphql import ExecutionResult

from ..graphql_request import GraphQLRequest
from .appsync_auth import AppSyncAuthentication, AppSyncIAMAuthentication
from .common.adapters.websockets import WebSocketsAdapter
from .common.base import SubscriptionTransportBase
Expand Down Expand Up @@ -150,22 +151,14 @@ def _parse_answer(

async def _send_query(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> int:

query_id = self.next_query_id

self.next_query_id += 1

data: Dict = {"query": print_ast(document)}

if variable_values:
data["variables"] = variable_values

if operation_name:
data["operationName"] = operation_name
data: Dict[str, Any] = request.payload

serialized_data = json.dumps(data, separators=(",", ":"))

Expand Down Expand Up @@ -203,9 +196,7 @@ async def _send_query(

async def execute(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> ExecutionResult:
"""This method is not available.

Expand Down
14 changes: 5 additions & 9 deletions gql/transport/async_transport.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import abc
from typing import Any, AsyncGenerator, Dict, List, Optional
from typing import Any, AsyncGenerator, List

from graphql import DocumentNode, ExecutionResult
from graphql import ExecutionResult

from ..graphql_request import GraphQLRequest

Expand All @@ -24,11 +24,9 @@ async def close(self):
@abc.abstractmethod
async def execute(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> ExecutionResult:
"""Execute the provided document AST for either a remote or local GraphQL
"""Execute the provided request for either a remote or local GraphQL
Schema."""
raise NotImplementedError(
"Any AsyncTransport subclass must implement execute method"
Expand All @@ -54,9 +52,7 @@ async def execute_batch(
@abc.abstractmethod
def subscribe(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> AsyncGenerator[ExecutionResult, None]:
"""Send a query and receive the results using an async generator

Expand Down
23 changes: 10 additions & 13 deletions gql/transport/common/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
from contextlib import suppress
from typing import Any, AsyncGenerator, Dict, Optional, Tuple, Union

from graphql import DocumentNode, ExecutionResult
from graphql import ExecutionResult

from ...graphql_request import GraphQLRequest
from ..async_transport import AsyncTransport
from ..exceptions import (
TransportAlreadyConnected,
Expand Down Expand Up @@ -158,9 +159,7 @@ async def _receive(self) -> str:
@abstractmethod
async def _send_query(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> int:
raise NotImplementedError # pragma: no cover

Expand Down Expand Up @@ -267,9 +266,8 @@ async def _handle_answer(

async def subscribe(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
*,
send_stop: Optional[bool] = True,
) -> AsyncGenerator[ExecutionResult, None]:
"""Send a query and receive the results using a python async generator.
Expand All @@ -281,7 +279,7 @@ async def subscribe(

# Send the query and receive the id
query_id: int = await self._send_query(
document, variable_values, operation_name
request,
)

# Create a queue to receive the answers for this query_id
Expand Down Expand Up @@ -325,11 +323,9 @@ async def subscribe(

async def execute(
self,
document: DocumentNode,
variable_values: Optional[Dict[str, Any]] = None,
operation_name: Optional[str] = None,
request: GraphQLRequest,
) -> ExecutionResult:
"""Execute the provided document AST against the configured remote server
"""Execute the provided request against the configured remote server
using the current session.

Send a query but close the async generator as soon as we have the first answer.
Expand All @@ -339,7 +335,8 @@ async def execute(
first_result = None

generator = self.subscribe(
document, variable_values, operation_name, send_stop=False
request,
send_stop=False,
)

async for result in generator:
Expand Down
Loading
Loading