Add deduplicator utility

This commit is contained in:
Jonathan Kim 2018-07-01 11:29:45 +01:00
parent 9efdf4c46e
commit cbcaac66d0
2 changed files with 308 additions and 0 deletions

View File

@ -0,0 +1,51 @@
from collections import defaultdict, Mapping, OrderedDict
def nested_dict():
return defaultdict(nested_dict)
def deflate(node, index=None, path=None):
if index is None:
index = {}
if path is None:
path = []
if node and 'id' in node and '__typename' in node:
route = ','.join(path)
if (
route in index and
node['__typename'] in index[route] and
index[route][node['__typename']].get(node['id'])
):
return {
'__typename': node['__typename'],
'id': node['id'],
}
else:
if route not in index:
index[route] = {}
if node['__typename'] not in index[route]:
index[route][node['__typename']] = {}
index[route][node['__typename']][node['id']] = True
field_names = node.keys()
result = OrderedDict()
for field_name in field_names:
value = node[field_name]
new_path = path + [field_name]
if isinstance(value, (list, tuple)):
result[field_name] = [
deflate(child, index, new_path) for child in value
]
elif isinstance(value, Mapping):
result[field_name] = deflate(value, index, new_path)
else:
result[field_name] = value
return result

View File

@ -0,0 +1,257 @@
import datetime
import graphene
from graphene import relay
from graphene.types.resolver import dict_resolver
from ..deduplicator import deflate
def test_does_not_modify_object_without_typename_and_id():
response = {
'foo': 'bar',
}
deflated_response = deflate(response)
assert deflated_response == {
'foo': 'bar',
}
def test_does_not_modify_first_instance_of_an_object():
response = {
'data': [
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
},
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
}
]
}
deflated_response = deflate(response)
assert deflated_response == {
'data': [
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
},
{
'__typename': 'foo',
'id': 1
}
]
}
def test_does_not_modify_first_instance_of_an_object_nested():
response = {
'data': [
{
'__typename': 'foo',
'bar1': {
'__typename': 'bar',
'id': 1,
'name': 'bar'
},
'bar2': {
'__typename': 'bar',
'id': 1,
'name': 'bar'
},
'id': 1
},
{
'__typename': 'foo',
'bar1': {
'__typename': 'bar',
'id': 1,
'name': 'bar'
},
'bar2': {
'__typename': 'bar',
'id': 1,
'name': 'bar'
},
'id': 2
}
]
}
deflated_response = deflate(response)
assert deflated_response == {
'data': [
{
'__typename': 'foo',
'bar1': {
'__typename': 'bar',
'id': 1,
'name': 'bar'
},
'bar2': {
'__typename': 'bar',
'id': 1,
'name': 'bar'
},
'id': 1
},
{
'__typename': 'foo',
'bar1': {
'__typename': 'bar',
'id': 1
},
'bar2': {
'__typename': 'bar',
'id': 1
},
'id': 2
}
]
}
def test_does_not_modify_input():
response = {
'data': [
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
},
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
}
]
}
deflate(response)
assert response == {
'data': [
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
},
{
'__typename': 'foo',
'id': 1,
'name': 'foo'
}
]
}
TEST_DATA = {
'events': [
{
'id': '568',
'date': datetime.date(2017, 5, 19),
'movie': '1198359',
},
{
'id': '234',
'date': datetime.date(2017, 5, 20),
'movie': '1198359',
},
],
'movies': {
'1198359': {
'name': 'King Arthur: Legend of the Sword',
'synopsis': (
"When the child Arthurs father is murdered, Vortigern, "
"Arthurs uncle, seizes the crown. Robbed of his birthright and "
"with no idea who he truly is..."
),
},
},
}
def test_example_end_to_end():
class Movie(graphene.ObjectType):
class Meta:
interfaces = (relay.Node,)
default_resolver = dict_resolver
name = graphene.String(required=True)
synopsis = graphene.String(required=True)
class Event(graphene.ObjectType):
class Meta:
interfaces = (relay.Node,)
default_resolver = dict_resolver
movie = graphene.Field(Movie, required=True)
date = graphene.types.datetime.Date(required=True)
def resolve_movie(event, info):
return TEST_DATA['movies'][event['movie']]
class Query(graphene.ObjectType):
events = graphene.List(
graphene.NonNull(Event),
required=True
)
def resolve_events(_, info):
return TEST_DATA['events']
schema = graphene.Schema(query=Query)
query = """\
{
events {
__typename
id
date
movie {
__typename
id
name
synopsis
}
}
}
"""
result = schema.execute(query)
assert not result.errors
result.data = deflate(result.data)
assert result.data == {
'events': [
{
'__typename': 'Event',
'id': 'RXZlbnQ6NTY4',
'date': '2017-05-19',
'movie': {
'__typename': 'Movie',
'id': 'TW92aWU6Tm9uZQ==',
'name': 'King Arthur: Legend of the Sword',
'synopsis': (
"When the child Arthurs father is murdered, Vortigern, "
"Arthurs uncle, seizes the crown. Robbed of his birthright and "
"with no idea who he truly is..."
),
},
},
{
'__typename': 'Event',
'id': 'RXZlbnQ6MjM0',
'date': '2017-05-20',
'movie': {
'__typename': 'Movie',
'id': 'TW92aWU6Tm9uZQ==',
},
},
],
}