From 2ae496d907702ba77a465c6ccc178bd7af405cb2 Mon Sep 17 00:00:00 2001 From: martinRenou Date: Wed, 27 Jul 2022 11:48:12 +0200 Subject: [PATCH] Update the way we store the multiple TOS languages --- plugins/quetz_tos/quetz_tos/api.py | 73 ++++++++++++++--------- plugins/quetz_tos/quetz_tos/db_models.py | 12 +++- plugins/quetz_tos/tests/conftest.py | 34 +++++------ plugins/quetz_tos/tests/test_quetz_tos.py | 68 ++++++--------------- 4 files changed, 87 insertions(+), 100 deletions(-) diff --git a/plugins/quetz_tos/quetz_tos/api.py b/plugins/quetz_tos/quetz_tos/api.py index 1d98b5160..d392e2b69 100644 --- a/plugins/quetz_tos/quetz_tos/api.py +++ b/plugins/quetz_tos/quetz_tos/api.py @@ -1,15 +1,16 @@ import os import uuid from tempfile import SpooledTemporaryFile +from typing import List -from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status +from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status, Query from sqlalchemy.orm.session import Session from quetz import authorization, dao from quetz.config import Config from quetz.deps import get_dao, get_db, get_rules -from .db_models import TermsOfService, TermsOfServiceSignatures +from .db_models import TermsOfServiceFile, TermsOfService, TermsOfServiceSignatures router = APIRouter() config = Config() @@ -34,34 +35,37 @@ def post_file(file): @router.get("/api/tos", tags=['Terms of Service']) -def get_current_tos(lang: str = "EN", db: Session = Depends(get_db)): - - terms_of_services = ( - db.query(TermsOfService).order_by(TermsOfService.time_created.desc()).all() +def get_current_tos(db: Session = Depends(get_db)): + current_tos = ( + db.query(TermsOfService).order_by(TermsOfService.time_created.desc()).first() ) - current_tos = None - for tos in terms_of_services: - if tos.language == lang: - current_tos = tos - break - if current_tos: - f = pkgstore.serve_path("root", current_tos.filename) - data_bytes = f.read() - return { - "id": str(uuid.UUID(bytes=current_tos.id)), - "content": data_bytes.decode('utf-8'), - "uploader_id": str(uuid.UUID(bytes=current_tos.uploader_id)), - "filename": current_tos.filename, - "language": current_tos.language, - "time_created": current_tos.time_created, - } - else: + if current_tos is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail="terms of service file not found", + detail=f"terms of service not found", ) + tos_files = [] + for tos_file in current_tos.files: + f = pkgstore.serve_path("root", tos_file.filename) + data_bytes = f.read() + + tos_files.append( + { + "content": data_bytes.decode('utf-8'), + "filename": tos_file.filename, + "language": tos_file.language, + } + ) + + return { + "id": str(uuid.UUID(bytes=current_tos.id)), + "uploader_id": str(uuid.UUID(bytes=current_tos.uploader_id)), + "files": tos_files, + "time_created": current_tos.time_created, + } + @router.get("/api/tos/status", status_code=201, tags=['Terms of Service']) def get_current_tos_status( @@ -162,15 +166,28 @@ def sign_current_tos( @router.post("/api/tos/upload", status_code=201, tags=['Terms of Service']) def upload_tos( - lang: str = "EN", + lang: List[str] = Query(...), db: Session = Depends(get_db), auth: authorization.Rules = Depends(get_rules), - tos_file: UploadFile = File(...), + tos_files: List[UploadFile] = File(...), ): user_id = auth.assert_server_roles( ["owner"], "To upload new Terms of Services you need to be a server owner." ) - filename = post_file(tos_file) - tos = TermsOfService(uploader_id=user_id, filename=filename, language=lang) + + if len(lang) != len(tos_files): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Number of languages '{lang}' does not match the number of uploaded files", + ) + + filenames = [post_file(tos_file) for tos_file in tos_files] + + tos_files = [ + TermsOfServiceFile(filename=filename, language=language) + for filename, language in zip(filenames, lang) + ] + tos = TermsOfService(uploader_id=user_id, files=tos_files) + db.add(tos) db.commit() diff --git a/plugins/quetz_tos/quetz_tos/db_models.py b/plugins/quetz_tos/quetz_tos/db_models.py index c8f000168..a0e644e52 100644 --- a/plugins/quetz_tos/quetz_tos/db_models.py +++ b/plugins/quetz_tos/quetz_tos/db_models.py @@ -6,13 +6,21 @@ from quetz.db_models import UUID, Base +class TermsOfServiceFile(Base): + __tablename__ = 'quetz_tos_file' + + id = Column(UUID, primary_key=True, default=lambda: uuid.uuid4().bytes) + filename = Column(String) + language = Column(String) + tos_id = Column(UUID, ForeignKey("quetz_tos.id"), primary_key=True) + + class TermsOfService(Base): __tablename__ = 'quetz_tos' id = Column(UUID, primary_key=True, default=lambda: uuid.uuid4().bytes) uploader_id = Column(UUID) - filename = Column(String) - language = Column(String) + files = relationship("TermsOfServiceFile") time_created = Column(DateTime, nullable=False, server_default=func.now()) diff --git a/plugins/quetz_tos/tests/conftest.py b/plugins/quetz_tos/tests/conftest.py index 14211f4d1..5a1ca843b 100644 --- a/plugins/quetz_tos/tests/conftest.py +++ b/plugins/quetz_tos/tests/conftest.py @@ -28,38 +28,32 @@ def member_user(db): @fixture def tos(db, owner_user): - tos = db_models.TermsOfService( - uploader_id=owner_user.id, filename="tos_en.txt", language="EN" - ) + tos_en = db_models.TermsOfServiceFile(filename="tos_en.txt", language="EN") + tos_fr = db_models.TermsOfServiceFile(filename="tos_fr.txt", language="FR") - tos2 = db_models.TermsOfService( - uploader_id=owner_user.id, filename="tos_fr.txt", language="FR" - ) + tos = db_models.TermsOfService(uploader_id=owner_user.id, files=[tos_en, tos_fr]) db.add(tos) - db.add(tos2) db.commit() - yield [tos, tos2] + yield tos @fixture def tos_file(config): pkgstore = config.get_package_store() - pkgstore.add_file(b"demo tos", "root", "tos_en.txt") - pkgstore.add_file(b"demo tos", "root", "tos_fr.txt") + pkgstore.add_file(b"demo tos en", "root", "tos_en.txt") + pkgstore.add_file(b"demo tos fr", "root", "tos_fr.txt") @fixture def tos_sign(db, tos, member_user): - all_signed_tos = [] - for a_tos in tos: - tos_sign = db_models.TermsOfServiceSignatures( - tos_id=a_tos.id, - user_id=member_user.id, - ) - - db.add(tos_sign) - all_signed_tos.append(tos_sign) + tos_sign = db_models.TermsOfServiceSignatures( + tos_id=tos.id, + user_id=member_user.id, + ) + + db.add(tos_sign) db.commit() - yield all_signed_tos + + yield tos_sign diff --git a/plugins/quetz_tos/tests/test_quetz_tos.py b/plugins/quetz_tos/tests/test_quetz_tos.py index d044f84c8..bb87e77fb 100644 --- a/plugins/quetz_tos/tests/test_quetz_tos.py +++ b/plugins/quetz_tos/tests/test_quetz_tos.py @@ -9,79 +9,48 @@ def plugins(): return ["quetz-tos"] -def upload_tos_en(client): - tos_en_filename = "tos_en.txt" - tos_en_content = "demo tos" - url = "/api/tos/upload?lang=EN" +def upload_tos(client): + url = "/api/tos/upload?lang=EN&lang=FR" - files_to_upload = {'tos_file': (tos_en_filename, io.StringIO(tos_en_content))} - - response = client.post(url, files=files_to_upload) - return response - - -def upload_tos_fr(client): - tos_fr_filename = "tos_fr.txt" - tos_fr_content = "demo tos" - url = "/api/tos/upload?lang=FR" - - files_to_upload = {'tos_file': (tos_fr_filename, io.StringIO(tos_fr_content))} + files_to_upload = ( + ('tos_files', ("tos_en.txt", io.StringIO("demo tos en"))), + ('tos_files', ("tos_fr.txt", io.StringIO("demo tos fr"))), + ) response = client.post(url, files=files_to_upload) return response -def test_tos_en_upload_by_member(client, member_user): - response = client.get("/api/dummylogin/alice") - assert response.status_code == 200 - - response = upload_tos_en(client) - assert response.status_code == 403 - assert response.json()['detail'] == [ - 'To upload new Terms of Services you need to be a server owner.' - ] - - -def test_tos_fr_upload_by_member(client, member_user): +def test_tos_upload_by_member(client, member_user): response = client.get("/api/dummylogin/alice") assert response.status_code == 200 - response = upload_tos_fr(client) + response = upload_tos(client) assert response.status_code == 403 assert response.json()['detail'] == [ 'To upload new Terms of Services you need to be a server owner.' ] -def test_tos_en_upload_by_owner(client, owner_user): - response = client.get("/api/dummylogin/madhurt") - assert response.status_code == 200 - - response = upload_tos_en(client) - assert response.status_code == 201 - assert response.content == b'null' - - -def test_tos_fr_upload_by_owner(client, owner_user): +def test_tos_upload_by_owner(client, owner_user): response = client.get("/api/dummylogin/madhurt") assert response.status_code == 200 - response = upload_tos_fr(client) + response = upload_tos(client) assert response.status_code == 201 assert response.content == b'null' -def test_get_tos_en(client, tos_file, tos): - response = client.get('/api/tos?lang=EN') - assert response.json()['filename'] == 'tos_en.txt' - assert response.json()['content'] == 'demo tos' - +def test_get_tos(client, tos_file, tos): + response = client.get('/api/tos') -def test_get_tos_fr(client, tos_file, tos): - response = client.get('/api/tos?lang=FR') + assert response.json()['files'][0]['language'] == 'EN' + assert response.json()['files'][0]['filename'] == 'tos_en.txt' + assert response.json()['files'][0]['content'] == 'demo tos en' - assert response.json()['filename'] == 'tos_fr.txt' - assert response.json()['content'] == 'demo tos' + assert response.json()['files'][1]['language'] == 'FR' + assert response.json()['files'][1]['filename'] == 'tos_fr.txt' + assert response.json()['files'][1]['content'] == 'demo tos fr' def test_tos_sign(client, member_user, tos_file, tos): @@ -133,7 +102,6 @@ def test_check_additional_permissions_hook_with_member( def test_check_additional_permissions_hook_with_member_signed( db, client, member_user, tos, tos_file, tos_sign ): - print(f"################################ member::: <{member_user}>") response = client.get("/api/dummylogin/alice") assert response.status_code == 200