Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 8f8c5d2

Browse files
authored
Merge pull request #159 from ambitioninc/develop
5.0.1
2 parents 4fb07b3 + 9f4db1e commit 8f8c5d2

File tree

5 files changed

+116
-8
lines changed

5 files changed

+116
-8
lines changed

entity/sync.py

+24
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,28 @@ def sync(self):
231231
# IDs of sub/super entity relationships
232232
super_entities_by_ctype = _get_super_entities_by_ctype(model_objs_by_ctype, model_ids_to_sync, sync_all)
233233

234+
# Handle the case where accounts are created before _get_super_entities_by_ctype and
235+
# the model_ids_to_sync do not match the model_objs_map
236+
for ctype, model_ids in model_ids_to_sync.items():
237+
238+
# Build a set of ids of already fetched models
239+
ids_of_fetched_models = {
240+
model.id
241+
for model in model_objs_by_ctype[ctype]
242+
}
243+
244+
# Compute the set diff to see if any new records were created
245+
created_model_ids = model_ids - ids_of_fetched_models
246+
247+
# Check if new records
248+
if created_model_ids:
249+
250+
# Fetch the records and add them to the model_objs_map
251+
new_records = ctype.model_class().objects.filter(id__in=created_model_ids)
252+
for new_record in new_records:
253+
model_objs_by_ctype[ctype].append(new_record)
254+
model_objs_map[(ctype, new_record.id)] = new_record
255+
234256
# Now that we have all models we need to sync, fetch them so that we can extract
235257
# metadata and entity kinds. If we are syncing all entities, we've already fetched
236258
# everything and can fill in this data struct without doing another DB hit
@@ -300,6 +322,7 @@ def sync(self):
300322
for sub_ctype, super_entities_by_sub_ctype in super_entities_by_ctype.items()
301323
for super_ctype, relationships in super_entities_by_sub_ctype.items()
302324
for sub_entity_id, super_entity_id in relationships
325+
if (sub_ctype.id, sub_entity_id) in entities_map and (super_ctype.id, super_entity_id) in entities_map
303326
]
304327

305328
# Find the entities of the original model objects we were syncing. These
@@ -308,6 +331,7 @@ def sync(self):
308331
entities_map[ctype.id, model_obj.id].id
309332
for ctype, model_objs_for_ctype in model_objs_by_ctype.items()
310333
for model_obj in model_objs_for_ctype
334+
if (ctype.id, model_obj.id) in entities_map
311335
]
312336

313337
if self.sync_all:

entity/tests/models.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,19 @@ def get_super_entities(self, model_objs, sync_all):
146146
"""
147147
Gets the super entities this entity belongs to.
148148
"""
149+
if sync_all:
150+
accounts = list(self.queryset.all())
151+
if not sync_all:
152+
accounts = model_objs
153+
149154
return {
150155
Team: [
151-
(a.id, a.team_id) for a in model_objs if a.team_id
156+
(a.id, a.team_id) for a in accounts if a.team_id
152157
] + [
153-
(a.id, a.team2_id) for a in model_objs if a.team2_id
158+
(a.id, a.team2_id) for a in accounts if a.team2_id
154159
],
155-
TeamGroup: [(a.id, a.team_group_id) for a in model_objs if a.team_group_id],
156-
Competitor: [(a.id, a.competitor_id) for a in model_objs if a.competitor_id]
160+
TeamGroup: [(a.id, a.team_group_id) for a in accounts if a.team_group_id],
161+
Competitor: [(a.id, a.competitor_id) for a in accounts if a.competitor_id]
157162
}
158163

159164

entity/tests/sync_tests.py

+80-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from django_dynamic_fixture import G
88
from entity.config import EntityRegistry
99
from entity.models import Entity, EntityRelationship, EntityKind
10-
from entity.sync import sync_entities, defer_entity_syncing, transaction_atomic_with_retry
10+
from entity.sync import sync_entities, defer_entity_syncing, transaction_atomic_with_retry, _get_super_entities_by_ctype
1111
from entity.signal_handlers import turn_on_syncing, turn_off_syncing
1212
from mock import patch, MagicMock, call
1313

@@ -250,9 +250,86 @@ def test_sync_all_accounts_teams(self):
250250
# There should be four entity relationships since four accounts have teams
251251
self.assertEquals(EntityRelationship.objects.all().count(), 4)
252252

253+
def test_sync_all_accounts_teams_new_account_during_sync(self):
254+
"""
255+
Tests the scenario of a new account being created after account ids are fetched but before the super
256+
entities are fetched
257+
"""
258+
# Create five test accounts
259+
accounts = [Account.objects.create() for i in range(5)]
260+
# Create two teams to assign to some of the accounts
261+
teams = [Team.objects.create() for i in range(2)]
262+
accounts[0].team = teams[0]
263+
accounts[0].save()
264+
accounts[1].team = teams[0]
265+
accounts[1].save()
266+
accounts[2].team = teams[1]
267+
accounts[2].save()
268+
accounts[3].team = teams[1]
269+
accounts[3].save()
270+
271+
def wrapped_super_entities(*args, **kwargs):
272+
if not Account.objects.filter(email='[email protected]').exists():
273+
Account.objects.create(
274+
275+
team=Team.objects.order_by('id')[0],
276+
team2=Team.objects.order_by('id')[1],
277+
)
278+
279+
return _get_super_entities_by_ctype(*args, **kwargs)
280+
281+
# Sync all the entities. There should be 8 (6 accounts 2 teams)
282+
with patch('entity.sync._get_super_entities_by_ctype', wraps=wrapped_super_entities):
283+
sync_entities()
284+
285+
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Account)).count(), 6)
286+
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Team)).count(), 2)
287+
self.assertEquals(Entity.objects.all().count(), 8)
288+
289+
# There should be six entity relationships
290+
self.assertEquals(EntityRelationship.objects.all().count(), 6)
291+
292+
def test_sync_all_accounts_teams_deleted_account_during_sync(self):
293+
"""
294+
Tests the scenario of an account being deleted after account ids are fetched but before the super
295+
entities are fetched
296+
"""
297+
# Create five test accounts
298+
accounts = [Account.objects.create() for i in range(5)]
299+
# Create two teams to assign to some of the accounts
300+
teams = [Team.objects.create() for i in range(2)]
301+
accounts[0].team = teams[0]
302+
accounts[0].email = '[email protected]'
303+
accounts[0].save()
304+
accounts[1].team = teams[0]
305+
accounts[1].save()
306+
accounts[2].team = teams[1]
307+
accounts[2].save()
308+
accounts[3].team = teams[1]
309+
accounts[3].save()
310+
311+
def wrapped_super_entities(*args, **kwargs):
312+
if Account.objects.filter(email='[email protected]').exists():
313+
Account.objects.filter(email='[email protected]').delete()
314+
315+
return _get_super_entities_by_ctype(*args, **kwargs)
316+
317+
# Sync all the entities. There should be 6 (4 accounts 2 teams)
318+
with patch('entity.sync._get_super_entities_by_ctype', wraps=wrapped_super_entities):
319+
sync_entities(*accounts)
320+
# Sync again to hit other wrapped function branch and make sure it doesn't error
321+
sync_entities(*accounts)
322+
323+
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Account)).count(), 4)
324+
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Team)).count(), 2)
325+
self.assertEquals(Entity.objects.all().count(), 6)
326+
327+
# There should be six entity relationships
328+
self.assertEquals(EntityRelationship.objects.all().count(), 3)
329+
253330
def test_sync_all_accounts_teams_inactive_entity_kind(self):
254331
"""
255-
Tests syncing of all accounts when they have super entities and the entiity kind is inactive
332+
Tests syncing of all accounts when they have super entities and the entity kind is inactive
256333
"""
257334
# Create five test accounts
258335
accounts = [Account.objects.create() for i in range(5)]
@@ -905,7 +982,7 @@ def test_sync_all_optimal_queries(self):
905982
with patch('entity.sync.entity_registry') as mock_entity_registry:
906983
mock_entity_registry.entity_registry = new_registry.entity_registry
907984
ContentType.objects.clear_cache()
908-
with self.assertNumQueries(19):
985+
with self.assertNumQueries(20):
909986
sync_entities()
910987

911988
self.assertEquals(Entity.objects.filter(entity_type=ContentType.objects.get_for_model(Account)).count(), 5)

entity/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '5.0.0'
1+
__version__ = '5.0.1'

release_notes.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Release Notes
22

3+
- 5.0.1:
4+
- Fix bug where an entity created or deleted during a sync would cause errors
35
- 5.0.0:
46
- Django 3.0, Django 3.1
57
- Drop Django 2.0, 2.1 support

0 commit comments

Comments
 (0)