Collection/item create/update require stoken.

This commit is contained in:
Tom Hacohen 2020-05-19 10:48:39 +03:00
parent bd1d11fe5f
commit 00a80740ca

View File

@ -112,18 +112,23 @@ class CollectionItemRevisionSerializer(serializers.ModelSerializer):
class CollectionItemSerializer(serializers.ModelSerializer): class CollectionItemSerializer(serializers.ModelSerializer):
encryptionKey = BinaryBase64Field() encryptionKey = BinaryBase64Field()
stoken = serializers.CharField(allow_null=True)
content = CollectionItemRevisionSerializer(many=False) content = CollectionItemRevisionSerializer(many=False)
class Meta: class Meta:
model = models.CollectionItem model = models.CollectionItem
fields = ('uid', 'version', 'encryptionKey', 'content') fields = ('uid', 'version', 'encryptionKey', 'content', 'stoken')
def create(self, validated_data): def create(self, validated_data):
"""Function that's called when this serializer creates an item""" """Function that's called when this serializer creates an item"""
stoken = validated_data.pop('stoken')
revision_data = validated_data.pop('content') revision_data = validated_data.pop('content')
instance = self.__class__.Meta.model(**validated_data) instance = self.__class__.Meta.model(**validated_data)
with transaction.atomic(): with transaction.atomic():
if stoken is not None:
raise serializers.ValidationError('Stoken is not None')
instance.save() instance.save()
process_revisions_for_item(instance, revision_data) process_revisions_for_item(instance, revision_data)
@ -132,9 +137,13 @@ class CollectionItemSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data): def update(self, instance, validated_data):
"""Function that's called when this serializer is meant to update an item""" """Function that's called when this serializer is meant to update an item"""
stoken = validated_data.pop('stoken')
revision_data = validated_data.pop('content') revision_data = validated_data.pop('content')
with transaction.atomic(): with transaction.atomic():
if stoken != instance.stoken:
raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(instance.stoken, stoken))
# We don't have to use select_for_update here because the unique constraint on current guards against # We don't have to use select_for_update here because the unique constraint on current guards against
# the race condition. But it's a good idea because it'll lock and wait rather than fail. # the race condition. But it's a good idea because it'll lock and wait rather than fail.
current_revision = instance.revisions.filter(current=True).select_for_update().first() current_revision = instance.revisions.filter(current=True).select_for_update().first()
@ -149,7 +158,7 @@ class CollectionItemSerializer(serializers.ModelSerializer):
class CollectionSerializer(serializers.ModelSerializer): class CollectionSerializer(serializers.ModelSerializer):
encryptionKey = CollectionEncryptionKeyField() encryptionKey = CollectionEncryptionKeyField()
accessLevel = serializers.SerializerMethodField('get_access_level_from_context') accessLevel = serializers.SerializerMethodField('get_access_level_from_context')
stoken = serializers.CharField(read_only=True) stoken = serializers.CharField(allow_null=True)
content = CollectionItemRevisionSerializer(many=False) content = CollectionItemRevisionSerializer(many=False)
class Meta: class Meta:
@ -164,11 +173,15 @@ class CollectionSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
"""Function that's called when this serializer creates an item""" """Function that's called when this serializer creates an item"""
stoken = validated_data.pop('stoken')
revision_data = validated_data.pop('content') revision_data = validated_data.pop('content')
encryption_key = validated_data.pop('encryptionKey') encryption_key = validated_data.pop('encryptionKey')
instance = self.__class__.Meta.model(**validated_data) instance = self.__class__.Meta.model(**validated_data)
with transaction.atomic(): with transaction.atomic():
if stoken is not None:
raise serializers.ValidationError('Stoken is not None')
instance.save() instance.save()
main_item = models.CollectionItem.objects.create( main_item = models.CollectionItem.objects.create(
uid=None, encryptionKey=None, version=instance.version, collection=instance) uid=None, encryptionKey=None, version=instance.version, collection=instance)
@ -185,9 +198,13 @@ class CollectionSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data): def update(self, instance, validated_data):
"""Function that's called when this serializer is meant to update an item""" """Function that's called when this serializer is meant to update an item"""
stoken = validated_data.pop('stoken')
revision_data = validated_data.pop('content') revision_data = validated_data.pop('content')
with transaction.atomic(): with transaction.atomic():
if stoken != instance.stoken:
raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(instance.stoken, stoken))
main_item = instance.main_item main_item = instance.main_item
# We don't have to use select_for_update here because the unique constraint on current guards against # We don't have to use select_for_update here because the unique constraint on current guards against
# the race condition. But it's a good idea because it'll lock and wait rather than fail. # the race condition. But it's a good idea because it'll lock and wait rather than fail.