mirror of
https://github.com/etesync/server
synced 2024-11-22 16:58:08 +00:00
Collections: add support for collection types.
We also added the field for invitations, as it's needed for collections to work.
This commit is contained in:
parent
acd22b9b47
commit
5d8a92f000
29
django_etebase/migrations/0031_auto_20201013_1336.py
Normal file
29
django_etebase/migrations/0031_auto_20201013_1336.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 3.1.1 on 2020-10-13 13:36
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('django_etebase', '0030_auto_20200922_0832'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='CollectionType',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('uid', models.BinaryField(db_index=True, editable=True)),
|
||||||
|
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='collectionmember',
|
||||||
|
name='collectionType',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='django_etebase.collectiontype'),
|
||||||
|
),
|
||||||
|
]
|
18
django_etebase/migrations/0032_auto_20201013_1409.py
Normal file
18
django_etebase/migrations/0032_auto_20201013_1409.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.1.1 on 2020-10-13 14:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('django_etebase', '0031_auto_20201013_1336'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='collectiontype',
|
||||||
|
name='uid',
|
||||||
|
field=models.BinaryField(db_index=True, editable=True, unique=True),
|
||||||
|
),
|
||||||
|
]
|
@ -30,6 +30,11 @@ from .exceptions import EtebaseValidationError
|
|||||||
UidValidator = RegexValidator(regex=r'^[a-zA-Z0-9\-_]{20,}$', message='Not a valid UID')
|
UidValidator = RegexValidator(regex=r'^[a-zA-Z0-9\-_]{20,}$', message='Not a valid UID')
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionType(models.Model):
|
||||||
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
|
uid = models.BinaryField(editable=True, blank=False, null=False, db_index=True, unique=True)
|
||||||
|
|
||||||
|
|
||||||
class Collection(models.Model):
|
class Collection(models.Model):
|
||||||
main_item = models.OneToOneField('CollectionItem', related_name='parent', null=True, on_delete=models.SET_NULL)
|
main_item = models.OneToOneField('CollectionItem', related_name='parent', null=True, on_delete=models.SET_NULL)
|
||||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
@ -162,6 +167,7 @@ class CollectionMember(models.Model):
|
|||||||
collection = models.ForeignKey(Collection, related_name='members', on_delete=models.CASCADE)
|
collection = models.ForeignKey(Collection, related_name='members', on_delete=models.CASCADE)
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
encryptionKey = models.BinaryField(editable=True, blank=False, null=False)
|
encryptionKey = models.BinaryField(editable=True, blank=False, null=False)
|
||||||
|
collectionType = models.ForeignKey(CollectionType, on_delete=models.PROTECT, null=True)
|
||||||
accessLevel = models.IntegerField(
|
accessLevel = models.IntegerField(
|
||||||
choices=AccessLevels.choices,
|
choices=AccessLevels.choices,
|
||||||
default=AccessLevels.READ_ONLY,
|
default=AccessLevels.READ_ONLY,
|
||||||
|
@ -86,6 +86,15 @@ class CollectionEncryptionKeyField(BinaryBase64Field):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionTypeField(BinaryBase64Field):
|
||||||
|
def get_attribute(self, instance):
|
||||||
|
request = self.context.get('request', None)
|
||||||
|
if request is not None:
|
||||||
|
collection_type = instance.members.get(user=request.user).collectionType
|
||||||
|
return collection_type and collection_type.uid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class UserSlugRelatedField(serializers.SlugRelatedField):
|
class UserSlugRelatedField(serializers.SlugRelatedField):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
view = self.context.get('view', None)
|
view = self.context.get('view', None)
|
||||||
@ -256,8 +265,15 @@ class CollectionItemBulkGetSerializer(BetterErrorsMixin, serializers.ModelSerial
|
|||||||
fields = ('uid', 'etag')
|
fields = ('uid', 'etag')
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionListMultiSerializer(BetterErrorsMixin, serializers.Serializer):
|
||||||
|
collectionTypes = serializers.ListField(
|
||||||
|
child=BinaryBase64Field()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CollectionSerializer(BetterErrorsMixin, serializers.ModelSerializer):
|
class CollectionSerializer(BetterErrorsMixin, serializers.ModelSerializer):
|
||||||
collectionKey = CollectionEncryptionKeyField()
|
collectionKey = CollectionEncryptionKeyField()
|
||||||
|
collectionType = CollectionTypeField()
|
||||||
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(read_only=True)
|
||||||
|
|
||||||
@ -265,7 +281,7 @@ class CollectionSerializer(BetterErrorsMixin, serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Collection
|
model = models.Collection
|
||||||
fields = ('item', 'accessLevel', 'collectionKey', 'stoken')
|
fields = ('item', 'accessLevel', 'collectionKey', 'collectionType', 'stoken')
|
||||||
|
|
||||||
def get_access_level_from_context(self, obj):
|
def get_access_level_from_context(self, obj):
|
||||||
request = self.context.get('request', None)
|
request = self.context.get('request', None)
|
||||||
@ -276,6 +292,7 @@ class CollectionSerializer(BetterErrorsMixin, 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"""
|
||||||
collection_key = validated_data.pop('collectionKey')
|
collection_key = validated_data.pop('collectionKey')
|
||||||
|
collection_type = validated_data.pop('collectionType')
|
||||||
|
|
||||||
main_item_data = validated_data.pop('main_item')
|
main_item_data = validated_data.pop('main_item')
|
||||||
etag = main_item_data.pop('etag')
|
etag = main_item_data.pop('etag')
|
||||||
@ -297,11 +314,16 @@ class CollectionSerializer(BetterErrorsMixin, serializers.ModelSerializer):
|
|||||||
|
|
||||||
process_revisions_for_item(main_item, revision_data)
|
process_revisions_for_item(main_item, revision_data)
|
||||||
|
|
||||||
|
user = validated_data.get('owner')
|
||||||
|
|
||||||
|
collection_type_obj, _ = models.CollectionType.objects.get_or_create(uid=collection_type, owner=user)
|
||||||
|
|
||||||
models.CollectionMember(collection=instance,
|
models.CollectionMember(collection=instance,
|
||||||
stoken=models.Stoken.objects.create(),
|
stoken=models.Stoken.objects.create(),
|
||||||
user=validated_data.get('owner'),
|
user=user,
|
||||||
accessLevel=models.AccessLevels.ADMIN,
|
accessLevel=models.AccessLevels.ADMIN,
|
||||||
encryptionKey=collection_key,
|
encryptionKey=collection_key,
|
||||||
|
collectionType=collection_type_obj,
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
@ -381,6 +403,7 @@ class CollectionInvitationSerializer(BetterErrorsMixin, serializers.ModelSeriali
|
|||||||
|
|
||||||
|
|
||||||
class InvitationAcceptSerializer(BetterErrorsMixin, serializers.Serializer):
|
class InvitationAcceptSerializer(BetterErrorsMixin, serializers.Serializer):
|
||||||
|
collectionType = BinaryBase64Field()
|
||||||
encryptionKey = BinaryBase64Field()
|
encryptionKey = BinaryBase64Field()
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@ -388,13 +411,18 @@ class InvitationAcceptSerializer(BetterErrorsMixin, serializers.Serializer):
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
invitation = self.context['invitation']
|
invitation = self.context['invitation']
|
||||||
encryption_key = validated_data.get('encryptionKey')
|
encryption_key = validated_data.get('encryptionKey')
|
||||||
|
collection_type = validated_data.pop('collectionType')
|
||||||
|
|
||||||
|
user = invitation.user
|
||||||
|
collection_type_obj, _ = models.CollectionType.objects.get_or_create(uid=collection_type, owner=user)
|
||||||
|
|
||||||
member = models.CollectionMember.objects.create(
|
member = models.CollectionMember.objects.create(
|
||||||
collection=invitation.collection,
|
collection=invitation.collection,
|
||||||
stoken=models.Stoken.objects.create(),
|
stoken=models.Stoken.objects.create(),
|
||||||
user=invitation.user,
|
user=user,
|
||||||
accessLevel=invitation.accessLevel,
|
accessLevel=invitation.accessLevel,
|
||||||
encryptionKey=encryption_key,
|
encryptionKey=encryption_key,
|
||||||
|
collectionType=collection_type_obj,
|
||||||
)
|
)
|
||||||
|
|
||||||
models.CollectionMemberRemoved.objects.filter(
|
models.CollectionMemberRemoved.objects.filter(
|
||||||
|
@ -66,6 +66,7 @@ from .serializers import (
|
|||||||
CollectionItemDepSerializer,
|
CollectionItemDepSerializer,
|
||||||
CollectionItemRevisionSerializer,
|
CollectionItemRevisionSerializer,
|
||||||
CollectionItemChunkSerializer,
|
CollectionItemChunkSerializer,
|
||||||
|
CollectionListMultiSerializer,
|
||||||
CollectionMemberSerializer,
|
CollectionMemberSerializer,
|
||||||
CollectionInvitationSerializer,
|
CollectionInvitationSerializer,
|
||||||
InvitationAcceptSerializer,
|
InvitationAcceptSerializer,
|
||||||
@ -210,6 +211,21 @@ class CollectionViewSet(BaseViewSet):
|
|||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
|
return self.list_common(request, queryset, *args, **kwargs)
|
||||||
|
|
||||||
|
@action_decorator(detail=False, methods=['POST'])
|
||||||
|
def list_multi(self, request, *args, **kwargs):
|
||||||
|
serializer = CollectionListMultiSerializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
|
collection_types = serializer.validated_data['collectionTypes']
|
||||||
|
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
queryset = queryset.filter(members__collectionType__uid__in=collection_types)
|
||||||
|
|
||||||
|
return self.list_common(request, queryset, *args, **kwargs)
|
||||||
|
|
||||||
|
def list_common(self, request, queryset, *args, **kwargs):
|
||||||
result, new_stoken_obj, done = self.filter_by_stoken_and_limit(request, queryset)
|
result, new_stoken_obj, done = self.filter_by_stoken_and_limit(request, queryset)
|
||||||
new_stoken = new_stoken_obj and new_stoken_obj.uid
|
new_stoken = new_stoken_obj and new_stoken_obj.uid
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user