collection.py: get by uid and fixed create

This commit is contained in:
Tal Leibman 2020-12-27 11:36:18 +02:00 committed by Tom Hacohen
parent 94161943ca
commit 53662451a3

View File

@ -7,8 +7,8 @@ from django.core.files.base import ContentFile
from django.db import transaction from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.db.models import QuerySet from django.db.models import QuerySet
from fastapi import APIRouter, Depends, status from fastapi import APIRouter, Depends, status, Query
from pydantic import BaseModel from pydantic import BaseModel, Field
from django_etebase import models from django_etebase import models
from django_etebase.models import Collection, AccessLevels, CollectionMember from django_etebase.models import Collection, AccessLevels, CollectionMember
@ -22,12 +22,57 @@ collection_router = APIRouter(route_class=MsgpackRoute)
default_queryset = Collection.objects.all() default_queryset = Collection.objects.all()
Prefetch = t.Literal["auto", "medium"]
PrefetchQuery = Query(default="auto")
class ListMulti(BaseModel): class ListMulti(BaseModel):
collectionTypes: t.List[bytes] collectionTypes: t.List[bytes]
class CollectionItemRevisionOut(BaseModel):
uid: str
meta: bytes
deleted: bool
chunks: t.List[t.Tuple[str, t.Optional[bytes]]]
class Config:
orm_mode = True
@classmethod
def from_orm_user(
cls: t.Type["CollectionItemRevisionOut"], obj: models.CollectionItemRevision, prefetch: Prefetch
) -> "CollectionItemRevisionOut":
chunk_obj = obj.chunks_relation.get().chunk
if prefetch == "auto":
with open(chunk_obj.chunkFile.path, "rb") as f:
chunks = chunk_obj.uid, f.read()
else:
chunks = (chunk_obj.uid,)
return cls(uid=obj.uid, meta=obj.meta, deleted=obj.deleted, chunks=[chunks])
class CollectionItemOut(BaseModel): class CollectionItemOut(BaseModel):
uid: str uid: str
version: int
encryptionKey: t.Optional[bytes]
etag: t.Optional[str]
content: CollectionItemRevisionOut
class Config:
orm_mode = True
@classmethod
def from_orm_user(
cls: t.Type["CollectionItemOut"], obj: models.CollectionItem, prefetch: Prefetch
) -> "CollectionItemOut":
return cls(
uid=obj.uid,
version=obj.version,
encryptionKey=obj.encryptionKey,
etag=obj.etag,
content=CollectionItemRevisionOut.from_orm_user(obj.content, prefetch),
)
class CollectionOut(BaseModel): class CollectionOut(BaseModel):
@ -38,16 +83,17 @@ class CollectionOut(BaseModel):
item: CollectionItemOut item: CollectionItemOut
@classmethod @classmethod
def from_orm_user(cls: t.Type["CollectionOut"], obj: Collection, user: User) -> "CollectionOut": def from_orm_user(cls: t.Type["CollectionOut"], obj: Collection, user: User, prefetch: Prefetch) -> "CollectionOut":
member: CollectionMember = obj.members.get(user=user) member: CollectionMember = obj.members.get(user=user)
collection_type = member.collectionType collection_type = member.collectionType
return cls( ret = cls(
collectionType=collection_type and collection_type.uid, collectionType=collection_type and collection_type.uid,
collectionKey=member.encryptionKey, collectionKey=member.encryptionKey,
accessLevel=member.accessLevel, accessLevel=member.accessLevel,
stoken=obj.stoken, stoken=obj.stoken,
item=CollectionItemOut(uid=obj.main_item.uid), item=CollectionItemOut.from_orm_user(obj.main_item, prefetch),
) )
return ret
class ListResponse(BaseModel): class ListResponse(BaseModel):
@ -56,11 +102,26 @@ class ListResponse(BaseModel):
done: bool done: bool
class ItemIn(BaseModel):
uid: str
version: int
etag: t.Optional[str]
content: CollectionItemRevisionOut
class CollectionIn(BaseModel):
collectionType: bytes
collectionKey: bytes
item: ItemIn
@sync_to_async @sync_to_async
def list_common(queryset: QuerySet, user: User, stoken: t.Optional[str], limit: int) -> MsgpackResponse: def list_common(
queryset: QuerySet, user: User, stoken: t.Optional[str], limit: int, prefetch: Prefetch
) -> MsgpackResponse:
result, new_stoken_obj, done = filter_by_stoken_and_limit(stoken, limit, queryset, Collection.stoken_annotation) result, new_stoken_obj, done = filter_by_stoken_and_limit(stoken, limit, queryset, Collection.stoken_annotation)
new_stoken = new_stoken_obj and new_stoken_obj.uid new_stoken = new_stoken_obj and new_stoken_obj.uid
data: t.List[CollectionOut] = [CollectionOut.from_orm_user(item, user) for item in queryset] data: t.List[CollectionOut] = [CollectionOut.from_orm_user(item, user, prefetch) for item in queryset]
ret = ListResponse(data=data, stoken=new_stoken, done=done) ret = ListResponse(data=data, stoken=new_stoken, done=done)
return MsgpackResponse(content=ret) return MsgpackResponse(content=ret)
@ -71,39 +132,22 @@ def get_collection_queryset(user: User, queryset: QuerySet) -> QuerySet:
@collection_router.post("/list_multi/") @collection_router.post("/list_multi/")
async def list_multi( async def list_multi(
data: ListMulti, stoken: t.Optional[str] = None, limit: int = 50, user: User = Depends(get_authenticated_user) data: ListMulti,
stoken: t.Optional[str] = None,
limit: int = 50,
user: User = Depends(get_authenticated_user),
prefetch: Prefetch = PrefetchQuery,
): ):
queryset = get_collection_queryset(user, default_queryset) queryset = get_collection_queryset(user, default_queryset)
# FIXME: Remove the isnull part once we attach collection types to all objects ("collection-type-migration") # FIXME: Remove the isnull part once we attach collection types to all objects ("collection-type-migration")
queryset = queryset.filter( queryset = queryset.filter(
Q(members__collectionType__uid__in=data.collectionTypes) | Q(members__collectionType__isnull=True) Q(members__collectionType__uid__in=data.collectionTypes) | Q(members__collectionType__isnull=True)
) )
response = await list_common(queryset, user, stoken, limit) response = await list_common(queryset, user, stoken, limit, prefetch)
return response return response
class CollectionItemRevision(BaseModel): def process_revisions_for_item(item: models.CollectionItem, revision_data: CollectionItemRevisionOut):
uid: str
meta: bytes
deleted: bool
chunks: t.List[t.Tuple[str, t.Optional[bytes]]]
class Item(BaseModel):
uid: str
version: int
etag: t.Optional[str]
encryptionKey: t.Optional[bytes]
content: CollectionItemRevision
class CollectionIn(BaseModel):
collectionType: bytes
collectionKey: bytes
item: Item
def process_revisions_for_item(item: models.CollectionItem, revision_data: CollectionItemRevision):
chunks_objs = [] chunks_objs = []
revision = models.CollectionItemRevision(**revision_data.dict(exclude={"chunks"}), item=item) revision = models.CollectionItemRevision(**revision_data.dict(exclude={"chunks"}), item=item)
@ -147,7 +191,7 @@ def _create(data: CollectionIn, user: User):
instance.save() instance.save()
main_item = models.CollectionItem.objects.create( main_item = models.CollectionItem.objects.create(
uid=data.item.uid, version=data.item.version, encryptionKey=data.item.encryptionKey, collection=instance uid=data.item.uid, version=data.item.version, collection=instance
) )
instance.main_item = main_item instance.main_item = main_item
@ -172,3 +216,10 @@ def _create(data: CollectionIn, user: User):
async def create(data: CollectionIn, user: User = Depends(get_authenticated_user)): async def create(data: CollectionIn, user: User = Depends(get_authenticated_user)):
await sync_to_async(_create)(data, user) await sync_to_async(_create)(data, user)
return MsgpackResponse({}, status_code=status.HTTP_201_CREATED) return MsgpackResponse({}, status_code=status.HTTP_201_CREATED)
@collection_router.get("/{uid}/")
def get_collection(uid: str, user: User = Depends(get_authenticated_user), prefetch: Prefetch = PrefetchQuery):
obj = get_collection_queryset(user, default_queryset).get(uid=uid)
ret = CollectionOut.from_orm_user(obj, user, prefetch)
return MsgpackResponse(ret)