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:
2026-07-02 23:16:53 +09:00
parent 36cad6db52
commit 8ce9e09e69
12 changed files with 361 additions and 675 deletions
+20 -14
View File
@@ -5,8 +5,8 @@ from typing import Any
from pydantic.dataclasses import dataclass
from mdrsclient.api import FilesApi, FoldersApi
from mdrsclient.client import MdrsClient
from mdrsclient.commands.base import BaseCommand
from mdrsclient.connection import MDRSConnection
from mdrsclient.exceptions import UnauthorizedException
from mdrsclient.models import File, Folder, FolderSimple, Laboratory
@@ -19,7 +19,7 @@ class Config:
@dataclass(config=Config)
class LsCommandContext:
prefix: str
connection: MDRSConnection
client: MdrsClient
laboratory: Laboratory
password: str
is_json: bool
@@ -58,21 +58,27 @@ class LsCommand(BaseCommand):
from mdrsclient.client import MdrsClient
client = MdrsClient.from_remote(remote)
client.ls_command(remote_path, password, is_json, is_recursive, is_quiet)
cls._ls_logic(client, remote_path, password, is_json, is_recursive, is_quiet)
return
@classmethod
def _ls_logic(
cls, connection, remote_path: str, password: str | None, is_json: bool, is_recursive: bool, is_quiet: bool
cls,
client: MdrsClient,
remote_path: str,
password: str | None,
is_json: bool,
is_recursive: bool,
is_quiet: bool,
) -> None:
remote = remote_path.split(":", 1)[0] if ":" in remote_path else ""
folder, laboratory = cls._resolve_folder(connection, remote_path, password)
folder, laboratory = client.resolve_folder(remote_path, password)
laboratory_name = laboratory.name
files = cls._find_files(connection, folder.id)
files = client.find_files(folder.id)
context = LsCommandContext(
f"{remote}:/{laboratory_name}",
connection,
client,
laboratory,
password if password is not None else "",
is_json,
@@ -102,7 +108,7 @@ class LsCommand(BaseCommand):
for key in label.keys():
length[key] = len(label[key]) if not context.is_quiet else 0
for sub_folder in folder.sub_folders:
sub_laboratory = context.connection.laboratories.find_by_id(sub_folder.laboratory_id)
sub_laboratory = context.client.connection.laboratories.find_by_id(sub_folder.laboratory_id)
sub_laboratory_name = sub_laboratory.name if sub_laboratory is not None else "(invalid)"
length["acl"] = max(length["acl"], len(sub_folder.access_level_name))
length["laboratory"] = max(length["laboratory"], len(sub_laboratory_name))
@@ -147,12 +153,12 @@ class LsCommand(BaseCommand):
if context.is_recursive:
print("")
for sub_folder in sorted(folder.sub_folders, key=lambda x: x.name):
folder_api = FoldersApi(context.connection)
folder_api = FoldersApi(context.client.connection)
try:
if sub_folder.lock:
folder_api.auth(sub_folder.id, context.password)
folder = folder_api.retrieve(sub_folder.id)
files = cls._find_files(context.connection, sub_folder.id)
files = context.client.find_files(sub_folder.id)
cls._ls_plain(context, folder, files)
except UnauthorizedException:
pass
@@ -174,7 +180,7 @@ class LsCommand(BaseCommand):
"updated_at": folder.updated_at,
}
if isinstance(folder, Folder):
folder_api = FoldersApi(context.connection)
folder_api = FoldersApi(context.client.connection)
data["metadata"] = folder_api.metadata(folder.id)
if context.is_recursive:
sub_folders: list[dict[str, Any]] = []
@@ -183,7 +189,7 @@ class LsCommand(BaseCommand):
if sub_folder.lock:
folder_api.auth(sub_folder.id, context.password)
folder2 = folder_api.retrieve(sub_folder.id)
files2 = cls._find_files(context.connection, sub_folder.id)
files2 = context.client.find_files(sub_folder.id)
sub_folders.append(cls._folder2dict(context, folder2, files2))
except UnauthorizedException:
pass
@@ -205,7 +211,7 @@ class LsCommand(BaseCommand):
# "thumbnail": file.thumbnail,
"description": file.description,
"metadata": file.metadata,
"download_url": f"{context.connection.url}/{file.download_url}",
"download_url": f"{context.client.connection.url}/{file.download_url}",
"created_at": file.created_at,
"updated_at": file.updated_at,
}
@@ -213,5 +219,5 @@ class LsCommand(BaseCommand):
@classmethod
def _laboratory_name(cls, context: LsCommandContext, laboratory_id: int) -> str:
laboratory = context.connection.laboratories.find_by_id(laboratory_id)
laboratory = context.client.connection.laboratories.find_by_id(laboratory_id)
return laboratory.name if laboratory is not None else "(invalid)"