Metadata-Version: 2.4
Name: baobab-database
Version: 2.0.0
Summary: Infrastructure de base de données réutilisable pour les projets Baobab.
Author: ANDRIANIAVP Patrick
License: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: sqlalchemy<3,>=2
Provides-Extra: all
Requires-Dist: mariadb<2,>=1.1; extra == 'all'
Requires-Dist: psycopg[binary]<4,>=3; extra == 'all'
Requires-Dist: pymysql<2,>=1; extra == 'all'
Provides-Extra: dev
Requires-Dist: bandit<2,>=1.7; extra == 'dev'
Requires-Dist: black<26,>=24; extra == 'dev'
Requires-Dist: coverage[toml]<8,>=7; extra == 'dev'
Requires-Dist: flake8-pyproject<2,>=1.2; extra == 'dev'
Requires-Dist: flake8<8,>=7; extra == 'dev'
Requires-Dist: mypy<2,>=1.10; extra == 'dev'
Requires-Dist: pylint<5,>=3; extra == 'dev'
Requires-Dist: pytest-asyncio<1,>=0.23; extra == 'dev'
Requires-Dist: pytest<9,>=7; extra == 'dev'
Provides-Extra: mariadb
Requires-Dist: mariadb<2,>=1.1; extra == 'mariadb'
Provides-Extra: mysql
Requires-Dist: pymysql<2,>=1; extra == 'mysql'
Provides-Extra: postgresql
Requires-Dist: psycopg[binary]<4,>=3; extra == 'postgresql'
Description-Content-Type: text/markdown

# baobab-database

Infrastructure de base de données réutilisable pour les projets Baobab, basée
sur **SQLAlchemy 2**. Depuis la **v2**, la librairie supporte plusieurs
dialectes : **SQLite**, **PostgreSQL**, **MySQL** et **MariaDB**, via une
architecture extensible par adaptateurs.

## Fonctionnalités

- Configuration typée par dialecte (`DatabaseDialect`, `SqliteDatabaseSettings`,
  `NetworkDatabaseSettings`)
- Registre de bases nommées (`DatabaseRegistry`)
- Ports d'adaptateurs et registre d'adaptateurs (`DatabaseAdapter`,
  `DatabaseAdapterRegistry`)
- Adaptateurs de dialecte SQLite, PostgreSQL, MySQL et MariaDB
- Factory de moteurs générique avec cache (`EngineFactory`)
- Sessions et unité de travail transactionnelle (`SessionFactory`, `UnitOfWork`)
- Création de schéma ORM (`SchemaManager`, `Base`)
- Construction d'URL via `sqlalchemy.engine.URL.create` (aucun secret concaténé)

## Prérequis

- Python **3.11** ou supérieur
- pip

## Installation

SQLite ne requiert aucun driver supplémentaire. Les drivers réseau sont
optionnels et installables par extras :

```bash
python -m venv .venv
# Windows
.\.venv\Scripts\Activate.ps1
# Linux / macOS
source .venv/bin/activate

# SQLite seul
pip install -e .

# Avec un driver réseau
pip install -e ".[postgresql]"
pip install -e ".[mysql]"
pip install -e ".[mariadb]"

# Tous les drivers réseau
pip install -e ".[all]"

# Outils de développement
pip install -e ".[dev]"
```

| Extra | Driver installé | Dialecte SQLAlchemy |
|---|---|---|
| `postgresql` | `psycopg` | `postgresql+psycopg` |
| `mysql` | `pymysql` | `mysql+pymysql` |
| `mariadb` | `mariadb` | `mariadb+mariadbconnector` |
| `all` | les trois ci-dessus | — |

## Démarrage rapide (SQLite)

```python
from pathlib import Path

from baobab_database.config.database_registry import DatabaseRegistry
from baobab_database.config.sqlite_database_settings import SqliteDatabaseSettings
from baobab_database.core.engine_factory import EngineFactory
from baobab_database.core.schema_manager import SchemaManager
from baobab_database.core.session_factory import SessionFactory
from baobab_database.core.unit_of_work import UnitOfWork
from baobab_database.dialects.default_adapter_registry import (
    build_default_adapter_registry,
)

registry = DatabaseRegistry(
    {
        "default": SqliteDatabaseSettings(
            name="default",
            database_path=Path("data/default.sqlite"),
        )
    }
)

engine_factory = EngineFactory(
    registry=registry,
    adapter_registry=build_default_adapter_registry(),
)

SchemaManager(engine_factory).create_schema("default")

session_factory = SessionFactory(engine_factory)
unit_of_work = UnitOfWork(session_factory)

with unit_of_work.begin("default") as session:
    # utiliser session ici
    pass
```

## Exemple PostgreSQL

> Les secrets (mot de passe) doivent provenir de la configuration applicative ou
> de variables d'environnement, jamais du code source.

```python
import os

from baobab_database.config.database_dialect import DatabaseDialect
from baobab_database.config.database_registry import DatabaseRegistry
from baobab_database.config.network_database_settings import NetworkDatabaseSettings
from baobab_database.core.engine_factory import EngineFactory
from baobab_database.dialects.default_adapter_registry import (
    build_default_adapter_registry,
)

registry = DatabaseRegistry(
    {
        "primary": NetworkDatabaseSettings(
            name="primary",
            dialect=DatabaseDialect.POSTGRESQL,
            host="localhost",
            port=5432,
            database="app",
            username="app_user",
            password=os.environ.get("APP_DB_PASSWORD"),
            ssl_enabled=True,
            ssl_ca="/etc/ssl/ca.pem",
        )
    }
)

engine_factory = EngineFactory(
    registry=registry,
    adapter_registry=build_default_adapter_registry(),
)
engine = engine_factory.get_engine("primary")
```

## Exemple MySQL

```python
from baobab_database.config.database_dialect import DatabaseDialect
from baobab_database.config.network_database_settings import NetworkDatabaseSettings

mysql_settings = NetworkDatabaseSettings(
    name="catalog",
    dialect=DatabaseDialect.MYSQL,
    host="localhost",
    port=3306,
    database="catalog",
    username="catalog_user",
    password=None,  # fourni via la configuration applicative
)
# URL générée : mysql+pymysql://...?charset=utf8mb4
```

## Exemple MariaDB

```python
from baobab_database.config.database_dialect import DatabaseDialect
from baobab_database.config.network_database_settings import NetworkDatabaseSettings

mariadb_settings = NetworkDatabaseSettings(
    name="reporting",
    dialect=DatabaseDialect.MARIADB,
    host="localhost",
    port=3306,
    database="reporting",
    username="reporting_user",
)
# URL générée : mariadb+mariadbconnector://...?charset=utf8mb4
```

MariaDB est un dialecte **distinct** de MySQL : il utilise le driver
`mariadbconnector` et le dialecte SQLAlchemy `mariadb+mariadbconnector`.

## Schéma ORM sur bases réseau

`SchemaManager.create_schema` appelle `Base.metadata.create_all`. C'est adapté à
SQLite et aux tests. Pour les bases réseau de production, privilégiez des
**migrations contrôlées** (par exemple Alembic) plutôt que `create_all`.

## Sécurité des secrets

- Les URL sont construites via `URL.create` ; aucun mot de passe n'est concaténé.
- `NetworkDatabaseSettings` masque le mot de passe dans sa représentation
  (`repr`).
- Les mots de passe ne doivent jamais figurer dans le code, les tests ou la
  documentation : utilisez des variables d'environnement ou un gestionnaire de
  secrets.

## Options SSL/TLS par dialecte

Activer `ssl_enabled=True` sur `NetworkDatabaseSettings` applique le chiffrement
selon le dialecte :

| Dialecte | Transmission | Détails |
|---|---|---|
| PostgreSQL | paramètres d'URL libpq | `sslmode=verify-ca` si `ssl_ca` est fourni, sinon `sslmode=require` ; `sslrootcert`, `sslcert`, `sslkey` ajoutés si fournis |
| MySQL | `connect_args={"ssl": {...}}` (pymysql) | clés `ca`, `cert`, `key` |
| MariaDB | `connect_args` (mariadbconnector) | `ssl=True`, `ssl_ca`, `ssl_cert`, `ssl_key` |

`ssl_cert` et `ssl_key` doivent être fournis ensemble ; fournir un fichier SSL
sans activer `ssl_enabled` lève une `ConfigurationError`.

## Migration de la v1 vers la v2

| v1 | v2 |
|---|---|
| `DatabaseSettings(name=..., database_path=...)` | `SqliteDatabaseSettings(name=..., database_path=...)` |
| `EngineFactory(registry, path_resolver, url_factory, pragma_configurator)` | `EngineFactory(registry, adapter_registry)` |
| `from baobab_database.sqlite.sqlite_url_factory import SqliteUrlFactory` | `from baobab_database.dialects.sqlite.sqlite_url_factory import SqliteUrlFactory` |

Compatibilité conservée :

- `DatabaseSettings` reste utilisable comme **façade SQLite dépréciée** (elle
  expose désormais une propriété `dialect`).
- Le paquet `baobab_database.sqlite` reste importable : ses modules redirigent
  vers `baobab_database.dialects.sqlite` (dépréciés).
- `SqliteAdapter` accepte aussi bien `SqliteDatabaseSettings` que la façade
  `DatabaseSettings`.

Étapes recommandées :

1. Remplacer `DatabaseSettings` par `SqliteDatabaseSettings`.
2. Construire `EngineFactory` avec un `DatabaseAdapterRegistry`
   (`build_default_adapter_registry()` fournit tous les adaptateurs).
3. Mettre à jour les imports `baobab_database.sqlite.*` vers
   `baobab_database.dialects.sqlite.*`.

## Tests d'intégration réseau (optionnels)

La suite standard ne nécessite aucun service réseau. Les tests d'intégration
sont marqués et désactivés par défaut. Pour les activer, définissez les
variables d'environnement adaptées puis lancez la marque correspondante :

```bash
export BAOBAB_IT_POSTGRESQL_HOST=localhost
export BAOBAB_IT_POSTGRESQL_DATABASE=app
export BAOBAB_IT_POSTGRESQL_USERNAME=app_user
export BAOBAB_IT_POSTGRESQL_PASSWORD=...  # via votre gestionnaire de secrets

pytest -m postgresql
```

Variables disponibles par dialecte (préfixe `BAOBAB_IT_<DIALECTE>_`) :
`HOST`, `PORT`, `DATABASE`, `USERNAME`, `PASSWORD`.

## Structure du package

```text
src/baobab_database/
├── config/          # DatabaseDialect, *DatabaseSettings, DatabaseRegistry
├── core/            # EngineFactory, SessionFactory, UnitOfWork, SchemaManager
│   └── ports/       # DatabaseAdapter, DatabaseUrlFactory, EngineConfigurator
├── dialects/        # adaptateurs sqlite / postgresql / mysql / mariadb
├── errors/          # ConfigurationError, UnknownAdapterError, DuplicateAdapterError
└── sqlite/          # compatibilité v1 (redirections dépréciées)
```

## Qualité et tests

```bash
pytest
coverage run -m pytest && coverage report
black --check src tests
flake8 src tests
pylint src tests
mypy src tests
python -m bandit -r src/baobab_database
```

Seuil de couverture minimal du projet : **90 %**.

## Licence

MIT — voir le fichier de licence du dépôt.

## Changelog

Voir [CHANGELOG.md](CHANGELOG.md).
