feat(db): add graylog schema and processed_tickets multi-source migration

This commit is contained in:
thibaud-lclr 2026-04-17 14:36:57 +02:00
parent a243064992
commit 941bde4f7c
2 changed files with 136 additions and 1 deletions

View file

@ -0,0 +1,127 @@
BEGIN;
PRAGMA foreign_keys = OFF;
DROP INDEX IF EXISTS idx_processed_tickets_tracker_artifact_unique;
CREATE TABLE processed_tickets_new (
id TEXT PRIMARY KEY,
tracker_id TEXT REFERENCES watched_trackers(id) ON DELETE CASCADE,
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
source TEXT NOT NULL DEFAULT 'tuleap',
source_ref TEXT,
artifact_id INTEGER NOT NULL,
artifact_title TEXT NOT NULL,
artifact_data TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'Pending',
analyst_report TEXT,
developer_report TEXT,
worktree_path TEXT,
branch_name TEXT,
detected_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
processed_at TEXT
);
INSERT INTO processed_tickets_new (
id,
tracker_id,
project_id,
source,
source_ref,
artifact_id,
artifact_title,
artifact_data,
status,
analyst_report,
developer_report,
worktree_path,
branch_name,
detected_at,
processed_at
)
SELECT
pt.id,
pt.tracker_id,
wt.project_id,
'tuleap',
NULL,
pt.artifact_id,
pt.artifact_title,
pt.artifact_data,
pt.status,
pt.analyst_report,
pt.developer_report,
pt.worktree_path,
pt.branch_name,
pt.detected_at,
pt.processed_at
FROM processed_tickets pt
LEFT JOIN watched_trackers wt ON wt.id = pt.tracker_id;
DROP TABLE processed_tickets;
ALTER TABLE processed_tickets_new RENAME TO processed_tickets;
CREATE UNIQUE INDEX idx_processed_tickets_tracker_artifact_unique
ON processed_tickets(tracker_id, artifact_id)
WHERE tracker_id IS NOT NULL;
CREATE INDEX idx_processed_tickets_project_detected
ON processed_tickets(project_id, detected_at DESC);
CREATE INDEX idx_processed_tickets_source_ref
ON processed_tickets(source, source_ref);
CREATE TABLE graylog_credentials (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE,
base_url TEXT NOT NULL,
api_token_encrypted TEXT NOT NULL,
analyst_agent_id TEXT NOT NULL REFERENCES agents(id),
developer_agent_id TEXT NOT NULL REFERENCES agents(id),
stream_id TEXT,
query_filter TEXT NOT NULL DEFAULT '',
polling_interval_minutes INTEGER NOT NULL DEFAULT 10,
lookback_minutes INTEGER NOT NULL DEFAULT 30,
score_threshold INTEGER NOT NULL DEFAULT 70,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
CREATE TABLE graylog_subjects (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
subject_key TEXT NOT NULL,
source TEXT NOT NULL,
normalized_message TEXT NOT NULL,
first_seen_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
last_score INTEGER NOT NULL DEFAULT 0,
active_ticket_id TEXT REFERENCES processed_tickets(id),
status TEXT NOT NULL DEFAULT 'idle',
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
UNIQUE(project_id, subject_key)
);
CREATE TABLE graylog_detections (
id TEXT PRIMARY KEY,
subject_id TEXT NOT NULL REFERENCES graylog_subjects(id) ON DELETE CASCADE,
window_start TEXT NOT NULL,
window_end TEXT NOT NULL,
critical_count INTEGER NOT NULL DEFAULT 0,
error_count INTEGER NOT NULL DEFAULT 0,
warning_count INTEGER NOT NULL DEFAULT 0,
total_count INTEGER NOT NULL DEFAULT 0,
last_seen_at TEXT NOT NULL,
score INTEGER NOT NULL,
triggered INTEGER NOT NULL DEFAULT 0,
triggered_ticket_id TEXT REFERENCES processed_tickets(id),
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
);
CREATE INDEX idx_graylog_detections_subject_created
ON graylog_detections(subject_id, created_at DESC);
PRAGMA foreign_keys = ON;
COMMIT;

View file

@ -9,6 +9,7 @@ const MIGRATION_005: &str = include_str!("../migrations/005_orchestration_module
const MIGRATION_006: &str = include_str!("../migrations/006_processed_tickets_unique_index.sql");
const MIGRATION_007: &str = include_str!("../migrations/007_normalize_timestamps_rfc3339.sql");
const MIGRATION_008: &str = include_str!("../migrations/008_project_scoped_tuleap_credentials.sql");
const MIGRATION_009: &str = include_str!("../migrations/009_graylog_auto_resolve.sql");
pub fn init(db_path: &Path) -> Result<Connection> {
let conn = Connection::open(db_path)?;
@ -66,6 +67,10 @@ fn migrate(conn: &Connection) -> Result<()> {
conn.execute_batch(MIGRATION_008)?;
conn.pragma_update(None, "user_version", 8)?;
}
if version < 9 {
conn.execute_batch(MIGRATION_009)?;
conn.pragma_update(None, "user_version", 9)?;
}
Ok(())
}
@ -91,6 +96,9 @@ mod tests {
tables,
vec![
"agents",
"graylog_credentials",
"graylog_detections",
"graylog_subjects",
"notifications",
"processed_tickets",
"project_agent_tasks",
@ -121,7 +129,7 @@ mod tests {
let version: i32 = conn
.pragma_query_value(None, "user_version", |row| row.get(0))
.unwrap();
assert_eq!(version, 8);
assert_eq!(version, 9);
}
#[test]