Loading .gitlab-ci.yml +4 −2 Original line number Diff line number Diff line Loading @@ -67,12 +67,14 @@ service-coverage: - > docker run --rm -v $PWD:/reports $CONTAINER_RDM_URL/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA bash -c "poetry run pytest --cov=. --cov-fail-under=80 --cov-report=html:/reports/htmlcov --cov-report=xml:/reports/coverage.xml tests/unit " $CONTAINER_RDM_URL/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA bash -c "poetry run pytest --cov=. --cov-fail-under=75 --cov-report=html:/reports/htmlcov --cov-report=xml:/reports/coverage.xml tests/unit " - sed -i "s:<source>/code:<source>${CI_BUILDS_DIR}/${CI_PROJECT_PATH}:g" coverage.xml coverage: '/coverage: \d+.\d+%/' artifacts: reports: cobertura: coverage.xml coverage_report: coverage_format: cobertura path: cobertura.xml paths: - htmlcov/ tags: Loading .pre-commit-config.yaml +1 −1 Original line number Diff line number Diff line Loading @@ -68,6 +68,6 @@ repos: - id: system name: Coverage description: use pytest to run unit tests with coverage entry: poetry run pytest --cov=. --cov-fail-under=80 tests/unit entry: poetry run pytest --cov=. --cov-fail-under=75 tests/unit pass_filenames: false language: system No newline at end of file app/brokers/abstract.py +8 −1 Original line number Diff line number Diff line from abc import ABC, abstractmethod from fastapi.responses import StreamingResponse from app.common import ( CopyData, DownloadData, ObjectData, ObjectMetaResponse, Loading @@ -25,5 +28,9 @@ class RemoteDataBroker(ABC): pass @abstractmethod def download(self, data: DownloadData) -> None: def download(self, data: DownloadData) -> StreamingResponse: pass @abstractmethod def local_copy(self, data: CopyData) -> None: pass app/brokers/filesys/broker.py +41 −1 Original line number Diff line number Diff line import os import shutil import subprocess from pathlib import Path from typing import Generator, List from fastapi import HTTPException from fastapi.responses import StreamingResponse from app.auth import AuthBroker from app.brokers import RemoteDataBroker from app.common import ( CopyData, DownloadData, ObjectData, ObjectMetaResponse, Loading Loading @@ -60,7 +66,41 @@ class FilesysBroker(RemoteDataBroker): copy_complete(user_info, data.input_path, settings.rdb_storage_path + "/" + uid) return UploadResponse(uid=uid) def download(self, data: DownloadData) -> None: def __get_download_cmd(self, data: DownloadData) -> List[str]: token = data.token cmd = settings.rdb_cat_cmd cmd = cmd.replace("$token", token) if cmd: return cmd.split(" ") + [data.filename] return ["cat", data.filename] def download(self, data: DownloadData) -> StreamingResponse: command = self.__get_download_cmd(data) try: process = subprocess.Popen( command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) def generate() -> Generator: while True: assert process.stdout is not None chunk = process.stdout.read(4096) if not chunk: break yield chunk headers = { "Content-Disposition": f'attachment; filename="{os.path.basename(data.filename)}"' } return StreamingResponse( headers=headers, content=generate(), media_type="application/octet-stream", ) except Exception: raise HTTPException(status_code=500, detail="download failed") def local_copy(self, data: CopyData) -> None: user_info = self._authorize(data.token) copy_complete( user_info, Loading app/common.py +10 −3 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ from typing import Any, Optional from pydantic import BaseModel, validator # pylint: disable=E0213 # pylint: disable=no-self-argument class ObjectMetaResponse(BaseModel): Loading Loading @@ -54,8 +54,8 @@ class UploadData(BaseModel): return key class DownloadData(BaseModel): """This is a model for file download""" class CopyData(BaseModel): """This is a model for file copy""" uid: Optional[str] key: Optional[str] Loading @@ -71,6 +71,13 @@ class DownloadData(BaseModel): return key class DownloadData(BaseModel): """This is a model for file download""" filename: str token: str class UserInfo(BaseModel): """This is a model for user information extracted from an oidc token""" Loading Loading
.gitlab-ci.yml +4 −2 Original line number Diff line number Diff line Loading @@ -67,12 +67,14 @@ service-coverage: - > docker run --rm -v $PWD:/reports $CONTAINER_RDM_URL/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA bash -c "poetry run pytest --cov=. --cov-fail-under=80 --cov-report=html:/reports/htmlcov --cov-report=xml:/reports/coverage.xml tests/unit " $CONTAINER_RDM_URL/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA bash -c "poetry run pytest --cov=. --cov-fail-under=75 --cov-report=html:/reports/htmlcov --cov-report=xml:/reports/coverage.xml tests/unit " - sed -i "s:<source>/code:<source>${CI_BUILDS_DIR}/${CI_PROJECT_PATH}:g" coverage.xml coverage: '/coverage: \d+.\d+%/' artifacts: reports: cobertura: coverage.xml coverage_report: coverage_format: cobertura path: cobertura.xml paths: - htmlcov/ tags: Loading
.pre-commit-config.yaml +1 −1 Original line number Diff line number Diff line Loading @@ -68,6 +68,6 @@ repos: - id: system name: Coverage description: use pytest to run unit tests with coverage entry: poetry run pytest --cov=. --cov-fail-under=80 tests/unit entry: poetry run pytest --cov=. --cov-fail-under=75 tests/unit pass_filenames: false language: system No newline at end of file
app/brokers/abstract.py +8 −1 Original line number Diff line number Diff line from abc import ABC, abstractmethod from fastapi.responses import StreamingResponse from app.common import ( CopyData, DownloadData, ObjectData, ObjectMetaResponse, Loading @@ -25,5 +28,9 @@ class RemoteDataBroker(ABC): pass @abstractmethod def download(self, data: DownloadData) -> None: def download(self, data: DownloadData) -> StreamingResponse: pass @abstractmethod def local_copy(self, data: CopyData) -> None: pass
app/brokers/filesys/broker.py +41 −1 Original line number Diff line number Diff line import os import shutil import subprocess from pathlib import Path from typing import Generator, List from fastapi import HTTPException from fastapi.responses import StreamingResponse from app.auth import AuthBroker from app.brokers import RemoteDataBroker from app.common import ( CopyData, DownloadData, ObjectData, ObjectMetaResponse, Loading Loading @@ -60,7 +66,41 @@ class FilesysBroker(RemoteDataBroker): copy_complete(user_info, data.input_path, settings.rdb_storage_path + "/" + uid) return UploadResponse(uid=uid) def download(self, data: DownloadData) -> None: def __get_download_cmd(self, data: DownloadData) -> List[str]: token = data.token cmd = settings.rdb_cat_cmd cmd = cmd.replace("$token", token) if cmd: return cmd.split(" ") + [data.filename] return ["cat", data.filename] def download(self, data: DownloadData) -> StreamingResponse: command = self.__get_download_cmd(data) try: process = subprocess.Popen( command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) def generate() -> Generator: while True: assert process.stdout is not None chunk = process.stdout.read(4096) if not chunk: break yield chunk headers = { "Content-Disposition": f'attachment; filename="{os.path.basename(data.filename)}"' } return StreamingResponse( headers=headers, content=generate(), media_type="application/octet-stream", ) except Exception: raise HTTPException(status_code=500, detail="download failed") def local_copy(self, data: CopyData) -> None: user_info = self._authorize(data.token) copy_complete( user_info, Loading
app/common.py +10 −3 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ from typing import Any, Optional from pydantic import BaseModel, validator # pylint: disable=E0213 # pylint: disable=no-self-argument class ObjectMetaResponse(BaseModel): Loading Loading @@ -54,8 +54,8 @@ class UploadData(BaseModel): return key class DownloadData(BaseModel): """This is a model for file download""" class CopyData(BaseModel): """This is a model for file copy""" uid: Optional[str] key: Optional[str] Loading @@ -71,6 +71,13 @@ class DownloadData(BaseModel): return key class DownloadData(BaseModel): """This is a model for file download""" filename: str token: str class UserInfo(BaseModel): """This is a model for user information extracted from an oidc token""" Loading