use crate::api::schema::{AgentViewClearParams, AgentViewSetParams, ResponseResult}; use crate::app::App; use super::responses::{encode_error, encode_success}; impl App { pub(super) fn handle_agent_view_set( &mut self, id: String, mut params: AgentViewSetParams, ) -> String { if let Err(message) = crate::app::agent_view::validate_agent_view(&mut params) { return encode_error(id, "invalid_agent_view", message); } if let Some(plugin_id) = params.source.strip_prefix("plugin:") { let Some(plugin_id) = super::plugins::normalize_plugin_id(plugin_id) else { return encode_error( id, "invalid_agent_view", "plugin-owned agent view source has an invalid plugin id", ); }; let Some(plugin) = self.state.installed_plugins.get(&plugin_id) else { return encode_error(id, "plugin_not_found", "plugin not found"); }; if !plugin.enabled { return encode_error(id, "plugin_disabled", "plugin is disabled"); } } let source = params.source.clone(); let label = params.label.clone(); self.replace_agent_view_override(Some(params)); encode_success( id, ResponseResult::AgentView { active: true, source: Some(source), label, }, ) } pub(super) fn handle_agent_view_clear( &mut self, id: String, params: AgentViewClearParams, ) -> String { let source = match params.source { Some(source) => match crate::app::agent_view::validate_agent_view_source(&source) { Ok(source) => Some(source), Err(message) => return encode_error(id, "invalid_agent_view", message), }, None => None, }; if source.as_deref().is_none_or(|source| { self.state .agent_view_override .as_ref() .is_some_and(|active| active.source == source) }) { self.replace_agent_view_override(None); } let active = self.state.agent_view_override.as_ref(); encode_success( id, ResponseResult::AgentView { active: active.is_some(), source: active.map(|view| view.source.clone()), label: active.and_then(|view| view.label.clone()), }, ) } pub(crate) fn clear_agent_view_for_source(&mut self, source: &str) -> bool { if self .state .agent_view_override .as_ref() .is_some_and(|active| active.source == source) { self.replace_agent_view_override(None); true } else { false } } fn replace_agent_view_override(&mut self, view: Option) { self.state.agent_view_override = view; } } #[cfg(test)] mod tests { use super::*; use crate::api::schema::{ AgentViewBuiltinField, AgentViewField, AgentViewFilter, AgentViewValue, }; fn test_app() -> App { let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); App::new( &crate::config::Config::default(), crate::app::AppPolicy::TEST, None, api_rx, crate::api::EventHub::default(), ) } fn working_view(source: &str) -> AgentViewSetParams { AgentViewSetParams { source: source.to_string(), label: Some("working".to_string()), filter: Some(AgentViewFilter::Eq { field: AgentViewField::Builtin(AgentViewBuiltinField::Status), value: AgentViewValue::String("working".to_string()), }), sort: Vec::new(), } } #[test] fn set_and_source_guarded_clear_replace_transient_view() { let mut app = test_app(); let set = app.handle_agent_view_set("set".to_string(), working_view("example.views")); let set: crate::api::schema::SuccessResponse = serde_json::from_str(&set).unwrap(); assert_eq!( set.result, ResponseResult::AgentView { active: true, source: Some("example.views".to_string()), label: Some("working".to_string()), } ); assert_eq!( app.state .agent_view_override .as_ref() .map(|view| view.source.as_str()), Some("example.views") ); app.handle_agent_view_clear( "wrong-source".to_string(), AgentViewClearParams { source: Some("other.views".to_string()), }, ); assert!(app.state.agent_view_override.is_some()); app.handle_agent_view_clear( "right-source".to_string(), AgentViewClearParams { source: Some("example.views".to_string()), }, ); assert!(app.state.agent_view_override.is_none()); } #[test] fn invalid_view_does_not_replace_active_view() { let mut app = test_app(); app.handle_agent_view_set("set".to_string(), working_view("example.views")); let mut invalid = working_view("example.other"); invalid.filter = Some(AgentViewFilter::Any { filters: Vec::new(), }); let response = app.handle_agent_view_set("invalid".to_string(), invalid); let response: crate::api::schema::ErrorResponse = serde_json::from_str(&response).unwrap(); assert_eq!(response.error.code, "invalid_agent_view"); assert_eq!( app.state .agent_view_override .as_ref() .map(|view| view.source.as_str()), Some("example.views") ); } }