Rename stoken to etag and cstoken to stoken.

This conforms better with what people know from HTTP and properly
differentiates from CSToken which is now renamed to stoken.
This commit is contained in:
Tom Hacohen 2020-05-27 10:09:45 +03:00
parent 8eee280bbb
commit 9c63f8d674
3 changed files with 67 additions and 67 deletions

View File

@ -46,11 +46,11 @@ class Collection(models.Model):
return self.main_item.content
@property
def stoken(self):
def etag(self):
return self.content.uid
@cached_property
def cstoken(self):
def stoken(self):
last_revision = CollectionItemRevision.objects.filter(item__collection=self).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.
@ -77,7 +77,7 @@ class CollectionItem(models.Model):
return self.revisions.get(current=True)
@property
def stoken(self):
def etag(self):
return self.content.uid

View File

@ -114,17 +114,17 @@ class CollectionItemRevisionSerializer(serializers.ModelSerializer):
class CollectionItemSerializer(serializers.ModelSerializer):
encryptionKey = BinaryBase64Field()
stoken = serializers.CharField(allow_null=True)
etag = serializers.CharField(allow_null=True)
content = CollectionItemRevisionSerializer(many=False)
class Meta:
model = models.CollectionItem
fields = ('uid', 'version', 'encryptionKey', 'content', 'stoken')
fields = ('uid', 'version', 'encryptionKey', 'content', 'etag')
def create(self, validated_data):
"""Function that's called when this serializer creates an item"""
validate_stoken = self.context.get('validate_stoken', False)
stoken = validated_data.pop('stoken')
validate_etag = self.context.get('validate_etag', False)
etag = validated_data.pop('etag')
revision_data = validated_data.pop('content')
uid = validated_data.pop('uid')
@ -132,10 +132,10 @@ class CollectionItemSerializer(serializers.ModelSerializer):
with transaction.atomic():
instance, created = Model.objects.get_or_create(uid=uid, defaults=validated_data)
cur_stoken = instance.stoken if not created else None
cur_etag = instance.etag if not created else None
if validate_stoken and cur_stoken != stoken:
raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(cur_stoken, stoken))
if validate_etag and cur_etag != etag:
raise serializers.ValidationError('Wrong etag. Expected {} got {}'.format(cur_etag, etag))
if not created:
# We don't have to use select_for_update here because the unique constraint on current guards against
@ -154,39 +154,39 @@ class CollectionItemSerializer(serializers.ModelSerializer):
class CollectionItemDepSerializer(serializers.ModelSerializer):
stoken = serializers.CharField()
etag = serializers.CharField()
class Meta:
model = models.CollectionItem
fields = ('uid', 'stoken')
fields = ('uid', 'etag')
def validate(self, data):
item = self.__class__.Meta.model.objects.get(uid=data['uid'])
stoken = data['stoken']
if item.stoken != stoken:
raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(item.stoken, stoken))
etag = data['etag']
if item.etag != etag:
raise serializers.ValidationError('Wrong etag. Expected {} got {}'.format(item.etag, etag))
return data
class CollectionItemBulkGetSerializer(serializers.ModelSerializer):
stoken = serializers.CharField(required=False)
etag = serializers.CharField(required=False)
class Meta:
model = models.CollectionItem
fields = ('uid', 'stoken')
fields = ('uid', 'etag')
class CollectionSerializer(serializers.ModelSerializer):
encryptionKey = CollectionEncryptionKeyField()
accessLevel = serializers.SerializerMethodField('get_access_level_from_context')
cstoken = serializers.CharField(read_only=True)
stoken = serializers.CharField(allow_null=True)
stoken = serializers.CharField(read_only=True)
etag = serializers.CharField(allow_null=True)
content = CollectionItemRevisionSerializer(many=False)
class Meta:
model = models.Collection
fields = ('uid', 'version', 'accessLevel', 'encryptionKey', 'content', 'cstoken', 'stoken')
fields = ('uid', 'version', 'accessLevel', 'encryptionKey', 'content', 'stoken', 'etag')
def get_access_level_from_context(self, obj):
request = self.context.get('request', None)
@ -196,13 +196,13 @@ class CollectionSerializer(serializers.ModelSerializer):
def create(self, validated_data):
"""Function that's called when this serializer creates an item"""
stoken = validated_data.pop('stoken')
etag = validated_data.pop('etag')
revision_data = validated_data.pop('content')
encryption_key = validated_data.pop('encryptionKey')
instance = self.__class__.Meta.model(**validated_data)
with transaction.atomic():
if stoken is not None:
if etag is not None:
raise serializers.ValidationError('Stoken is not None')
instance.save()
@ -221,12 +221,12 @@ class CollectionSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
"""Function that's called when this serializer is meant to update an item"""
stoken = validated_data.pop('stoken')
etag = validated_data.pop('etag')
revision_data = validated_data.pop('content')
with transaction.atomic():
if stoken != instance.stoken:
raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(instance.stoken, stoken))
if etag != instance.etag:
raise serializers.ValidationError('Wrong etag. Expected {} got {}'.format(instance.etag, etag))
main_item = instance.main_item
# We don't have to use select_for_update here because the unique constraint on current guards against

View File

@ -70,7 +70,7 @@ User = get_user_model()
class BaseViewSet(viewsets.ModelViewSet):
authentication_classes = tuple(app_settings.API_AUTHENTICATORS)
permission_classes = tuple(app_settings.API_PERMISSIONS)
cstoken_id_field = None
stoken_id_field = None
def get_serializer_class(self):
serializer_class = self.serializer_class
@ -84,43 +84,43 @@ class BaseViewSet(viewsets.ModelViewSet):
user = self.request.user
return queryset.filter(members__user=user)
def get_cstoken_obj(self, request):
cstoken = request.GET.get('cstoken', None)
def get_stoken_obj(self, request):
stoken = request.GET.get('stoken', None)
if cstoken is not None:
return get_object_or_404(Stoken.objects.all(), uid=cstoken)
if stoken is not None:
return get_object_or_404(Stoken.objects.all(), uid=stoken)
return None
def filter_by_cstoken(self, request, queryset):
cstoken_id_field = self.cstoken_id_field + '__id'
def filter_by_stoken(self, request, queryset):
stoken_id_field = self.stoken_id_field + '__id'
cstoken_rev = self.get_cstoken_obj(request)
if cstoken_rev is not None:
filter_by = {cstoken_id_field + '__gt': cstoken_rev.id}
stoken_rev = self.get_stoken_obj(request)
if stoken_rev is not None:
filter_by = {stoken_id_field + '__gt': stoken_rev.id}
queryset = queryset.filter(**filter_by)
return queryset, cstoken_rev
return queryset, stoken_rev
def get_queryset_cstoken(self, queryset):
cstoken_id_field = self.cstoken_id_field + '__id'
def get_queryset_stoken(self, queryset):
stoken_id_field = self.stoken_id_field + '__id'
new_cstoken_id = queryset.aggregate(cstoken_id=Max(cstoken_id_field))['cstoken_id']
new_cstoken = new_cstoken_id and Stoken.objects.get(id=new_cstoken_id).uid
new_stoken_id = queryset.aggregate(stoken_id=Max(stoken_id_field))['stoken_id']
new_stoken = new_stoken_id and Stoken.objects.get(id=new_stoken_id).uid
return queryset, new_cstoken
return queryset, new_stoken
def filter_by_cstoken_and_limit(self, request, queryset):
def filter_by_stoken_and_limit(self, request, queryset):
limit = int(request.GET.get('limit', 50))
queryset, cstoken_rev = self.filter_by_cstoken(request, queryset)
cstoken = cstoken_rev.uid if cstoken_rev is not None else None
queryset, stoken_rev = self.filter_by_stoken(request, queryset)
stoken = stoken_rev.uid if stoken_rev is not None else None
queryset = queryset[:limit]
queryset, new_cstoken = self.get_queryset_cstoken(queryset)
new_cstoken = new_cstoken or cstoken
queryset, new_stoken = self.get_queryset_stoken(queryset)
new_stoken = new_stoken or stoken
return queryset, new_cstoken
return queryset, new_stoken
class CollectionViewSet(BaseViewSet):
@ -129,7 +129,7 @@ class CollectionViewSet(BaseViewSet):
queryset = Collection.objects.all()
serializer_class = CollectionSerializer
lookup_field = 'uid'
cstoken_id_field = 'items__revisions__stoken'
stoken_id_field = 'items__revisions__stoken'
def get_queryset(self, queryset=None):
if queryset is None:
@ -172,13 +172,13 @@ class CollectionViewSet(BaseViewSet):
def list(self, request):
queryset = self.get_queryset()
queryset, new_cstoken = self.filter_by_cstoken_and_limit(request, queryset)
queryset, new_stoken = self.filter_by_stoken_and_limit(request, queryset)
serializer = self.serializer_class(queryset, context=self.get_serializer_context(), many=True)
ret = {
'data': serializer.data,
'cstoken': new_cstoken,
'stoken': new_stoken,
}
return Response(ret)
@ -189,7 +189,7 @@ class CollectionItemViewSet(BaseViewSet):
queryset = CollectionItem.objects.all()
serializer_class = CollectionItemSerializer
lookup_field = 'uid'
cstoken_id_field = 'revisions__stoken'
stoken_id_field = 'revisions__stoken'
def get_queryset(self):
collection_uid = self.kwargs['collection_uid']
@ -240,13 +240,13 @@ class CollectionItemViewSet(BaseViewSet):
def list(self, request, collection_uid=None):
queryset = self.get_queryset()
queryset, new_cstoken = self.filter_by_cstoken_and_limit(request, queryset)
queryset, new_stoken = self.filter_by_stoken_and_limit(request, queryset)
serializer = self.serializer_class(queryset, context=self.get_serializer_context(), many=True)
ret = {
'data': serializer.data,
'cstoken': new_cstoken,
'stoken': new_stoken,
}
return Response(ret)
@ -277,21 +277,21 @@ class CollectionItemViewSet(BaseViewSet):
'detail': 'Request has too many items. Limit: {}'. format(item_limit)}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
queryset, cstoken_rev = self.filter_by_cstoken(request, queryset)
queryset, stoken_rev = self.filter_by_stoken(request, queryset)
uids, stokens = zip(*[(item['uid'], item.get('stoken')) for item in serializer.validated_data])
revs = CollectionItemRevision.objects.filter(uid__in=stokens, current=True)
uids, etags = zip(*[(item['uid'], item.get('etag')) for item in serializer.validated_data])
revs = CollectionItemRevision.objects.filter(uid__in=etags, current=True)
queryset = queryset.filter(uid__in=uids).exclude(revisions__in=revs)
queryset, new_cstoken = self.get_queryset_cstoken(queryset)
cstoken = cstoken_rev and cstoken_rev.uid
new_cstoken = new_cstoken or cstoken
queryset, new_stoken = self.get_queryset_stoken(queryset)
stoken = stoken_rev and stoken_rev.uid
new_stoken = new_stoken or stoken
serializer = self.get_serializer_class()(queryset, context=self.get_serializer_context(), many=True)
ret = {
'data': serializer.data,
'cstoken': new_cstoken,
'stoken': new_stoken,
}
return Response(ret)
@ -299,11 +299,11 @@ class CollectionItemViewSet(BaseViewSet):
@action_decorator(detail=False, methods=['POST'])
def batch(self, request, collection_uid=None):
cstoken = request.GET.get('cstoken', None)
stoken = request.GET.get('stoken', None)
collection_object = get_object_or_404(self.get_collection_queryset(Collection.objects), uid=collection_uid)
if cstoken is not None and cstoken != collection_object.cstoken:
content = {'code': 'stale_cstoken', 'detail': 'CSToken is too old'}
if stoken is not None and stoken != collection_object.stoken:
content = {'code': 'stale_stoken', 'detail': 'Stoken is too old'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
items = request.data.get('items')
@ -331,18 +331,18 @@ class CollectionItemViewSet(BaseViewSet):
@action_decorator(detail=False, methods=['POST'])
def transaction(self, request, collection_uid=None):
cstoken = request.GET.get('cstoken', None)
stoken = request.GET.get('stoken', None)
collection_object = get_object_or_404(self.get_collection_queryset(Collection.objects), uid=collection_uid)
if cstoken is not None and cstoken != collection_object.cstoken:
content = {'code': 'stale_cstoken', 'detail': 'CSToken is too old'}
if stoken is not None and stoken != collection_object.stoken:
content = {'code': 'stale_stoken', 'detail': 'Stoken is too old'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
items = request.data.get('items')
deps = request.data.get('deps', None)
# FIXME: It should just be one serializer
context = self.get_serializer_context()
context.update({'validate_stoken': True})
context.update({'validate_etag': True})
serializer = self.get_serializer_class()(data=items, context=context, many=True)
deps_serializer = CollectionItemDepSerializer(data=deps, context=context, many=True)