Collection: add content support.
This commit is contained in:
parent
c56cbb3f82
commit
66e5062461
19
django_etesync/migrations/0027_collection_mainitem.py
Normal file
19
django_etesync/migrations/0027_collection_mainitem.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
24
django_etesync/migrations/0028_auto_20200312_1819.py
Normal file
24
django_etesync/migrations/0028_auto_20200312_1819.py
Normal 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]')]),
|
||||||
|
),
|
||||||
|
]
|
31
django_etesync/migrations/0029_auto_20200312_1849.py
Normal file
31
django_etesync/migrations/0029_auto_20200312_1849.py
Normal 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),
|
||||||
|
]
|
19
django_etesync/migrations/0030_auto_20200312_1859.py
Normal file
19
django_etesync/migrations/0030_auto_20200312_1859.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
@ -29,6 +29,7 @@ class Collection(models.Model):
|
|||||||
max_length=44, validators=[UidValidator])
|
max_length=44, validators=[UidValidator])
|
||||||
version = models.PositiveSmallIntegerField()
|
version = models.PositiveSmallIntegerField()
|
||||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
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:
|
class Meta:
|
||||||
unique_together = ('uid', 'owner')
|
unique_together = ('uid', 'owner')
|
||||||
@ -36,19 +37,23 @@ class Collection(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.uid
|
return self.uid
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def content(self):
|
||||||
|
return self.mainItem.content
|
||||||
|
|
||||||
|
|
||||||
class CollectionItem(models.Model):
|
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])
|
max_length=44, validators=[UidValidator])
|
||||||
collection = models.ForeignKey(Collection, related_name='items', on_delete=models.CASCADE)
|
collection = models.ForeignKey(Collection, related_name='items', on_delete=models.CASCADE)
|
||||||
version = models.PositiveSmallIntegerField()
|
version = models.PositiveSmallIntegerField()
|
||||||
encryptionKey = models.BinaryField(editable=True, blank=False, null=False)
|
encryptionKey = models.BinaryField(editable=True, blank=False, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('uid', 'collection')
|
unique_together = ('uid', 'collection')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.uid
|
return '{} {}'.format(self.uid, self.collection.uid)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def content(self):
|
def content(self):
|
||||||
|
@ -43,44 +43,13 @@ class CollectionEncryptionKeyField(BinaryBase64Field):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class CollectionSerializer(serializers.ModelSerializer):
|
class CollectionContentField(BinaryBase64Field):
|
||||||
encryptionKey = CollectionEncryptionKeyField()
|
def get_attribute(self, instance):
|
||||||
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):
|
|
||||||
request = self.context.get('request', None)
|
request = self.context.get('request', None)
|
||||||
if request is not None:
|
if request is not None:
|
||||||
return obj.members.get(user=request.user).accessLevel
|
return instance.members.get(user=request.user).encryptionKey
|
||||||
return None
|
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 CollectionItemChunkSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -177,3 +146,53 @@ class CollectionItemSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class CollectionItemInlineSerializer(CollectionItemSerializer):
|
class CollectionItemInlineSerializer(CollectionItemSerializer):
|
||||||
content = CollectionItemRevisionInlineSerializer(read_only=True, many=False)
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user