refactor: use services layer and modularize transfer operations
Decouple CLI commands from internal helper logic and consolidate the core file transfer operations in the service layer to improve library portability. - Make MdrsClient subclass MdrsService to inherit resource resolution. - Remove all deprecated helper methods from BaseCommand. - Move core upload and download logic to a new transfer module. - Refactor all CLI commands to route actions through MdrsClient. - Eliminate circular imports between client and CLI command modules.
This commit is contained in:
+63
-85
@@ -1,69 +1,47 @@
|
||||
import os
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from typing import Any
|
||||
from unicodedata import normalize
|
||||
|
||||
from mdrsclient.api import DoiApi, FilesApi, FoldersApi, LaboratoriesApi, UsersApi
|
||||
from mdrsclient.commands.base import BaseCommand
|
||||
from mdrsclient.cache import CacheInterface
|
||||
from mdrsclient.connection import MDRSConnection
|
||||
from mdrsclient.exceptions import IllegalArgumentException, MDRSException, UnauthorizedException, UnexpectedException
|
||||
from mdrsclient.models import File, Folder, Laboratory, Token, User
|
||||
from mdrsclient.models.file import find_file
|
||||
from mdrsclient.settings import CONCURRENT
|
||||
from mdrsclient.services import MdrsService
|
||||
|
||||
|
||||
class MdrsClient:
|
||||
class MdrsClient(MdrsService):
|
||||
"""Service layer client for MDRS."""
|
||||
|
||||
def __init__(self, connection: MDRSConnection):
|
||||
self.connection = connection
|
||||
super().__init__(connection)
|
||||
|
||||
@classmethod
|
||||
def from_remote(cls, remote: str) -> "MdrsClient":
|
||||
return cls(BaseCommand._create_connection(remote))
|
||||
|
||||
def login(self, username: str, password: str) -> tuple[Token, User]:
|
||||
user_api = UsersApi(self.connection)
|
||||
token = user_api.token(username, password)
|
||||
self.connection.token = token
|
||||
user = user_api.current()
|
||||
self.connection.user = user
|
||||
return token, user
|
||||
|
||||
def logout(self) -> None:
|
||||
self.connection.logout()
|
||||
|
||||
def whoami(self) -> User:
|
||||
user_api = UsersApi(self.connection)
|
||||
return user_api.current()
|
||||
|
||||
def get_laboratories(self) -> list[Laboratory]:
|
||||
laboratory_api = LaboratoriesApi(self.connection)
|
||||
labs = laboratory_api.list()
|
||||
self.connection.laboratories = labs
|
||||
return list(labs)
|
||||
def from_remote(cls, remote: str, cache: CacheInterface | None = None) -> "MdrsClient":
|
||||
return cls(cls.create_connection(remote, cache))
|
||||
|
||||
def mkdir(self, remote_path: str) -> None:
|
||||
remote, laboratory_name, r_path = BaseCommand._parse_remote_host_with_path(remote_path)
|
||||
remote, laboratory_name, r_path = self.parse_remote_host_with_path(remote_path)
|
||||
r_path = r_path.rstrip("/")
|
||||
r_dirname = os.path.dirname(r_path)
|
||||
r_basename = os.path.basename(r_path)
|
||||
laboratory = BaseCommand._find_laboratory(self.connection, laboratory_name)
|
||||
parent_folder = BaseCommand._find_folder(self.connection, laboratory, r_dirname)
|
||||
files = BaseCommand._find_files(self.connection, parent_folder.id)
|
||||
laboratory = self.find_laboratory(laboratory_name)
|
||||
parent_folder = self.find_folder(laboratory, r_dirname)
|
||||
files = self.find_files(parent_folder.id)
|
||||
if parent_folder.find_sub_folder(r_basename) is not None or find_file(files, r_basename) is not None:
|
||||
raise IllegalArgumentException(f"Cannot create folder `{r_path}`: File exists.")
|
||||
folder_api = FoldersApi(self.connection)
|
||||
folder_api.create(normalize("NFC", r_basename), parent_folder.id)
|
||||
|
||||
def rm(self, remote_path: str, is_recursive: bool = False) -> None:
|
||||
remote, laboratory_name, r_path = BaseCommand._parse_remote_host_with_path(remote_path)
|
||||
remote, laboratory_name, r_path = self.parse_remote_host_with_path(remote_path)
|
||||
r_path = r_path.rstrip("/")
|
||||
r_dirname = os.path.dirname(r_path)
|
||||
r_basename = os.path.basename(r_path)
|
||||
laboratory = BaseCommand._find_laboratory(self.connection, laboratory_name)
|
||||
parent_folder = BaseCommand._find_folder(self.connection, laboratory, r_dirname)
|
||||
parent_files = BaseCommand._find_files(self.connection, parent_folder.id)
|
||||
laboratory = self.find_laboratory(laboratory_name)
|
||||
parent_folder = self.find_folder(laboratory, r_dirname)
|
||||
parent_files = self.find_files(parent_folder.id)
|
||||
file = find_file(parent_files, r_basename)
|
||||
if file is not None:
|
||||
file_api = FilesApi(self.connection)
|
||||
@@ -78,13 +56,13 @@ class MdrsClient:
|
||||
folder_api.destroy(folder.id, True)
|
||||
|
||||
def ls(self, remote_path: str, password: str | None = None) -> tuple[Folder, list[File]]:
|
||||
folder, laboratory = BaseCommand._resolve_folder(self.connection, remote_path, password)
|
||||
files = BaseCommand._find_files(self.connection, folder.id)
|
||||
folder, laboratory = self.resolve_folder(remote_path, password)
|
||||
files = self.find_files(folder.id)
|
||||
return folder, files
|
||||
|
||||
def cp(self, src_path: str, dest_path: str, is_recursive: bool = False) -> None:
|
||||
s_remote, s_laboratory_name, s_path = BaseCommand._parse_remote_host_with_path(src_path)
|
||||
d_remote, d_laboratory_name, d_path = BaseCommand._parse_remote_host_with_path(dest_path)
|
||||
s_remote, s_laboratory_name, s_path = self.parse_remote_host_with_path(src_path)
|
||||
d_remote, d_laboratory_name, d_path = self.parse_remote_host_with_path(dest_path)
|
||||
if s_remote != d_remote:
|
||||
raise IllegalArgumentException("Remote host mismatched.")
|
||||
if s_laboratory_name != d_laboratory_name:
|
||||
@@ -98,11 +76,11 @@ class MdrsClient:
|
||||
else:
|
||||
d_dirname = os.path.dirname(d_path)
|
||||
d_basename = os.path.basename(d_path)
|
||||
laboratory = BaseCommand._find_laboratory(self.connection, s_laboratory_name)
|
||||
s_parent_folder = BaseCommand._find_folder(self.connection, laboratory, s_dirname)
|
||||
s_parent_files = BaseCommand._find_files(self.connection, s_parent_folder.id)
|
||||
d_parent_folder = BaseCommand._find_folder(self.connection, laboratory, d_dirname)
|
||||
d_parent_files = BaseCommand._find_files(self.connection, d_parent_folder.id)
|
||||
laboratory = self.find_laboratory(s_laboratory_name)
|
||||
s_parent_folder = self.find_folder(laboratory, s_dirname)
|
||||
s_parent_files = self.find_files(s_parent_folder.id)
|
||||
d_parent_folder = self.find_folder(laboratory, d_dirname)
|
||||
d_parent_files = self.find_files(d_parent_folder.id)
|
||||
s_file = find_file(s_parent_files, s_basename)
|
||||
if s_file is not None:
|
||||
d_file = find_file(d_parent_files, d_basename)
|
||||
@@ -132,8 +110,8 @@ class MdrsClient:
|
||||
folder_api.copy(s_folder, d_parent_folder.id, normalize("NFC", d_basename))
|
||||
|
||||
def mv(self, src_path: str, dest_path: str) -> None:
|
||||
s_remote, s_laboratory_name, s_path = BaseCommand._parse_remote_host_with_path(src_path)
|
||||
d_remote, d_laboratory_name, d_path = BaseCommand._parse_remote_host_with_path(dest_path)
|
||||
s_remote, s_laboratory_name, s_path = self.parse_remote_host_with_path(src_path)
|
||||
d_remote, d_laboratory_name, d_path = self.parse_remote_host_with_path(dest_path)
|
||||
if s_remote != d_remote:
|
||||
raise IllegalArgumentException("Remote host mismatched.")
|
||||
if s_laboratory_name != d_laboratory_name:
|
||||
@@ -147,11 +125,11 @@ class MdrsClient:
|
||||
else:
|
||||
d_dirname = os.path.dirname(d_path)
|
||||
d_basename = os.path.basename(d_path)
|
||||
laboratory = BaseCommand._find_laboratory(self.connection, s_laboratory_name)
|
||||
s_parent_folder = BaseCommand._find_folder(self.connection, laboratory, s_dirname)
|
||||
s_parent_files = BaseCommand._find_files(self.connection, s_parent_folder.id)
|
||||
d_parent_folder = BaseCommand._find_folder(self.connection, laboratory, d_dirname)
|
||||
d_parent_files = BaseCommand._find_files(self.connection, d_parent_folder.id)
|
||||
laboratory = self.find_laboratory(s_laboratory_name)
|
||||
s_parent_folder = self.find_folder(laboratory, s_dirname)
|
||||
s_parent_files = self.find_files(s_parent_folder.id)
|
||||
d_parent_folder = self.find_folder(laboratory, d_dirname)
|
||||
d_parent_files = self.find_files(d_parent_folder.id)
|
||||
s_file = find_file(s_parent_files, s_basename)
|
||||
if s_file is not None:
|
||||
d_file = find_file(d_parent_files, d_basename)
|
||||
@@ -181,36 +159,34 @@ class MdrsClient:
|
||||
def chacl(
|
||||
self, remote_path: str, access_level: int, is_recursive: bool = False, password: str | None = None
|
||||
) -> None:
|
||||
remote, laboratory_name, r_path = BaseCommand._parse_remote_host_with_path(remote_path)
|
||||
remote, laboratory_name, r_path = self.parse_remote_host_with_path(remote_path)
|
||||
r_path = r_path.rstrip("/")
|
||||
laboratory = BaseCommand._find_laboratory(self.connection, laboratory_name)
|
||||
folder = BaseCommand._find_folder(self.connection, laboratory, r_path)
|
||||
laboratory = self.find_laboratory(laboratory_name)
|
||||
folder = self.find_folder(laboratory, r_path)
|
||||
folder_api = FoldersApi(self.connection)
|
||||
folder_api.acl(folder.id, access_level, is_recursive, password)
|
||||
|
||||
def metadata(self, remote_path: str, password: str | None = None) -> dict:
|
||||
folder, laboratory = BaseCommand._resolve_folder(self.connection, remote_path, password)
|
||||
folder, laboratory = self.resolve_folder(remote_path, password)
|
||||
folder_api = FoldersApi(self.connection)
|
||||
return folder_api.metadata(folder.id)
|
||||
|
||||
def file_metadata(self, remote_path: str, password: str | None = None) -> dict:
|
||||
folder, laboratory, r_basename = BaseCommand._resolve_file(self.connection, remote_path, password)
|
||||
files = BaseCommand._find_files(self.connection, folder.id)
|
||||
folder, laboratory, r_basename = self.resolve_file(remote_path, password)
|
||||
files = self.find_files(folder.id)
|
||||
file = find_file(files, r_basename)
|
||||
if file is None:
|
||||
raise IllegalArgumentException(f"File `{r_basename}` not found.")
|
||||
file_api = FilesApi(self.connection)
|
||||
return file_api.metadata(file)
|
||||
|
||||
def _create_connection(self, remote: str):
|
||||
return self.connection
|
||||
|
||||
def upload(
|
||||
self, local_path: str, remote_path: str, is_recursive: bool = False, is_skip_if_exists: bool = False
|
||||
) -> None:
|
||||
from mdrsclient.commands.upload import UploadCommand
|
||||
from mdrsclient.transfer import Uploader
|
||||
|
||||
UploadCommand._upload_logic(self.connection, local_path, remote_path, is_recursive, is_skip_if_exists)
|
||||
uploader = Uploader(self)
|
||||
uploader.upload(local_path, remote_path, is_recursive, is_skip_if_exists)
|
||||
|
||||
def download(
|
||||
self,
|
||||
@@ -221,23 +197,10 @@ class MdrsClient:
|
||||
password: str | None = None,
|
||||
excludes: list[str] | None = None,
|
||||
) -> None:
|
||||
from mdrsclient.commands.download import DownloadCommand
|
||||
from mdrsclient.transfer import Downloader
|
||||
|
||||
DownloadCommand._download_logic(
|
||||
self.connection, remote_path, local_path, is_recursive, is_skip_if_exists, password, excludes or []
|
||||
)
|
||||
|
||||
def ls_command(
|
||||
self,
|
||||
remote_path: str,
|
||||
password: str | None = None,
|
||||
is_json: bool = False,
|
||||
is_recursive: bool = False,
|
||||
is_quiet: bool = False,
|
||||
) -> None:
|
||||
from mdrsclient.commands.ls import LsCommand
|
||||
|
||||
LsCommand._ls_logic(self.connection, remote_path, password, is_json, is_recursive, is_quiet)
|
||||
downloader = Downloader(self)
|
||||
downloader.download(remote_path, local_path, is_recursive, is_skip_if_exists, password, excludes)
|
||||
|
||||
def version(self) -> str:
|
||||
from mdrsclient.__version__ import __version__
|
||||
@@ -245,14 +208,24 @@ class MdrsClient:
|
||||
return f"mdrs {__version__}"
|
||||
|
||||
def config_create(self, remote: str, url: str) -> None:
|
||||
from mdrsclient.commands.config import ConfigCommand
|
||||
from mdrsclient.config import ConfigFile
|
||||
|
||||
ConfigCommand.create(remote, url)
|
||||
remote = self.parse_remote_host(remote)
|
||||
config = ConfigFile(remote)
|
||||
if config.url is not None:
|
||||
raise IllegalArgumentException(f"Remote host `{remote}` is already exists.")
|
||||
else:
|
||||
config.url = url
|
||||
|
||||
def config_update(self, remote: str, url: str) -> None:
|
||||
from mdrsclient.commands.config import ConfigCommand
|
||||
from mdrsclient.config import ConfigFile
|
||||
|
||||
ConfigCommand.update(remote, url)
|
||||
remote = self.parse_remote_host(remote)
|
||||
config = ConfigFile(remote)
|
||||
if config.url is None:
|
||||
raise IllegalArgumentException(f"Remote host `{remote}` is not exists.")
|
||||
else:
|
||||
config.url = url
|
||||
|
||||
def config_list(self) -> list:
|
||||
from mdrsclient.config import ConfigFile
|
||||
@@ -261,6 +234,11 @@ class MdrsClient:
|
||||
return config.list()
|
||||
|
||||
def config_delete(self, remote: str) -> None:
|
||||
from mdrsclient.commands.config import ConfigCommand
|
||||
from mdrsclient.config import ConfigFile
|
||||
|
||||
ConfigCommand.delete(remote)
|
||||
remote = self.parse_remote_host(remote)
|
||||
config = ConfigFile(remote)
|
||||
if config.url is None:
|
||||
raise IllegalArgumentException(f"Remote host `{remote}` is not exists.")
|
||||
else:
|
||||
del config.url
|
||||
|
||||
Reference in New Issue
Block a user