use crate::dashboard::events::{journal_to_event_records, log_record_for_event};
use crate::dashboard::model::{
DashboardCriticality, DashboardState, RegistrationState, RuntimeState, SupervisorEdge,
SupervisorEdgeKind, SupervisorNode, SupervisorNodeKind, SupervisorTopology,
TargetConnectionState, TargetProcessIdentity,
};
use crate::id::types::SupervisorPath;
use crate::journal::ring::EventJournal;
use crate::spec::child::Criticality;
use crate::spec::supervisor::SupervisorSpec;
use crate::state::supervisor::SupervisorState;
use std::collections::BTreeMap;
#[derive(Debug, Clone)]
pub struct DashboardStateInput {
pub target_id: String,
pub display_name: String,
pub state_generation: u64,
pub recent_limit: usize,
}
pub fn build_dashboard_state(
input: DashboardStateInput,
spec: &SupervisorSpec,
state: &SupervisorState,
journal: &EventJournal,
) -> DashboardState {
let config_version = spec.config_version.clone();
let recent_events = journal_to_event_records(
&input.target_id,
&config_version,
journal,
input.recent_limit,
);
let recent_logs = recent_events
.iter()
.map(|event| log_record_for_event(event, format!("event {}", event.event_type)))
.collect::<Vec<_>>();
DashboardState {
target: TargetProcessIdentity {
target_id: input.target_id,
display_name: input.display_name,
registration_state: RegistrationState::Active,
connection_state: TargetConnectionState::Registered,
},
topology: topology_from_spec(spec),
runtime_state: runtime_state_rows(state),
recent_events,
recent_logs,
dropped_event_count: journal.dropped_count,
dropped_log_count: 0,
config_version,
generated_at_unix_nanos: state.generated_at_unix_nanos,
state_generation: input.state_generation,
}
}
pub fn topology_from_spec(spec: &SupervisorSpec) -> SupervisorTopology {
let root_path = spec.path.to_string();
let root = SupervisorNode {
node_id: root_path.clone(),
child_id: None,
path: root_path.clone(),
name: "root supervisor".to_owned(),
kind: SupervisorNodeKind::RootSupervisor,
tags: Vec::new(),
criticality: DashboardCriticality::Critical,
state_summary: "root".to_owned(),
diagnostics: BTreeMap::new(),
};
let mut nodes = vec![root.clone()];
let mut edges = Vec::new();
let mut declaration_order = vec![root_path.clone()];
for (index, child) in spec.children.iter().enumerate() {
let child_path = spec.path.join(child.id.value.clone()).to_string();
declaration_order.push(child_path.clone());
nodes.push(SupervisorNode {
node_id: child_path.clone(),
child_id: Some(child.id.to_string()),
path: child_path.clone(),
name: child.name.clone(),
kind: SupervisorNodeKind::ChildTask,
tags: child.tags.clone(),
criticality: criticality(child.criticality),
state_summary: "declared".to_owned(),
diagnostics: BTreeMap::new(),
});
edges.push(SupervisorEdge {
edge_id: format!("parent:{root_path}->{child_path}"),
source_path: root_path.clone(),
target_path: child_path.clone(),
kind: SupervisorEdgeKind::ParentChild,
order: index,
});
for (dependency_index, dependency) in child.dependencies.iter().enumerate() {
let dependency_path = spec.path.join(dependency.value.clone()).to_string();
edges.push(SupervisorEdge {
edge_id: format!("dependency:{dependency_path}->{child_path}"),
source_path: dependency_path,
target_path: child_path.clone(),
kind: SupervisorEdgeKind::Dependency,
order: dependency_index,
});
}
}
SupervisorTopology {
root,
nodes,
edges,
declaration_order,
}
}
pub fn runtime_state_rows(state: &SupervisorState) -> Vec<RuntimeState> {
state
.children
.values()
.map(|child| RuntimeState {
child_path: child.path.to_string(),
lifecycle_state: child.state.as_label().to_owned(),
health: format!("{:?}", child.health).to_lowercase(),
readiness: format!("{:?}", child.readiness).to_lowercase(),
generation: child.generation.value,
attempt: child.attempt.value,
restart_count: child.restart_count,
last_failure: child
.last_failure
.as_ref()
.map(|failure| format!("{failure:?}")),
last_policy_decision: child
.last_policy_decision
.as_ref()
.map(|decision| decision.decision.clone()),
shutdown_state: format!("{:?}", state.shutdown_state).to_lowercase(),
})
.collect()
}
fn criticality(value: Criticality) -> DashboardCriticality {
match value {
Criticality::Critical => DashboardCriticality::Critical,
Criticality::Optional => DashboardCriticality::Standard,
}
}
pub fn declared_state_from_spec(spec: &SupervisorSpec) -> SupervisorState {
spec.children.iter().fold(
SupervisorState::new(
SupervisorPath::root(),
crate::event::time::EventSequence::new(1),
1,
),
|state, child| {
let path = spec.path.join(child.id.value.clone());
state.with_child(crate::state::child::ChildState::declared(
path,
child.id.clone(),
child.name.clone(),
))
},
)
}