Add config and client injection with test reorganization.

Introduce typed config objects, optional adapter injection, and .env loading to simplify testing while preserving env-based defaults for production usage.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Brian Bjarke Jensen
2026-07-06 20:44:08 +02:00
co-authored by Cursor
parent 366831ac52
commit 7991dabdbc
28 changed files with 645 additions and 377 deletions
+35 -23
View File
@@ -1,14 +1,12 @@
"""Definition of MinioAdapter class."""
from __future__ import annotations
import os
from io import BytesIO
from python_utils import check_env
from python_repositories.adapters.connection_aware_adapter import (
ConnectionAwareAdapter,
)
from python_repositories.config import MinioConfig
from python_repositories.interfaces import ObjectRepositoryInterface
try:
@@ -30,60 +28,74 @@ class MinioAdapter(ObjectRepositoryInterface, ConnectionAwareAdapter):
chunk_size: int = 5 * 2**20 # 5 MiB
connection_name: str = "Minio"
def __init__(self) -> None:
def __init__(
self,
*,
config: MinioConfig | None = None,
client: minio.Minio | None = None,
) -> None:
super().__init__()
check_env(
{
if client is not None and config is None:
raise ValueError("config is required when client is provided")
if config is None and client is None:
config = MinioConfig.from_env(
self.endpoint_env_var_name,
self.access_key_env_var_name,
self.secret_key_env_var_name,
self.bucket_env_var_name,
},
)
self._client: minio.Minio | None = None
self._bucket_name: str | None = None
)
assert config is not None
self._config = config
self._client_injected = client is not None
self._client: minio.Minio | None = client
self._bucket_name: str | None = config.bucket if client is not None else None
if self._client_injected:
self._invalidate_health_cache()
def _is_client_ready(self) -> bool:
return self._client is not None and self._bucket_name is not None
def connect(self) -> None:
"""Connect to the Minio server."""
if self._client_injected:
if self._client is not None:
try:
_ = self._client.list_buckets()
except Exception as exc: # pylint: disable=broad-except
raise ConnectionError(
f"Could not connect to Minio at {self._config.endpoint}"
) from exc
self._invalidate_health_cache()
return
if self._client is not None and self.is_connected():
self.logger.info("Already connected to Minio")
return
if self._client is not None:
self.disconnect()
# Prepare arguments
endpoint = str(os.getenv(self.endpoint_env_var_name))
access_key = str(os.getenv(self.access_key_env_var_name))
secret_key = str(os.getenv(self.secret_key_env_var_name))
bucket = str(os.getenv(self.bucket_env_var_name))
# Connect client
endpoint = self._config.endpoint
access_key = self._config.access_key
secret_key = self._config.secret_key
bucket = self._config.bucket
client = minio.Minio(
endpoint=endpoint,
access_key=access_key,
secret_key=secret_key,
secure=False,
secure=self._config.secure,
)
# Test the connection by listing buckets (will raise if connection fails)
try:
_ = client.list_buckets()
except Exception as exc: # pylint: disable=broad-except
raise ConnectionError(f"Could not connect to Minio at {endpoint}") from exc
# Ensure bucket exists
if not client.bucket_exists(bucket):
self.logger.info(f"Creating bucket '{bucket}'")
client.make_bucket(bucket)
# Persist information
self._client = client
self._bucket_name = bucket
self._invalidate_health_cache()
def disconnect(self) -> None:
"""Disconnect from the Minio server."""
# Close connection
# N.B. Minio client does not have a close method, but we include this for symmetry with other adapters
# Reset client
# N.B. Minio client does not have a close method, but we include this for symmetry
self._client = None
self._bucket_name = None
self._invalidate_health_cache()