Skip to content

Commit f438eb8

Browse files
committed
Some additional type sanity checks when lazy resolving.
1 parent 70e8b95 commit f438eb8

File tree

3 files changed

+62
-10
lines changed

3 files changed

+62
-10
lines changed

epoxy/registry.py

+21-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from collections import OrderedDict, defaultdict
22
from enum import Enum
33
from functools import partial
4+
from operator import itemgetter
45
from graphql.core.type import (
56
GraphQLBoolean,
67
GraphQLEnumType,
@@ -17,7 +18,6 @@
1718
)
1819
from graphql.core.type.definition import GraphQLType, get_named_type
1920
import six
20-
2121
from .bases.class_type_creator import ClassTypeCreator
2222
from .bases.input_type import InputTypeBase
2323
from .bases.mutation import MutationBase
@@ -34,7 +34,7 @@
3434
from .utils.enum_to_graphql_enum import enum_to_graphql_enum
3535
from .utils.maybe_t import maybe_t
3636
from .utils.method_dispatch import method_dispatch
37-
from .utils.thunk import AttributeTypeThunk, RootTypeThunk, ThunkList, TransformThunkList
37+
from .utils.thunk import AttributeTypeThunk, IdentityTypeThunk, RootTypeThunk, ThunkList, TransformThunkList
3838

3939
builtin_scalars = [
4040
GraphQLBoolean,
@@ -113,12 +113,22 @@ def _resolve_type(self, item):
113113
if not isinstance(item, str):
114114
item = maybe_t(item)
115115
assert isinstance(item, GraphQLType), \
116-
'Attempted to resolve an item {} that is not a GraphQLType'.format(item)
116+
'Attempted to resolve an item "{}" that is not a GraphQLType'.format(item)
117+
118+
named_type = get_named_type(item)
119+
known_type = self._registered_types.get(named_type.name)
120+
# Happens when we attempt to resolve an un-registered type.
121+
assert known_type and known_type not in self._reserved_names, \
122+
'Attempted to resolve a type "{}" that is not registered with this Registry.'.format(item)
123+
124+
# Happens when we attempt to resolve a type that is already registered, but isn't the same type.
125+
assert known_type is named_type, \
126+
'Attempted to resolve a type "{}" that does not match the already registered type.'.format(item)
117127

118128
return item
119129

120130
value = self._registered_types.get(item)
121-
assert value, "Type {} was requested, but was not registered.".format(item)
131+
assert value, 'Type "{}" was requested, but was not registered.'.format(item)
122132
return value
123133

124134
def __getattr__(self, item):
@@ -266,14 +276,16 @@ def Mutations(self):
266276
if not self._mutations:
267277
raise TypeError("No mutations have been registered.")
268278

269-
mutations = OrderedDict()
270-
for k in sorted(self._mutations.keys()):
271-
mutations[k] = self._mutations[k]()
279+
existing_mutation_type = self._registered_types.get('Mutations')
280+
if existing_mutation_type:
281+
return IdentityTypeThunk(existing_mutation_type)
272282

273-
return GraphQLObjectType(
283+
mutations = GraphQLObjectType(
274284
name='Mutations',
275-
fields=mutations
285+
fields=lambda: OrderedDict([(k, v()) for k, v in sorted(self._mutations.items(), key=itemgetter(0))])
276286
)
287+
self._registered_types[mutations.name] = mutations
288+
return IdentityTypeThunk(mutations)
277289

278290
def _create_is_type_of(self, type):
279291
return partial(self._is_type_of, type)

tests/test_declarative_definition.py

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from graphql.core.type.definition import GraphQLObjectType, GraphQLNonNull, GraphQLList
1+
from graphql.core.type.definition import GraphQLObjectType, GraphQLNonNull, GraphQLList, GraphQLField
22
from graphql.core.type.scalars import GraphQLString
33
from epoxy.registry import TypeRegistry
44
from pytest import raises
@@ -178,3 +178,41 @@ class Dog(R.ObjectType):
178178

179179
field_order = list(Dog.T.get_fields().keys())
180180
assert field_order == ['id', 'name', 'dog', 'someOtherField', 'someOtherDog', 'foo', 'bar', 'aaa']
181+
182+
183+
def test_cannot_resolve_unregistered_type():
184+
R = TypeRegistry()
185+
186+
Dog = GraphQLObjectType(
187+
name='Dog',
188+
fields={
189+
'a': GraphQLField(GraphQLString)
190+
}
191+
)
192+
193+
with raises(AssertionError) as excinfo:
194+
R[Dog]()
195+
196+
assert str(excinfo.value) == 'Attempted to resolve a type "Dog" that is not registered with this Registry.'
197+
198+
R(Dog)
199+
assert R[Dog]() is Dog
200+
201+
202+
def test_cannot_resolve_type_of_same_name_that_is_mismatched():
203+
R = TypeRegistry()
204+
205+
class Dog(R.ObjectType):
206+
a = R.String
207+
208+
SomeOtherDog = GraphQLObjectType(
209+
name='Dog',
210+
fields={
211+
'a': GraphQLField(GraphQLString)
212+
}
213+
)
214+
215+
with raises(AssertionError) as excinfo:
216+
R[SomeOtherDog]()
217+
218+
assert str(excinfo.value) == 'Attempted to resolve a type "Dog" that does not match the already registered type.'

tests/test_graphql_object_interoperability.py

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class Query(R.ObjectType):
2222
def resolve_built_in_type(self, obj, args, info):
2323
return BuiltInTypeTuple('Hello World. I am a string.')
2424

25+
R(BuiltInType)
26+
2527
schema = R.Schema(R.Query)
2628
result = graphql(schema, '{ builtInType { someString } }')
2729
assert not result.errors

0 commit comments

Comments
 (0)