Files
ss-tools/backend/alembic/versions/a8b9c0d1e2f3_add_scenario_automation_tables.py

114 lines
5.4 KiB
Python

# #region Alembic.ScenarioAutomationTables [C:3] [TYPE Module] [SEMANTICS alembic,scenario,automation,schedule,trigger,notification]
# @ingroup Alembic
# @BRIEF Add 046 scenario schedules, trigger rules and notification events tables.
# @LAYER Database
"""add scenario automation tables (046 dashboard-scenario-automation)
Revision ID: a8b9c0d1e2f3
Revises: z7a8b9c0d1e2
Create Date: 2026-08-20
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a8b9c0d1e2f3"
down_revision: str | Sequence[str] | None = "z7a8b9c0d1e2"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""Create scenario_schedules, scenario_trigger_rules, scenario_automation_policies,
scenario_notification_events (046 automation persistence).
revision_id is nullable — required only when revision_policy=pinned; a `current`
schedule/rule resolves the atomically activated revision at dispatch time (042).
"""
# ── scenario_schedules ──
op.create_table(
"scenario_schedules",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("scenario_id", sa.String(length=36), nullable=False),
sa.Column("revision_policy", sa.String(length=16), nullable=False),
sa.Column("revision_id", sa.String(length=36), nullable=True),
sa.Column("environment_id", sa.String(length=128), nullable=False),
sa.Column("cron_expr", sa.String(length=128), nullable=False),
sa.Column("timezone", sa.String(length=64), nullable=False),
sa.Column("missed_execution_policy", sa.String(length=16), nullable=False),
sa.Column("policy_id", sa.String(length=36), nullable=True),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("max_instances", sa.Integer(), nullable=False),
sa.Column("misfire_grace_time", sa.Integer(), nullable=False),
sa.Column("created_by", sa.String(length=128), nullable=True),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_scenario_schedules_scenario_id", "scenario_schedules", ["scenario_id"])
# ── scenario_trigger_rules ──
op.create_table(
"scenario_trigger_rules",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("trigger", sa.String(length=64), nullable=False),
sa.Column("scenario_id", sa.String(length=36), nullable=False),
sa.Column("revision_policy", sa.String(length=16), nullable=False),
sa.Column("revision_id", sa.String(length=36), nullable=True),
sa.Column("environment_id", sa.String(length=128), nullable=False),
sa.Column("policy_id", sa.String(length=36), nullable=True),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("conditions", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_scenario_trigger_rules_trigger", "scenario_trigger_rules", ["trigger"])
# ── scenario_automation_policies ──
op.create_table(
"scenario_automation_policies",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("name", sa.String(length=255), nullable=False),
sa.Column("enabled", sa.Boolean(), nullable=False),
sa.Column("workload_class", sa.String(length=32), nullable=False),
sa.Column("max_concurrent_per_env", sa.Integer(), nullable=False),
sa.Column("dedup_window_seconds", sa.Integer(), nullable=False),
sa.Column("overlap_rule", sa.String(length=16), nullable=False),
sa.Column("retention_days", sa.Integer(), nullable=False),
sa.Column("prod_gate_required", sa.Boolean(), nullable=False),
sa.Column("on_repeated_failure", sa.String(length=16), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
# ── scenario_notification_events ──
op.create_table(
"scenario_notification_events",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("event_type", sa.String(length=32), nullable=False),
sa.Column("scenario_id", sa.String(length=36), nullable=False),
sa.Column("run_id", sa.String(length=36), nullable=True),
sa.Column("severity", sa.String(length=16), nullable=False),
sa.Column("payload", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_scenario_notification_events_event_type", "scenario_notification_events", ["event_type"]
)
def downgrade() -> None:
"""Drop scenario automation tables in reverse dependency order."""
op.drop_index("ix_scenario_notification_events_event_type", table_name="scenario_notification_events")
op.drop_table("scenario_notification_events")
op.drop_table("scenario_automation_policies")
op.drop_index("ix_scenario_trigger_rules_trigger", table_name="scenario_trigger_rules")
op.drop_table("scenario_trigger_rules")
op.drop_index("ix_scenario_schedules_scenario_id", table_name="scenario_schedules")
op.drop_table("scenario_schedules")
# #endregion Alembic.ScenarioAutomationTables