fix(core): preserve adapter command contracts

This commit is contained in:
2026-07-11 21:00:50 +02:00
parent dbbab6d005
commit a35efa16de
20 changed files with 163 additions and 143 deletions
+16 -48
View File
@@ -1,43 +1,14 @@
from pathlib import Path
import typer
from sqlalchemy.exc import OperationalError
from tht.adapters.dwh import ThothRestDwhAdapter
from tht.adapters.factory import build_dwh
from tht.cli.config_cmd import CONFIG_OPT
from tht.config import ConfigError, load_config
from tht.db.connection import can_create_in_schema, make_engine, ping, writable_tables
from tht.db.fetch_ca import CaFetchError, describe_pem, fetch_chain_pem, parse_host_port
db_app = typer.Typer(help="Operazioni sul database target")
def _ping_rest(adapter: ThothRestDwhAdapter, cfg, schema: str) -> None:
"""Health check via REST. Il read-only è garantito strutturalmente dall'API
(ammette solo SELECT/WITH): non serve il controllo dei privilegi di scrittura."""
from tht.rest.client import RestError
try:
info = adapter._client.ping()
except RestError as e:
typer.secho(f"ERRORE di connessione: {e}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
if not info.get("db_connected") or not info.get("schema_accessible"):
typer.secho(
f"ERRORE: DWH non accessibile via REST (risposta: {info}).",
fg=typer.colors.RED,
err=True,
)
raise typer.Exit(code=1)
typer.secho(
f"OK: connesso via REST a {cfg.rest.base_url} (schema {schema})", fg=typer.colors.GREEN
)
typer.secho(
"OK: accesso read-only garantito dall'API (solo SELECT/WITH).", fg=typer.colors.GREEN
)
@db_app.command("ping")
def ping_cmd(config: Path = CONFIG_OPT) -> None:
"""Testa la connessione e verifica che l'utente sia effettivamente read-only."""
@@ -46,29 +17,26 @@ def ping_cmd(config: Path = CONFIG_OPT) -> None:
except ConfigError as e:
typer.secho(f"ERRORE: {e}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
schema = cfg.database.db_schema
adapter = build_dwh(cfg)
if isinstance(adapter, ThothRestDwhAdapter):
_ping_rest(adapter, cfg, schema)
return
engine = make_engine(cfg.database)
try:
ping(engine)
except OperationalError as e:
typer.secho(f"ERRORE di connessione: {e.orig}", fg=typer.colors.RED, err=True)
health = build_dwh(cfg).health()
if not health.ok:
typer.secho(f"ERRORE di connessione: {health.detail}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
typer.secho(f"OK: connesso a {cfg.database.database} (schema {schema})", fg=typer.colors.GREEN)
writable = writable_tables(engine, schema)
can_create = can_create_in_schema(engine, schema)
if writable or can_create:
if health.endpoint:
typer.secho(f"OK: connesso via REST a {health.endpoint} (schema {health.schema})",
fg=typer.colors.GREEN)
typer.secho("OK: accesso read-only garantito dall'API (solo SELECT/WITH).",
fg=typer.colors.GREEN)
return
typer.secho(f"OK: connesso a {health.database} (schema {health.schema})",
fg=typer.colors.GREEN)
if not health.read_only:
typer.secho(
f"ERRORE: l'utente '{cfg.database.user}' NON e' read-only.", fg=typer.colors.RED, err=True
)
if writable:
typer.echo(f" Tabelle scrivibili: {', '.join(writable[:10])}", err=True)
if can_create:
typer.echo(f" L'utente puo' creare oggetti nello schema {schema}.", err=True)
if health.writable_tables:
typer.echo(f" Tabelle scrivibili: {', '.join(health.writable_tables[:10])}", err=True)
if health.can_create:
typer.echo(f" L'utente puo' creare oggetti nello schema {health.schema}.", err=True)
typer.echo(" Crea un ruolo read-only con scripts/create_readonly_role.sql.", err=True)
raise typer.Exit(code=2)
typer.secho("OK: l'utente e' read-only sullo schema target.", fg=typer.colors.GREEN)