use super::event::{Event, EventStatus, EventType};
use async_trait::async_trait;
use mofa_kernel::plugin::{
AgentPlugin, PluginContext, PluginMetadata, PluginPriority, PluginResult, PluginState,
PluginType,
};
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::collections::HashMap;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventResponseConfig {
pub enabled: bool,
pub priority: PluginPriority,
pub handled_event_types: Vec<EventType>,
pub max_impact_scope: String, pub rules: HashMap<String, serde_json::Value>,
}
impl Default for EventResponseConfig {
fn default() -> Self {
Self {
enabled: true,
priority: PluginPriority::Normal,
handled_event_types: vec![],
max_impact_scope: "component".to_string(),
rules: HashMap::new(),
}
}
}
#[async_trait]
pub trait EventResponsePlugin: AgentPlugin {
fn config(&self) -> &EventResponseConfig;
async fn update_config(&mut self, config: EventResponseConfig) -> PluginResult<()>;
fn can_handle(&self, event: &Event) -> bool;
async fn handle_event(&mut self, event: Event) -> PluginResult<Event>;
async fn execute_workflow(&self, event: &Event) -> PluginResult<HashMap<String, String>>;
}
pub struct BaseEventResponsePlugin {
metadata: PluginMetadata,
state: PluginState,
config: RwLock<EventResponseConfig>,
handled_event_types: Vec<EventType>,
workflow_steps: Vec<String>,
}
impl BaseEventResponsePlugin {
pub fn new(
id: &str,
name: &str,
handled_event_types: Vec<EventType>,
workflow_steps: Vec<String>,
) -> Self {
let metadata = PluginMetadata::new(id, name, PluginType::Tool)
.with_priority(PluginPriority::Normal)
.with_capability("event-response");
let config = EventResponseConfig {
handled_event_types: handled_event_types.clone(),
..Default::default()
};
Self {
metadata,
state: PluginState::Unloaded,
config: RwLock::new(config),
handled_event_types,
workflow_steps,
}
}
pub fn with_priority(mut self, priority: PluginPriority) -> Self {
self.metadata = self.metadata.with_priority(priority);
self
}
pub fn with_max_impact_scope(self, _scope: &str) -> Self {
self
}
}
#[async_trait]
impl AgentPlugin for BaseEventResponsePlugin {
fn metadata(&self) -> &PluginMetadata {
&self.metadata
}
fn state(&self) -> PluginState {
self.state.clone()
}
async fn load(&mut self, _ctx: &PluginContext) -> PluginResult<()> {
self.state = PluginState::Loading;
self.state = PluginState::Loaded;
Ok(())
}
async fn init_plugin(&mut self) -> PluginResult<()> {
Ok(())
}
async fn start(&mut self) -> PluginResult<()> {
self.state = PluginState::Running;
Ok(())
}
async fn stop(&mut self) -> PluginResult<()> {
self.state = PluginState::Paused;
Ok(())
}
async fn unload(&mut self) -> PluginResult<()> {
self.state = PluginState::Unloaded;
Ok(())
}
async fn execute(&mut self, input: String) -> PluginResult<String> {
let mut event: Event = serde_json::from_str(&input)?;
if !self.can_handle(&event) {
return Err(anyhow::anyhow!("Cannot handle this event type"));
}
event.update_status(EventStatus::Processing);
let processed_event = self.handle_event(event).await?;
processed_event.to_json().map_err(|e| anyhow::anyhow!(e))
}
fn stats(&self) -> HashMap<String, serde_json::Value> {
HashMap::new() }
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
#[async_trait]
impl EventResponsePlugin for BaseEventResponsePlugin {
fn config(&self) -> &EventResponseConfig {
panic!("config() should be implemented by concrete plugin");
}
async fn update_config(&mut self, config: EventResponseConfig) -> PluginResult<()> {
let mut current_config = self.config.write().await;
*current_config = config;
Ok(())
}
fn can_handle(&self, event: &Event) -> bool {
self.handled_event_types.contains(&event.event_type)
}
async fn handle_event(&mut self, mut event: Event) -> PluginResult<Event> {
let workflow_result = self.execute_workflow(&event).await?;
event.update_status(EventStatus::Resolved);
event.data["workflow_result"] = serde_json::json!(workflow_result);
Ok(event)
}
async fn execute_workflow(&self, event: &Event) -> PluginResult<HashMap<String, String>> {
let mut result = HashMap::new();
result.insert("status".to_string(), "handled".to_string());
result.insert(
"message".to_string(),
format!("Event handled by default workflow: {:?}", event.event_type),
);
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::secretary::monitoring::event::*;
#[test]
fn test_base_plugin_creation() {
let plugin = BaseEventResponsePlugin::new(
"test-plugin",
"Test Plugin",
vec![EventType::ServerFault],
vec!["step1".to_string(), "step2".to_string()],
);
assert_eq!(plugin.metadata().id, "test-plugin");
assert_eq!(plugin.metadata().name, "Test Plugin");
assert_eq!(plugin.state(), PluginState::Unloaded);
}
#[tokio::test]
async fn test_can_handle_event() {
let plugin = BaseEventResponsePlugin::new(
"test-plugin",
"Test Plugin",
vec![EventType::ServerFault, EventType::ServiceException],
vec![],
);
let server_fault_event = Event::new(
EventType::ServerFault,
EventPriority::High,
ImpactScope::Instance("server-01".to_string()),
"monitoring".to_string(),
"Server down".to_string(),
serde_json::Value::Null,
);
let network_attack_event = Event::new(
EventType::NetworkAttack,
EventPriority::Emergency,
ImpactScope::System,
"ids".to_string(),
"DDoS attack".to_string(),
serde_json::Value::Null,
);
assert!(plugin.can_handle(&server_fault_event));
assert!(!plugin.can_handle(&network_attack_event));
}
}