Gracefully handle uploading the same item twice.

We were failing until now, but since the uid is sure to be unique,
we can just assume that if it's the same uid it's the same content.
This means we can just gracefully fail as the data is the same.

Until now, we were raising an error, but we now just do nothing
and consider it a success.

This is especially useful when a network error caused an item to
be uploaded but not updated on the client side.
This commit is contained in:
Tom Hacohen 2020-11-09 17:31:12 +02:00
parent e9de8f1adb
commit bdd787b915

View File

@ -30,6 +30,10 @@ User = get_user_model()
def process_revisions_for_item(item, revision_data): def process_revisions_for_item(item, revision_data):
chunks_objs = [] chunks_objs = []
chunks = revision_data.pop('chunks_relation') chunks = revision_data.pop('chunks_relation')
revision = models.CollectionItemRevision(**revision_data, item=item)
revision.validate_unique() # Verify there aren't any validation issues
for chunk in chunks: for chunk in chunks:
uid = chunk[0] uid = chunk[0]
chunk_obj = models.CollectionItemChunk.objects.filter(uid=uid).first() chunk_obj = models.CollectionItemChunk.objects.filter(uid=uid).first()
@ -47,8 +51,9 @@ def process_revisions_for_item(item, revision_data):
chunks_objs.append(chunk_obj) chunks_objs.append(chunk_obj)
stoken = models.Stoken.objects.create() stoken = models.Stoken.objects.create()
revision.stoken = stoken
revision.save()
revision = models.CollectionItemRevision.objects.create(**revision_data, item=item, stoken=stoken)
for chunk in chunks_objs: for chunk in chunks_objs:
models.RevisionChunkRelation.objects.create(chunk=chunk, revision=revision) models.RevisionChunkRelation.objects.create(chunk=chunk, revision=revision)
return revision return revision
@ -196,6 +201,9 @@ class CollectionItemRevisionSerializer(BetterErrorsMixin, serializers.ModelSeria
class Meta: class Meta:
model = models.CollectionItemRevision model = models.CollectionItemRevision
fields = ('chunks', 'meta', 'uid', 'deleted') fields = ('chunks', 'meta', 'uid', 'deleted')
extra_kwargs = {
'uid': {'validators': []}, # We deal with it in the serializers
}
class CollectionItemSerializer(BetterErrorsMixin, serializers.ModelSerializer): class CollectionItemSerializer(BetterErrorsMixin, serializers.ModelSerializer):
@ -220,6 +228,10 @@ class CollectionItemSerializer(BetterErrorsMixin, serializers.ModelSerializer):
instance, created = Model.objects.get_or_create(uid=uid, defaults=validated_data) instance, created = Model.objects.get_or_create(uid=uid, defaults=validated_data)
cur_etag = instance.etag if not created else None cur_etag = instance.etag if not created else None
# If we are trying to update an up to date item, abort early and consider it a success
if cur_etag == revision_data.get('uid'):
return instance
if validate_etag and cur_etag != etag: if validate_etag and cur_etag != etag:
raise EtebaseValidationError('wrong_etag', 'Wrong etag. Expected {} got {}'.format(cur_etag, etag), raise EtebaseValidationError('wrong_etag', 'Wrong etag. Expected {} got {}'.format(cur_etag, etag),
status_code=status.HTTP_409_CONFLICT) status_code=status.HTTP_409_CONFLICT)
@ -231,7 +243,10 @@ class CollectionItemSerializer(BetterErrorsMixin, serializers.ModelSerializer):
current_revision.current = None current_revision.current = None
current_revision.save() current_revision.save()
process_revisions_for_item(instance, revision_data) try:
process_revisions_for_item(instance, revision_data)
except django_exceptions.ValidationError as e:
self.transform_validation_error("content", e)
return instance return instance