Skip to content

Commit 45ec6b9

Browse files
committed
fix: Increases test coverage. Replaces global_id lookup in default connection resolver with more expected get_node convention.
1 parent ef5c214 commit 45ec6b9

File tree

9 files changed

+190
-42
lines changed

9 files changed

+190
-42
lines changed

graphene_mongo/fields.py

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from .advanced_types import PointFieldType, MultiPolygonFieldType
1515
from .converter import convert_mongoengine_field, MongoEngineConversionError
1616
from .registry import get_global_registry
17-
from .utils import get_model_reference_fields, global_id_via_node
17+
from .utils import get_model_reference_fields, get_node_from_global_id
1818

1919

2020
class MongoengineConnectionField(ConnectionField):
@@ -113,18 +113,24 @@ def fields(self):
113113
return self._type._meta.fields
114114

115115
def get_queryset(self, model, info, **args):
116+
117+
if args:
118+
reference_fields = get_model_reference_fields(self.model)
119+
hydrated_references = {}
120+
for arg_name, arg in args.copy().items():
121+
if arg_name in reference_fields:
122+
reference_obj = get_node_from_global_id(reference_fields[arg_name], info, args.pop(arg_name))
123+
hydrated_references[arg_name] = reference_obj
124+
args.update(hydrated_references)
116125
if self._get_queryset:
117126
queryset_or_filters = self._get_queryset(model, info, **args)
118127
if isinstance(queryset_or_filters, mongoengine.QuerySet):
119128
return queryset_or_filters
120129
else:
121-
return model.objects(**queryset_or_filters)
122-
return model.objects()
130+
args.update(queryset_or_filters)
131+
return model.objects(**args)
123132

124133
def default_resolver(self, _root, info, **args):
125-
if not callable(getattr(self.model, 'objects', None)):
126-
return [], 0
127-
128134
args = args or {}
129135

130136
connection_args = {
@@ -134,29 +140,22 @@ def default_resolver(self, _root, info, **args):
134140
'after': args.pop('after', None)
135141
}
136142

137-
objs = self.get_queryset(self.model, info, **args)
138-
139-
if args:
140-
reference_fields = get_model_reference_fields(self.model)
141-
reference_args = {}
142-
for arg_name, arg in args.copy().items():
143-
if arg_name in reference_fields:
144-
reference_model = self.model._fields[arg_name]
145-
pk = global_id_via_node(self.node_type, args.pop(arg_name))[-1]
146-
reference_obj = reference_model.document_type_obj.objects(pk=pk).get()
147-
reference_args[arg_name] = reference_obj
148-
149-
args.update(reference_args)
150-
_id = args.pop('id', None)
151-
if _id is not None:
152-
args['pk'] = global_id_via_node(self.node_type, _id)[-1]
143+
_id = args.pop('id', None)
153144

154-
objs = objs.filter(**args)
145+
if _id is not None:
146+
objs = [get_node_from_global_id(self.node_type, info, _id)]
147+
list_length = 1
148+
elif callable(getattr(self.model, 'objects', None)):
149+
objs = self.get_queryset(self.model, info, **args)
150+
list_length = objs.count()
151+
else:
152+
objs = []
153+
list_length = 0
155154

156155
connection = connection_from_list_slice(
157156
list_slice=objs,
158157
args=connection_args,
159-
list_length=objs.count(),
158+
list_length=list_length,
160159
connection_type=self.type,
161160
edge_type=self.type.Edge,
162161
pageinfo_type=PageInfo,

graphene_mongo/tests/models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ class Publisher(Document):
1717
meta = {'collection': 'test_publisher'}
1818
name = StringField()
1919

20+
@property
21+
def legal_name(self):
22+
return self.name + " Inc."
23+
24+
def bad_field(self):
25+
return None
26+
2027

2128
class Editor(Document):
2229
"""
@@ -137,3 +144,9 @@ class ChildRegisteredAfter(Document):
137144
meta = {'collection': 'test_child_after_reference'}
138145
parent = ReferenceField(ParentWithRelationship)
139146
name = StringField()
147+
148+
149+
class ErroneousModel(Document):
150+
meta = {'collection': 'test_colliding_objects_model'}
151+
152+
objects = ListField(StringField())

graphene_mongo/tests/setup.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@
55
Article, Editor, EmbeddedArticle, Player,
66
Reporter, Child, ProfessorMetadata, ProfessorVector,
77
ChildRegisteredBefore, ChildRegisteredAfter,
8-
ParentWithRelationship, CellTower
9-
)
8+
ParentWithRelationship, CellTower,
9+
Publisher)
1010

1111

1212
@pytest.fixture(scope='module')
1313
def fixtures():
14+
Publisher.drop_collection()
15+
publisher1 = Publisher(name="Newsco")
16+
publisher1.save()
17+
1418
Editor.drop_collection()
1519
editor1 = Editor(
1620
id='1',
1721
first_name='Penny',
1822
last_name='Hardaway',
19-
metadata={'age': '20', 'nickname': '$1'}
23+
metadata={'age': '20', 'nickname': '$1'},
24+
company=publisher1
2025
)
2126
editor1.save()
2227
editor2 = Editor(

graphene_mongo/tests/test_converter.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ def test_should_convert_none():
159159
assert graphene_type is None
160160

161161

162+
def test_should_convert_none_lazily():
163+
registry.reset_global_registry()
164+
dynamic_field = convert_mongoengine_field(
165+
Editor._fields['company'], registry.get_global_registry())
166+
assert isinstance(dynamic_field, Dynamic)
167+
graphene_type = dynamic_field.get_type()
168+
assert graphene_type is None
169+
170+
162171
def test_should_list_of_reference_convert_list():
163172

164173
class A(MongoengineObjectType):

graphene_mongo/tests/test_fields.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from ..fields import MongoengineConnectionField
2-
from .types import ArticleNode
2+
from .types import ArticleNode, PublisherNode, ErroneousModelNode
33

44

55
def test_field_args():
@@ -14,3 +14,24 @@ def test_field_args():
1414
default_args = ['after', 'last', 'first', 'before']
1515
args = field_args + reference_args + default_args
1616
assert set(field.args) == set(args)
17+
18+
19+
def test_field_args_with_property():
20+
field = MongoengineConnectionField(PublisherNode)
21+
22+
field_args = ['id', 'name']
23+
assert set(field.field_args.keys()) == set(field_args)
24+
25+
26+
def test_field_args_with_unconverted_field():
27+
field = MongoengineConnectionField(PublisherNode)
28+
29+
field_args = ['id', 'name']
30+
assert set(field.field_args.keys()) == set(field_args)
31+
32+
33+
def test_default_resolver_with_colliding_objects_field():
34+
field = MongoengineConnectionField(ErroneousModelNode)
35+
36+
connection = field.default_resolver(None, {})
37+
assert 0 == len(connection.iterable)

graphene_mongo/tests/test_query.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ def resolve_editors(self, *args, **kwargs):
2828
query EditorQuery {
2929
editor {
3030
firstName,
31-
metadata
31+
metadata,
32+
company {
33+
name
34+
}
3235
}
3336
editors {
3437
firstName,
@@ -39,7 +42,8 @@ def resolve_editors(self, *args, **kwargs):
3942
expected = {
4043
'editor': {
4144
'firstName': 'Penny',
42-
'metadata': '{"age": "20", "nickname": "$1"}'
45+
'metadata': '{"age": "20", "nickname": "$1"}',
46+
'company': {"name": "Newsco"}
4347
},
4448
'editors': [{
4549
'firstName': 'Penny',
@@ -56,11 +60,8 @@ def resolve_editors(self, *args, **kwargs):
5660
schema = graphene.Schema(query=Query)
5761
result = schema.execute(query)
5862
assert not result.errors
59-
metadata = result.data['editor'].pop('metadata')
60-
expected_metadata = expected['editor'].pop('metadata')
61-
assert(json.loads(metadata)) == dict(json.loads(expected_metadata))
62-
assert dict(result.data['editor']) == expected['editor']
63-
assert all(item in result.data['editors'] for item in expected['editors'])
63+
assert json.dumps(result.data, sort_keys=True) == \
64+
json.dumps(expected, sort_keys=True)
6465

6566

6667
def test_should_query_reporter(fixtures):

graphene_mongo/tests/test_relay_query.py

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ class Query(graphene.ObjectType):
247247
schema = graphene.Schema(query=Query)
248248
result = schema.execute(query)
249249
assert not result.errors
250-
assert result.data == expected
250+
assert json.dumps(result.data, sort_keys=True) == json.dumps(
251+
expected, sort_keys=True)
251252

252253

253254
def test_should_filter_by_reference_field(fixtures):
@@ -287,7 +288,8 @@ class Query(graphene.ObjectType):
287288
schema = graphene.Schema(query=Query)
288289
result = schema.execute(query)
289290
assert not result.errors
290-
assert result.data == expected
291+
assert json.dumps(result.data, sort_keys=True) == json.dumps(
292+
expected, sort_keys=True)
291293

292294

293295
def test_should_filter_through_inheritance(fixtures):
@@ -768,3 +770,92 @@ class Query(graphene.ObjectType):
768770
result = schema.execute(query)
769771
assert not result.errors
770772
assert dict(result.data['allProfessors']) == expected['allProfessors']
773+
774+
775+
def test_should_get_queryset_returns_dict_filters(fixtures):
776+
777+
class Query(graphene.ObjectType):
778+
node = Node.Field()
779+
articles = MongoengineConnectionField(ArticleNode, get_queryset=lambda *_, **__: {"headline": "World"})
780+
781+
query = '''
782+
query ArticlesQuery {
783+
articles {
784+
edges {
785+
node {
786+
headline,
787+
pubDate,
788+
editor {
789+
firstName
790+
}
791+
}
792+
}
793+
}
794+
}
795+
'''
796+
expected = {
797+
'articles': {
798+
'edges': [
799+
{
800+
'node': {
801+
'headline': 'World',
802+
'editor': {
803+
'firstName': 'Grant'
804+
},
805+
'pubDate': '2020-01-01T00:00:00'
806+
}
807+
}
808+
]
809+
}
810+
}
811+
schema = graphene.Schema(query=Query)
812+
result = schema.execute(query)
813+
assert not result.errors
814+
assert json.dumps(result.data, sort_keys=True) == json.dumps(
815+
expected, sort_keys=True)
816+
817+
818+
def test_should_get_queryset_returns_qs_filters(fixtures):
819+
820+
def get_queryset(model, info, **args):
821+
return model.objects(headline="World")
822+
823+
class Query(graphene.ObjectType):
824+
node = Node.Field()
825+
articles = MongoengineConnectionField(ArticleNode, get_queryset=get_queryset)
826+
827+
query = '''
828+
query ArticlesQuery {
829+
articles {
830+
edges {
831+
node {
832+
headline,
833+
pubDate,
834+
editor {
835+
firstName
836+
}
837+
}
838+
}
839+
}
840+
}
841+
'''
842+
expected = {
843+
'articles': {
844+
'edges': [
845+
{
846+
'node': {
847+
'headline': 'World',
848+
'editor': {
849+
'firstName': 'Grant'
850+
},
851+
'pubDate': '2020-01-01T00:00:00'
852+
}
853+
}
854+
]
855+
}
856+
}
857+
schema = graphene.Schema(query=Query)
858+
result = schema.execute(query)
859+
assert not result.errors
860+
assert json.dumps(result.data, sort_keys=True) == json.dumps(
861+
expected, sort_keys=True)

graphene_mongo/tests/types.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import graphene
12
from graphene.relay import Node
23

34
from ..types import MongoengineObjectType
@@ -6,7 +7,7 @@
67
Parent, Child, ProfessorMetadata, ProfessorVector,
78
ParentWithRelationship, ChildRegisteredBefore,
89
ChildRegisteredAfter, CellTower,
9-
Publisher)
10+
Publisher, ErroneousModel)
1011

1112

1213
class PublisherType(MongoengineObjectType):
@@ -76,9 +77,12 @@ class Meta:
7677

7778

7879
class PublisherNode(MongoengineObjectType):
80+
legal_name = graphene.String()
81+
bad_field = graphene.String()
7982

8083
class Meta:
8184
model = Publisher
85+
only_fields = ('id', 'name')
8286
interfaces = (Node,)
8387

8488

@@ -157,3 +161,9 @@ class ProfessorVectorNode(MongoengineObjectType):
157161
class Meta:
158162
model = ProfessorVector
159163
interfaces = (Node, )
164+
165+
166+
class ErroneousModelNode(MongoengineObjectType):
167+
class Meta:
168+
model = ErroneousModel
169+
interfaces = (Node,)

graphene_mongo/utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import mongoengine
77
from graphene import Node
88
from graphene.utils.trim_docstring import trim_docstring
9-
from graphql_relay import from_global_id
109

1110

1211
def get_model_fields(model, excluding=None):
@@ -91,10 +90,10 @@ def get_field_description(field, registry=None):
9190
return "\n".join(parts)
9291

9392

94-
def global_id_via_node(node, _id):
93+
def get_node_from_global_id(node, info, global_id):
9594
try:
9695
for interface in node._meta.interfaces:
9796
if issubclass(interface, Node):
98-
return interface.from_global_id(_id)
97+
return interface.get_node_from_global_id(info, global_id)
9998
except AttributeError:
100-
return from_global_id(_id)
99+
return Node.get_node_from_global_id(info, global_id)

0 commit comments

Comments
 (0)