Skip to content

Commit f84c6a6

Browse files
committed
Implement assert_object_implements_interface #8
1 parent e95e1a9 commit f84c6a6

File tree

3 files changed

+60
-4
lines changed

3 files changed

+60
-4
lines changed

graphql/core/type/definition.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ def define_enum_values(type, value_map):
519519
assert isinstance(value, GraphQLEnumValue), (
520520
'{}.{} must be an instance of GraphQLEnumValue, but got: {}'.format(type, value_name, value)
521521
)
522+
value = copy.copy(value)
522523
value.name = value_name
523524
if value.value is None:
524525
value.value = value_name

graphql/core/type/schema.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def __init__(self, query, mutation=None):
3636
self._type_map = self._build_type_map()
3737
self._directives = None
3838

39+
for type in self._type_map.values():
40+
if isinstance(type, GraphQLObjectType):
41+
for interface in type.get_interfaces():
42+
assert_object_implements_interface(type, interface)
43+
3944
def get_query_type(self):
4045
return self._query
4146

@@ -82,8 +87,8 @@ def type_map_reducer(map, type):
8287
if type.name in map:
8388
assert map[type.name] == type, (
8489
'Schema must contain unique named types but contains multiple types named "{}".'
85-
.format(type.name)
86-
)
90+
).format(type.name)
91+
8792
return map
8893

8994
map[type.name] = type
@@ -110,3 +115,50 @@ def type_map_reducer(map, type):
110115
reduced_map = type_map_reducer(reduced_map, getattr(field, 'type', None))
111116

112117
return reduced_map
118+
119+
120+
def assert_object_implements_interface(object, interface):
121+
object_field_map = object.get_fields()
122+
interface_field_map = interface.get_fields()
123+
124+
for field_name, interface_field in interface_field_map.items():
125+
object_field = object_field_map.get(field_name)
126+
127+
assert object_field, '"{}" expects field "{}" but "{}" does not provide it.'.format(
128+
interface, field_name, object
129+
)
130+
131+
assert is_equal_type(interface_field.type, object_field.type), (
132+
'{}.{} expects type "{}" but {}.{} provides type "{}".'
133+
).format(interface, field_name, interface_field.type, object, field_name, object_field.type)
134+
135+
object_arg_map = {arg.name: arg for arg in object_field.args}
136+
for interface_arg in interface_field.args:
137+
arg_name = interface_arg.name
138+
object_arg = object_arg_map.get(arg_name)
139+
140+
assert object_arg, (
141+
'{}.{} expects argument "{}" but {}.{} does not provide it.'
142+
).format(interface, field_name, arg_name, object, field_name)
143+
144+
assert is_equal_type(interface_arg.type, object_arg.type), (
145+
'{}.{}({}:) expects type "{}" but {}.{}({}:) provides type "{}".'
146+
).format(interface, field_name, arg_name, interface_arg.type, object, field_name, arg_name, object_arg.type)
147+
148+
interface_arg_map = {arg.name: arg for arg in interface_field.args}
149+
for object_arg in object_field.args:
150+
arg_name = object_arg.name
151+
interface_arg = interface_arg_map.get(arg_name)
152+
assert interface_arg, (
153+
'{}.{} does not define argument "{}" but {}.{} provides it.'
154+
).format(interface, field_name, arg_name, object, field_name)
155+
156+
157+
def is_equal_type(type_a, type_b):
158+
if isinstance(type_a, GraphQLNonNull) and isinstance(type_b, GraphQLNonNull):
159+
return is_equal_type(type_a.of_type, type_b.of_type)
160+
161+
if isinstance(type_a, GraphQLList) and isinstance(type_b, GraphQLList):
162+
return is_equal_type(type_a.of_type, type_b.of_type)
163+
164+
return type_a is type_b

tests/core_validation/utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@
5050
}, interfaces=[Being, Pet], is_type_of=lambda: None)
5151

5252
Cat = GraphQLObjectType('Cat', lambda: {
53-
'furColor': GraphQLField(FurColor)
53+
'furColor': GraphQLField(FurColor),
54+
'name': GraphQLField(GraphQLString, {
55+
'surname': GraphQLArgument(GraphQLBoolean),
56+
})
5457
}, interfaces=[Being, Pet], is_type_of=lambda: None)
5558

5659
CatOrDog = GraphQLUnionType('CatOrDog', [Dog, Cat])
@@ -185,7 +188,7 @@ def expect_invalid(schema, rules, query, expected_errors, sort_list=True):
185188
error['locations'] = [
186189
{'line': loc.line, 'column': loc.column}
187190
for loc in error['locations']
188-
]
191+
]
189192
if sort_list:
190193
assert sort_lists(list(map(format_error, errors))) == sort_lists(expected_errors)
191194

0 commit comments

Comments
 (0)