Collection: add content support.

This commit is contained in:
Tom Hacohen 2020-03-12 21:02:27 +02:00
parent c56cbb3f82
commit 66e5062461
6 changed files with 154 additions and 37 deletions

View File

@ -0,0 +1,19 @@
# Generated by Django 3.0.3 on 2020-03-12 14:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_etesync', '0026_collectionitemrevision_meta'),
]
operations = [
migrations.AddField(
model_name='collection',
name='mainItem',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='of_collection', to='django_etesync.CollectionItem'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 3.0.3 on 2020-03-12 18:19
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('django_etesync', '0027_collection_mainitem'),
]
operations = [
migrations.AlterField(
model_name='collectionitem',
name='encryptionKey',
field=models.BinaryField(editable=True, null=True),
),
migrations.AlterField(
model_name='collectionitem',
name='uid',
field=models.CharField(db_index=True, max_length=44, null=True, validators=[django.core.validators.RegexValidator(message='Not a valid UID', regex='[a-zA-Z0-9]')]),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 3.0.3 on 2020-03-12 18:49
from django.db import migrations
from django_etesync.serializers import generate_rev_uid
def add_collection_main_item(apps, schema_editor):
Collection = apps.get_model('django_etesync', 'Collection')
CollectionItem = apps.get_model('django_etesync', 'CollectionItem')
CollectionItemRevision = apps.get_model('django_etesync', 'CollectionItemRevision')
for col in Collection.objects.all():
main_item = CollectionItem.objects.create(uid=None, encryptionKey=None, version=col.version, collection=col)
col.mainItem = main_item
col.save()
CollectionItemRevision.objects.create(
uid=generate_rev_uid(),
hmac='hmac-hash',
item=main_item)
class Migration(migrations.Migration):
dependencies = [
('django_etesync', '0028_auto_20200312_1819'),
]
operations = [
migrations.RunPython(add_collection_main_item),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.0.3 on 2020-03-12 18:59
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('django_etesync', '0029_auto_20200312_1849'),
]
operations = [
migrations.AlterField(
model_name='collection',
name='mainItem',
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='of_collection', to='django_etesync.CollectionItem'),
),
]

View File

@ -29,6 +29,7 @@ class Collection(models.Model):
max_length=44, validators=[UidValidator])
version = models.PositiveSmallIntegerField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
mainItem = models.OneToOneField('CollectionItem', related_name='of_collection', on_delete=models.PROTECT)
class Meta:
unique_together = ('uid', 'owner')
@ -36,19 +37,23 @@ class Collection(models.Model):
def __str__(self):
return self.uid
@cached_property
def content(self):
return self.mainItem.content
class CollectionItem(models.Model):
uid = models.CharField(db_index=True, blank=False, null=False,
uid = models.CharField(db_index=True, blank=False, null=True,
max_length=44, validators=[UidValidator])
collection = models.ForeignKey(Collection, related_name='items', on_delete=models.CASCADE)
version = models.PositiveSmallIntegerField()
encryptionKey = models.BinaryField(editable=True, blank=False, null=False)
encryptionKey = models.BinaryField(editable=True, blank=False, null=True)
class Meta:
unique_together = ('uid', 'collection')
def __str__(self):
return self.uid
return '{} {}'.format(self.uid, self.collection.uid)
@cached_property
def content(self):

View File

@ -43,44 +43,13 @@ class CollectionEncryptionKeyField(BinaryBase64Field):
return None
class CollectionSerializer(serializers.ModelSerializer):
encryptionKey = CollectionEncryptionKeyField()
accessLevel = serializers.SerializerMethodField('get_access_level_from_context')
ctag = serializers.SerializerMethodField('get_ctag')
class Meta:
model = models.Collection
fields = ('uid', 'version', 'accessLevel', 'encryptionKey', 'ctag')
def get_access_level_from_context(self, obj):
class CollectionContentField(BinaryBase64Field):
def get_attribute(self, instance):
request = self.context.get('request', None)
if request is not None:
return obj.members.get(user=request.user).accessLevel
return instance.members.get(user=request.user).encryptionKey
return None
def get_ctag(self, obj):
last_revision = models.CollectionItemRevision.objects.filter(item__collection=obj).last()
if last_revision is None:
# FIXME: what is the etag for None? Though if we use the revision for collection it should be shared anyway.
return None
return last_revision.uid
def create(self, validated_data):
"""Function that's called when this serializer creates an item"""
encryption_key = validated_data.pop('encryptionKey')
instance = self.__class__.Meta.model(**validated_data)
with transaction.atomic():
instance.save()
models.CollectionMember(collection=instance,
user=validated_data.get('owner'),
accessLevel=models.CollectionMember.AccessLevels.ADMIN,
encryptionKey=encryption_key,
).save()
return instance
class CollectionItemChunkSerializer(serializers.ModelSerializer):
class Meta:
@ -177,3 +146,53 @@ class CollectionItemSerializer(serializers.ModelSerializer):
class CollectionItemInlineSerializer(CollectionItemSerializer):
content = CollectionItemRevisionInlineSerializer(read_only=True, many=False)
class CollectionSerializer(serializers.ModelSerializer):
encryptionKey = CollectionEncryptionKeyField()
accessLevel = serializers.SerializerMethodField('get_access_level_from_context')
ctag = serializers.SerializerMethodField('get_ctag')
content = CollectionItemRevisionSerializer(many=False)
class Meta:
model = models.Collection
fields = ('uid', 'version', 'accessLevel', 'encryptionKey', 'content', 'ctag')
def get_access_level_from_context(self, obj):
request = self.context.get('request', None)
if request is not None:
return obj.members.get(user=request.user).accessLevel
return None
def get_ctag(self, obj):
last_revision = models.CollectionItemRevision.objects.filter(item__collection=obj).last()
if last_revision is None:
# FIXME: what is the etag for None? Though if we use the revision for collection it should be shared anyway.
return None
return last_revision.uid
def create(self, validated_data):
"""Function that's called when this serializer creates an item"""
revision_data = validated_data.pop('content')
encryption_key = validated_data.pop('encryptionKey')
instance = self.__class__.Meta.model(**validated_data)
with transaction.atomic():
main_item = models.CollectionItem.objects.create(
uid=None, encryptionKey=None, version=instance.version, collection=instance)
instance.mainItem = main_item
chunks = revision_data.pop('chunks')
revision = models.CollectionItemRevision.objects.create(**revision_data, uid=generate_rev_uid(),
item=main_item)
revision.chunks.set(chunks)
instance.save()
models.CollectionMember(collection=instance,
user=validated_data.get('owner'),
accessLevel=models.CollectionMember.AccessLevels.ADMIN,
encryptionKey=encryption_key,
).save()
return instance