refactor(config): abstract config storage and enable dependency injection
Abstract the configuration storage mechanism to allow using custom configurations, such as in-memory setups, when using the tool as a library. This aligns the configuration architecture with the session cache abstraction. - Define ConfigInterface protocol and InMemoryConfig class - Make CacheFile, InMemoryCache, ConfigFile, and InMemoryConfig explicitly inherit their interfaces - Update MdrsService and MdrsClient to accept customizable config_class and config instances - Add validation to check remote parameter consistency in create_connection - Remove unused imports across command files
This commit is contained in:
+2
-2
@@ -66,7 +66,7 @@ class CacheInterface(Protocol):
|
||||
def laboratories(self, laboratories: Laboratories) -> None: ...
|
||||
|
||||
|
||||
class InMemoryCache:
|
||||
class InMemoryCache(CacheInterface):
|
||||
def __init__(self) -> None:
|
||||
self.__data = CacheData()
|
||||
|
||||
@@ -105,7 +105,7 @@ class InMemoryCache:
|
||||
self.__data.laboratories = laboratories
|
||||
|
||||
|
||||
class CacheFile:
|
||||
class CacheFile(CacheInterface):
|
||||
__serial: int
|
||||
__cache_dir: str
|
||||
__cache_file: str
|
||||
|
||||
+11
-16
@@ -4,6 +4,7 @@ from unicodedata import normalize
|
||||
|
||||
from mdrsclient.api import DoiApi, FilesApi, FoldersApi, LaboratoriesApi, UsersApi
|
||||
from mdrsclient.cache import CacheInterface
|
||||
from mdrsclient.config import ConfigInterface
|
||||
from mdrsclient.connection import MDRSConnection
|
||||
from mdrsclient.exceptions import IllegalArgumentException, MDRSException, UnauthorizedException, UnexpectedException
|
||||
from mdrsclient.models import File, Folder, Laboratory, Token, User
|
||||
@@ -14,12 +15,14 @@ from mdrsclient.services import MdrsService
|
||||
class MdrsClient(MdrsService):
|
||||
"""Service layer client for MDRS."""
|
||||
|
||||
def __init__(self, connection: MDRSConnection):
|
||||
super().__init__(connection)
|
||||
def __init__(self, connection: MDRSConnection, config_class: type[ConfigInterface] | None = None):
|
||||
super().__init__(connection, config_class)
|
||||
|
||||
@classmethod
|
||||
def from_remote(cls, remote: str, cache: CacheInterface | None = None) -> "MdrsClient":
|
||||
return cls(cls.create_connection(remote, cache))
|
||||
def from_remote(
|
||||
cls, remote: str, cache: CacheInterface | None = None, config: ConfigInterface | None = None
|
||||
) -> "MdrsClient":
|
||||
return cls(cls.create_connection(remote, cache, config))
|
||||
|
||||
def mkdir(self, remote_path: str) -> None:
|
||||
remote, laboratory_name, r_path = self.parse_remote_host_with_path(remote_path)
|
||||
@@ -208,36 +211,28 @@ class MdrsClient(MdrsService):
|
||||
return f"mdrs {__version__}"
|
||||
|
||||
def config_create(self, remote: str, url: str) -> None:
|
||||
from mdrsclient.config import ConfigFile
|
||||
|
||||
remote = self.parse_remote_host(remote)
|
||||
config = ConfigFile(remote)
|
||||
config = self.config_class(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.config import ConfigFile
|
||||
|
||||
remote = self.parse_remote_host(remote)
|
||||
config = ConfigFile(remote)
|
||||
config = self.config_class(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
|
||||
|
||||
config = ConfigFile("")
|
||||
config = self.config_class("")
|
||||
return config.list()
|
||||
|
||||
def config_delete(self, remote: str) -> None:
|
||||
from mdrsclient.config import ConfigFile
|
||||
|
||||
remote = self.parse_remote_host(remote)
|
||||
config = ConfigFile(remote)
|
||||
config = self.config_class(remote)
|
||||
if config.url is None:
|
||||
raise IllegalArgumentException(f"Remote host `{remote}` is not exists.")
|
||||
else:
|
||||
|
||||
@@ -2,11 +2,7 @@ import getpass
|
||||
from argparse import Namespace
|
||||
from typing import Any
|
||||
|
||||
from mdrsclient.api import UsersApi
|
||||
from mdrsclient.commands.base import BaseCommand
|
||||
from mdrsclient.config import ConfigFile
|
||||
from mdrsclient.connection import MDRSConnection
|
||||
from mdrsclient.exceptions import MissingConfigurationException
|
||||
|
||||
|
||||
class LoginCommand(BaseCommand):
|
||||
|
||||
@@ -2,9 +2,6 @@ from argparse import Namespace
|
||||
from typing import Any
|
||||
|
||||
from mdrsclient.commands.base import BaseCommand
|
||||
from mdrsclient.config import ConfigFile
|
||||
from mdrsclient.connection import MDRSConnection
|
||||
from mdrsclient.exceptions import MissingConfigurationException
|
||||
|
||||
|
||||
class LogoutCommand(BaseCommand):
|
||||
|
||||
@@ -2,9 +2,6 @@ from argparse import Namespace
|
||||
from typing import Any, Final
|
||||
|
||||
from mdrsclient.commands.base import BaseCommand
|
||||
from mdrsclient.config import ConfigFile
|
||||
from mdrsclient.connection import MDRSConnection
|
||||
from mdrsclient.exceptions import MissingConfigurationException
|
||||
|
||||
|
||||
class WhoamiCommand(BaseCommand):
|
||||
|
||||
+52
-2
@@ -1,6 +1,7 @@
|
||||
import configparser
|
||||
import os
|
||||
from typing import Final
|
||||
import threading
|
||||
from typing import Final, Protocol, runtime_checkable
|
||||
|
||||
import validators
|
||||
|
||||
@@ -9,7 +10,56 @@ from mdrsclient.settings import CONFIG_DIRNAME
|
||||
from mdrsclient.utils import FileLock
|
||||
|
||||
|
||||
class ConfigFile:
|
||||
@runtime_checkable
|
||||
class ConfigInterface(Protocol):
|
||||
remote: str
|
||||
|
||||
def list(self) -> list[tuple[str, str]]: ...
|
||||
@property
|
||||
def url(self) -> str | None: ...
|
||||
@url.setter
|
||||
def url(self, url: str) -> None: ...
|
||||
@url.deleter
|
||||
def url(self) -> None: ...
|
||||
|
||||
|
||||
class InMemoryConfig(ConfigInterface):
|
||||
__configs: dict[str, str] = {}
|
||||
__lock: threading.Lock = threading.Lock()
|
||||
remote: str
|
||||
|
||||
def __init__(self, remote: str) -> None:
|
||||
self.remote = remote
|
||||
|
||||
def list(self) -> list[tuple[str, str]]:
|
||||
with self.__lock:
|
||||
return list(self.__configs.items())
|
||||
|
||||
@property
|
||||
def url(self) -> str | None:
|
||||
with self.__lock:
|
||||
return self.__configs.get(self.remote)
|
||||
|
||||
@url.setter
|
||||
def url(self, url: str) -> None:
|
||||
if not validators.url(url):
|
||||
raise IllegalArgumentException("malformed URI sequence")
|
||||
with self.__lock:
|
||||
self.__configs[self.remote] = url
|
||||
|
||||
@url.deleter
|
||||
def url(self) -> None:
|
||||
with self.__lock:
|
||||
if self.remote in self.__configs:
|
||||
del self.__configs[self.remote]
|
||||
|
||||
@classmethod
|
||||
def clear(cls) -> None:
|
||||
with cls.__lock:
|
||||
cls.__configs.clear()
|
||||
|
||||
|
||||
class ConfigFile(ConfigInterface):
|
||||
OPTION_URL: Final[str] = "url"
|
||||
CONFIG_FILENAME: Final[str] = "config.ini"
|
||||
remote: str
|
||||
|
||||
+13
-4
@@ -5,7 +5,7 @@ from unicodedata import normalize
|
||||
|
||||
from mdrsclient.api import DoiApi, FilesApi, FoldersApi, LaboratoriesApi, UsersApi
|
||||
from mdrsclient.cache import CacheInterface
|
||||
from mdrsclient.config import ConfigFile
|
||||
from mdrsclient.config import ConfigFile, ConfigInterface
|
||||
from mdrsclient.connection import MDRSConnection
|
||||
from mdrsclient.exceptions import (
|
||||
IllegalArgumentException,
|
||||
@@ -18,12 +18,21 @@ from mdrsclient.utils import page_num_from_url
|
||||
|
||||
|
||||
class MdrsService:
|
||||
def __init__(self, connection: MDRSConnection):
|
||||
config_class: type[ConfigInterface] = ConfigFile
|
||||
|
||||
def __init__(self, connection: MDRSConnection, config_class: type[ConfigInterface] | None = None):
|
||||
self.connection = connection
|
||||
if config_class is not None:
|
||||
self.config_class = config_class
|
||||
|
||||
@classmethod
|
||||
def create_connection(cls, remote: str, cache: CacheInterface | None = None) -> MDRSConnection:
|
||||
config = ConfigFile(remote)
|
||||
def create_connection(
|
||||
cls, remote: str, cache: CacheInterface | None = None, config: ConfigInterface | None = None
|
||||
) -> MDRSConnection:
|
||||
if config is None:
|
||||
config = ConfigFile(remote)
|
||||
elif config.remote != remote:
|
||||
raise IllegalArgumentException("Remote host parameter mismatch.")
|
||||
if config.url is None:
|
||||
raise MissingConfigurationException(f"Remote host `{remote}` is not found.")
|
||||
return MDRSConnection(config.remote, config.url, cache=cache)
|
||||
|
||||
Reference in New Issue
Block a user