feat: consolidate database management work

Add catalog-owned logical relationships and runtime snapshots, extend the database-management UI and validation coverage, and document the updated operational workflow.

Keep active sensitive-generation status in a tooltip and indicator, and update the layout E2E to follow the history action in its new database-scoped location.
This commit is contained in:
Codex
2026-09-01 14:46:55 +02:00
parent f586152636
commit 076c9742c5
73 changed files with 6966 additions and 610 deletions
+48 -21
View File
@@ -10,6 +10,12 @@ 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.sampling import is_text_type
from tht.mschema.context import (
SchemaContextError,
annotations_path,
load_schema_context,
physical_path,
)
from tht.mschema.eligibility import classify_all
schema_app = typer.Typer(help="Gestione mschema (rappresentazione canonica dello schema)")
@@ -40,20 +46,6 @@ def _load_config_or_exit(config: Path):
raise typer.Exit(code=1)
def physical_path(cfg) -> Path:
from tht.jobs.dwh_pipeline import resolve_dwh_snapshot
if not (cfg.paths.artifacts.parent / ".tht-dwh").exists():
return cfg.paths.artifacts / "mschema" / "physical.yaml"
return resolve_dwh_snapshot(cfg).physical
def annotations_path(cfg) -> Path:
if cfg.paths.annotations_root is not None:
return cfg.paths.annotations_root / "mschema" / "annotations.yaml"
return cfg.paths.artifacts / "mschema" / "annotations.yaml"
def refresh_catalog(cfg, *, dwh=None, output_path: Path | None = None):
"""Run the existing catalog algorithm and persist its canonical output."""
target = dwh if dwh is not None else build_dwh(cfg)
@@ -432,6 +424,21 @@ def suggest_fks_cmd(
from tht.mschema.models import Annotations, PhysicalSchema, TableAnnotation
cfg = _load_config_or_exit(config)
if write and cfg.paths.effective_relationships is not None:
error = "effective relationships are managed by the catalog; --write is disabled"
if json_output:
_emit_json({
"code": "catalog_relationships_managed",
"error": error,
"operation": "schema_suggest_fks",
"schemaVersion": 1,
"status": "failed",
"workspaceId": cfg._workspace_id,
"workspaceRevision": cfg._workspace_revision,
})
else:
typer.secho(f"ERRORE: {error}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
phys_file = physical_path(cfg)
if not phys_file.exists():
if json_output:
@@ -556,7 +563,6 @@ def render_cmd(
"""Serializza mschema (physical + annotations) nel formato richiesto."""
import json
from tht.mschema.models import Annotations, PhysicalSchema
from tht.mschema.render import to_markdown, to_mschema_text, to_schema_dict
cfg = _load_config_or_exit(config)
@@ -564,19 +570,40 @@ def render_cmd(
if not phys_file.exists():
typer.secho(
f"ERRORE: {phys_file} non trovato. Esegui prima `tht schema introspect`.",
fg=typer.colors.RED, err=True,
fg=typer.colors.RED,
err=True,
)
raise typer.Exit(code=1)
physical = PhysicalSchema.from_yaml(phys_file)
annotations = Annotations.from_yaml(annotations_path(cfg))
try:
context = load_schema_context(cfg, physical_file=phys_file)
except SchemaContextError as exc:
typer.secho(f"ERRORE: {exc}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1) from None
table_filter = list(tables) if tables else None
if format == "markdown":
out = to_markdown(physical, annotations)
out = to_markdown(
context.physical,
context.annotations,
effective_relationships=context.effective_relationships,
)
elif format == "mschema-text":
out = to_mschema_text(physical, annotations, tables=table_filter)
out = to_mschema_text(
context.physical,
context.annotations,
tables=table_filter,
effective_relationships=context.effective_relationships,
)
elif format == "schema-dict":
out = json.dumps(to_schema_dict(physical, annotations), ensure_ascii=False, indent=2)
out = json.dumps(
to_schema_dict(
context.physical,
context.annotations,
effective_relationships=context.effective_relationships,
),
ensure_ascii=False,
indent=2,
)
else:
typer.secho(f"ERRORE: formato sconosciuto: {format}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)