-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathmutation.py
More file actions
117 lines (100 loc) · 4.1 KB
/
mutation.py
File metadata and controls
117 lines (100 loc) · 4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
from collections.abc import Mapping
from inspect import iscoroutinefunction
from typing import Any, Callable, Dict, Optional
from graphql import (
resolve_thunk,
GraphQLArgument,
GraphQLField,
GraphQLFieldMap,
GraphQLInputField,
GraphQLInputFieldMap,
GraphQLInputObjectType,
GraphQLNonNull,
GraphQLObjectType,
GraphQLResolveInfo,
GraphQLString,
ThunkMapping,
)
from graphql.pyutils import AwaitableOrValue
__all__ = [
"mutation_with_client_mutation_id",
"MutationFn",
"MutationFnWithoutArgs",
"NullResult",
]
# Note: Contrary to the Javascript implementation of MutationFn,
# the context is passed as part of the GraphQLResolveInfo and any arguments
# are passed individually as keyword arguments.
MutationFnWithoutArgs = Callable[[GraphQLResolveInfo], AwaitableOrValue[Any]]
# Unfortunately there is currently no syntax to indicate optional or keyword
# arguments in Python, so we also allow any other Callable as a workaround:
MutationFn = Callable[..., AwaitableOrValue[Any]]
class NullResult:
def __init__(self, clientMutationId: Optional[str] = None) -> None:
self.clientMutationId = clientMutationId
def mutation_with_client_mutation_id(
name: str,
input_fields: ThunkMapping[GraphQLInputField],
output_fields: ThunkMapping[GraphQLField],
mutate_and_get_payload: MutationFn,
description: Optional[str] = None,
deprecation_reason: Optional[str] = None,
extensions: Optional[Dict[str, Any]] = None,
) -> GraphQLField:
"""
Returns a GraphQLFieldConfig for the specified mutation.
The input_fields and output_fields should not include `clientMutationId`,
as this will be provided automatically.
An input object will be created containing the input fields, and an
object will be created containing the output fields.
mutate_and_get_payload will receive a GraphQLResolveInfo as first argument,
and the input fields as keyword arguments, and it should return an object
(or a dict) with an attribute (or a key) for each output field.
It may return synchronously or asynchronously.
"""
def augmented_input_fields() -> GraphQLInputFieldMap:
return dict(
resolve_thunk(input_fields),
clientMutationId=GraphQLInputField(GraphQLString),
)
def augmented_output_fields() -> GraphQLFieldMap:
return dict(
resolve_thunk(output_fields),
clientMutationId=GraphQLField(GraphQLString),
)
output_type = GraphQLObjectType(name + "Payload", fields=augmented_output_fields)
input_type = GraphQLInputObjectType(name + "Input", fields=augmented_input_fields)
if iscoroutinefunction(mutate_and_get_payload):
# noinspection PyShadowingBuiltins
async def resolve(_root: Any, info: GraphQLResolveInfo, input: Dict) -> Any:
payload = await mutate_and_get_payload(info, **input)
clientMutationId = input.get("clientMutationId")
if payload is None:
return NullResult(clientMutationId)
if isinstance(payload, Mapping):
payload["clientMutationId"] = clientMutationId # type: ignore
else:
payload.clientMutationId = clientMutationId
return payload
else:
# noinspection PyShadowingBuiltins
def resolve( # type: ignore
_root: Any, info: GraphQLResolveInfo, input: Dict
) -> Any:
payload = mutate_and_get_payload(info, **input)
clientMutationId = input.get("clientMutationId")
if payload is None:
return NullResult(clientMutationId)
if isinstance(payload, Mapping):
payload["clientMutationId"] = clientMutationId # type: ignore
else:
payload.clientMutationId = clientMutationId # type: ignore
return payload
return GraphQLField(
output_type,
description=description,
deprecation_reason=deprecation_reason,
args={"input": GraphQLArgument(GraphQLNonNull(input_type))},
resolve=resolve,
extensions=extensions,
)