Skip to content

Commit 951e014

Browse files
committed
[v0.2.1] Argument key translation from camelCase in GraphQL world to the original casing specified.
1 parent 2df4fad commit 951e014

File tree

5 files changed

+92
-10
lines changed

5 files changed

+92
-10
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Query(R.ObjectType):
4040

4141
def resolve_human(self, obj, args, info):
4242
"""This will be used as the description of the field Query.human."""
43-
return Human(5, 'Bob', [Human(6, 'Bill')]
43+
return Human(id=5, name='Bob', friends=[Human(id=6, name='Bill')]
4444

4545
```
4646

Diff for: epoxy/types/field.py

+24-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
from collections import OrderedDict
2-
32
from graphql.core.type import GraphQLField, GraphQLInputObjectField
4-
53
from ..types.argument import Argument
64
from ..utils.gen_id import gen_id
75
from ..utils.thunk import TypeThunk
86
from ..utils.to_camel_case import to_camel_case
7+
from ..utils.wrap_resolver_translating_arguments import wrap_resolver_translating_arguments
98

109

1110
class Field(object):
@@ -20,13 +19,19 @@ def __init__(self, type, description=None, args=None, name=None, resolver=None,
2019
self._counter = _counter or gen_id()
2120

2221
def to_field(self, registry, resolver):
23-
return GraphQLField(registry[self.type](), args=self.get_arguments(registry), resolver=resolver)
22+
args, arguments_to_original_case = self.get_arguments(registry)
23+
24+
if arguments_to_original_case:
25+
resolver = wrap_resolver_translating_arguments(resolver, arguments_to_original_case)
26+
27+
return GraphQLField(registry[self.type](), args=args, resolver=resolver)
2428

2529
def get_arguments(self, registry):
2630
if not self.args:
27-
return None
31+
return None, None
2832

2933
arguments = []
34+
arguments_to_original_case = {}
3035

3136
for k, argument in self.args.items():
3237
if isinstance(argument, TypeThunk):
@@ -35,16 +40,29 @@ def get_arguments(self, registry):
3540
elif not isinstance(argument, Argument):
3641
raise ValueError('Unknown argument value type %r' % argument)
3742

43+
camel_cased_name = to_camel_case(k)
44+
if camel_cased_name in arguments_to_original_case:
45+
raise ValueError(
46+
'Argument %s already exists as %s' %
47+
(k, arguments_to_original_case[camel_cased_name])
48+
)
49+
50+
arguments_to_original_case[camel_cased_name] = k
3851
arguments.append((
39-
to_camel_case(k), argument
52+
camel_cased_name, argument
4053
))
4154

4255
if not isinstance(self.args, OrderedDict):
4356
arguments.sort(
4457
key=lambda i: i[1]._counter
4558
)
4659

47-
return OrderedDict([(k, v.to_argument(registry)) for k, v in arguments])
60+
# Remove things that wouldn't perform any meaningful translation.
61+
for k, v in list(arguments_to_original_case.items()):
62+
if k == v:
63+
del arguments_to_original_case[k]
64+
65+
return OrderedDict([(k, v.to_argument(registry)) for k, v in arguments]), arguments_to_original_case
4866

4967

5068
class InputField(object):

Diff for: epoxy/utils/wrap_resolver_translating_arguments.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from collections import OrderedDict
2+
3+
from six import wraps
4+
5+
6+
def wrap_resolver_translating_arguments(resolver, arguments_to_original_case):
7+
translate_key = arguments_to_original_case.get
8+
9+
@wraps(resolver)
10+
def wrapped(obj, args, info):
11+
new_args = OrderedDict() if isinstance(args, OrderedDict) else {}
12+
for k in args:
13+
new_args[translate_key(k, k)] = args[k]
14+
15+
return resolver(obj, new_args, info)
16+
17+
return wrapped

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
setup(
1313
name='graphql-epoxy',
14-
version='0.2',
14+
version='0.2.1',
1515
description='GraphQL implementation for Python',
1616
url='https://github.com/graphql-python/graphql-core',
1717
download_url='https://github.com/graphql-python/graphql-core/releases',

Diff for: tests/test_arguments.py

+49-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections import OrderedDict
2-
2+
from pytest import raises
3+
from graphql.core import graphql
34
from graphql.core.type import GraphQLString, GraphQLInt, GraphQLID, GraphQLNonNull
4-
55
from epoxy.registry import TypeRegistry
66
from epoxy.types.argument import Argument
77

@@ -88,3 +88,50 @@ class Query(R.ObjectType):
8888
query_type = R.Query()
8989
check_args(TestInputType.T, query_type.get_fields()['int'].args)
9090
check_args(TestInputType.T, query_type.get_fields()['intFromField'].args)
91+
92+
93+
def test_resolved_args_will_be_translated_to_original_casing():
94+
R = TypeRegistry()
95+
96+
class Query(R.ObjectType):
97+
argument_keys = R.String.List(args={
98+
'foo': R.String,
99+
'foo_bar': R.String
100+
})
101+
102+
def resolve_argument_keys(self, obj, args, info):
103+
return list(sorted(args.keys()))
104+
105+
Schema = R.Schema(R.Query)
106+
result = graphql(Schema, '''
107+
{
108+
argumentKeys(foo: "Hello", fooBar: "World")
109+
}
110+
''')
111+
112+
assert not result.errors
113+
114+
assert result.data == {
115+
'argumentKeys': ['foo', 'foo_bar']
116+
}
117+
118+
119+
def test_will_recognize_casing_conversion_conflicts():
120+
R = TypeRegistry()
121+
122+
class Query(R.ObjectType):
123+
argument_keys = R.String.List(args={
124+
'foo_bar': R.String,
125+
'fooBar': R.String
126+
})
127+
128+
def resolve_argument_keys(self, obj, args, info):
129+
return list(sorted(args.keys()))
130+
131+
with raises(ValueError) as excinfo:
132+
Schema = R.Schema(R.Query)
133+
134+
assert str(excinfo.value) in (
135+
'Argument foo_bar already exists as fooBar',
136+
'Argument fooBar already exists as foo_bar',
137+
)

0 commit comments

Comments
 (0)