migration reorg basically done

This commit is contained in:
2026-02-05 16:56:45 -06:00
parent 343488b3eb
commit c62f41669d
33 changed files with 1569 additions and 355 deletions

View File

@@ -126,7 +126,11 @@ impl RuleLifecycleListener {
for rule in rules {
if rule.enabled {
if let Err(e) = self
.start_timer_from_params(rule.id, Some(rule.trigger_params))
.start_timer_from_params(
rule.id,
"core.intervaltimer",
Some(rule.trigger_params),
)
.await
{
error!("Failed to start timer for rule {}: {}", rule.id, e);
@@ -232,7 +236,7 @@ impl RuleLifecycleListener {
);
if enabled {
self.start_timer_from_params(rule_id, trigger_params)
self.start_timer_from_params(rule_id, &trigger_type, trigger_params)
.await?;
} else {
info!("Rule {} is disabled, not starting timer", rule_id);
@@ -241,6 +245,7 @@ impl RuleLifecycleListener {
RuleLifecycleEvent::RuleEnabled {
rule_id,
rule_ref,
trigger_type,
trigger_params,
..
} => {
@@ -249,7 +254,7 @@ impl RuleLifecycleListener {
rule_id, rule_ref
);
self.start_timer_from_params(rule_id, trigger_params)
self.start_timer_from_params(rule_id, &trigger_type, trigger_params)
.await?;
}
RuleLifecycleEvent::RuleDisabled {
@@ -281,13 +286,21 @@ impl RuleLifecycleListener {
async fn start_timer_from_params(
&self,
rule_id: i64,
trigger_ref: &str,
trigger_params: Option<JsonValue>,
) -> Result<()> {
let params = trigger_params.ok_or_else(|| {
anyhow::anyhow!("Timer trigger requires trigger_params but none provided")
})?;
let config: TimerConfig = serde_json::from_value(params)
info!(
"Parsing timer config for rule {}: trigger_ref='{}', params={}",
rule_id,
trigger_ref,
serde_json::to_string(&params).unwrap_or_else(|_| "<invalid json>".to_string())
);
let config = TimerConfig::from_trigger_params(trigger_ref, params)
.context("Failed to parse trigger_params as TimerConfig")?;
info!(

View File

@@ -1,13 +1,14 @@
//! Shared types for timer sensor
//!
//! Defines timer configurations and common data structures.
//! Updated: 2026-02-05 - Fixed TimerConfig parsing to use trigger_ref
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
/// Timer configuration for different timer types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[serde(untagged)]
pub enum TimerConfig {
/// Interval-based timer (fires every N seconds/minutes/hours)
Interval {
@@ -44,6 +45,75 @@ pub enum TimeUnit {
}
impl TimerConfig {
/// Deserialize TimerConfig from JSON value based on trigger_ref
///
/// Maps trigger_ref to the appropriate TimerConfig variant:
/// - "core.intervaltimer" -> TimerConfig::Interval
/// - "core.crontimer" -> TimerConfig::Cron
/// - "core.datetimetimer" -> TimerConfig::DateTime
pub fn from_trigger_params(
trigger_ref: &str,
params: serde_json::Value,
) -> anyhow::Result<Self> {
match trigger_ref {
"core.intervaltimer" => {
// Parse interval and unit from params
let interval =
params
.get("interval")
.and_then(|v| v.as_u64())
.ok_or_else(|| {
anyhow::anyhow!(
"Missing or invalid 'interval' field in params: {}",
serde_json::to_string(&params)
.unwrap_or_else(|_| "<invalid>".to_string())
)
})?;
let unit = if let Some(unit_val) = params.get("unit") {
serde_json::from_value(unit_val.clone())
.map_err(|e| anyhow::anyhow!("Failed to parse 'unit' field: {}", e))?
} else {
TimeUnit::Seconds
};
Ok(TimerConfig::Interval { interval, unit })
}
"core.crontimer" => {
let expression = params
.get("expression")
.and_then(|v| v.as_str())
.ok_or_else(|| {
anyhow::anyhow!(
"Missing or invalid 'expression' field in params: {}",
serde_json::to_string(&params)
.unwrap_or_else(|_| "<invalid>".to_string())
)
})?
.to_string();
Ok(TimerConfig::Cron { expression })
}
"core.datetimetimer" => {
let fire_at = params.get("fire_at").ok_or_else(|| {
anyhow::anyhow!(
"Missing 'fire_at' field in params: {}",
serde_json::to_string(&params).unwrap_or_else(|_| "<invalid>".to_string())
)
})?;
let fire_at: DateTime<Utc> = serde_json::from_value(fire_at.clone())
.map_err(|e| anyhow::anyhow!("Failed to parse 'fire_at' as DateTime: {}", e))?;
Ok(TimerConfig::DateTime { fire_at })
}
_ => Err(anyhow::anyhow!(
"Unknown timer trigger type: {}",
trigger_ref
)),
}
}
/// Calculate total interval in seconds
#[allow(dead_code)]
pub fn interval_seconds(&self) -> Option<u64> {
@@ -204,39 +274,57 @@ mod tests {
}
#[test]
fn test_timer_config_deserialization_interval() {
let json = r#"{
"type": "interval",
fn test_timer_config_from_trigger_params_interval() {
let params = serde_json::json!({
"interval": 30,
"unit": "seconds"
}"#;
});
let config: TimerConfig = serde_json::from_str(json).unwrap();
let config = TimerConfig::from_trigger_params("core.intervaltimer", params).unwrap();
assert_eq!(config.interval_seconds(), Some(30));
}
#[test]
fn test_timer_config_deserialization_interval_default_unit() {
let json = r#"{
"type": "interval",
fn test_timer_config_from_trigger_params_interval_default_unit() {
let params = serde_json::json!({
"interval": 60
}"#;
});
let config: TimerConfig = serde_json::from_str(json).unwrap();
let config = TimerConfig::from_trigger_params("core.intervaltimer", params).unwrap();
assert_eq!(config.interval_seconds(), Some(60));
}
#[test]
fn test_timer_config_deserialization_cron() {
let json = r#"{
"type": "cron",
fn test_timer_config_from_trigger_params_cron() {
let params = serde_json::json!({
"expression": "0 0 * * *"
}"#;
});
let config: TimerConfig = serde_json::from_str(json).unwrap();
let config = TimerConfig::from_trigger_params("core.crontimer", params).unwrap();
assert_eq!(config.cron_expression(), Some("0 0 * * *"));
}
#[test]
fn test_timer_config_from_trigger_params_datetime() {
let fire_at = chrono::Utc::now();
let params = serde_json::json!({
"fire_at": fire_at
});
let config = TimerConfig::from_trigger_params("core.datetimetimer", params).unwrap();
assert_eq!(config.fire_time(), Some(fire_at));
}
#[test]
fn test_timer_config_from_trigger_params_unknown_trigger() {
let params = serde_json::json!({
"interval": 30
});
let result = TimerConfig::from_trigger_params("unknown.trigger", params);
assert!(result.is_err());
}
#[test]
fn test_rule_lifecycle_event_rule_id() {
let event = RuleLifecycleEvent::RuleCreated {