diff --git a/src/cli/readarr/list_command_handler.rs b/src/cli/readarr/list_command_handler.rs index fb7c2383..7b2f570f 100644 --- a/src/cli/readarr/list_command_handler.rs +++ b/src/cli/readarr/list_command_handler.rs @@ -29,6 +29,8 @@ pub enum ReadarrListCommand { )] output_in_log_format: bool, }, + #[command(about = "List all Readarr metadata profiles")] + MetadataProfiles, #[command(about = "List all Readarr quality profiles")] QualityProfiles, #[command(about = "List all queued events")] @@ -90,6 +92,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, ReadarrListCommand> for ReadarrListComman serde_json::to_string_pretty(&logs)? } } + ReadarrListCommand::MetadataProfiles => { + let resp = self + .network + .handle_network_event(ReadarrEvent::GetMetadataProfiles.into()) + .await?; + serde_json::to_string_pretty(&resp)? + } ReadarrListCommand::QualityProfiles => { let resp = self .network diff --git a/src/cli/readarr/list_command_handler_tests.rs b/src/cli/readarr/list_command_handler_tests.rs index bd2bb76b..2e32c43d 100644 --- a/src/cli/readarr/list_command_handler_tests.rs +++ b/src/cli/readarr/list_command_handler_tests.rs @@ -25,7 +25,14 @@ mod tests { #[rstest] fn test_list_commands_have_no_arg_requirements( - #[values("disk-space", "quality-profiles", "queued-events", "tasks", "updates")] + #[values( + "disk-space", + "metadata-profiles", + "quality-profiles", + "queued-events", + "tasks", + "updates" + )] subcommand: &str, ) { let result = Cli::command().try_get_matches_from(["managarr", "readarr", "list", subcommand]); @@ -162,6 +169,32 @@ mod tests { assert_str_eq!(result.unwrap(), "[\n \"readarr log line\"\n]"); } + #[tokio::test] + async fn test_handle_list_metadata_profiles_command() { + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::(ReadarrEvent::GetMetadataProfiles.into())) + .times(1) + .returning(|_| { + Ok(Serdeable::Readarr(ReadarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::test_default())); + let list_metadata_profiles_command = ReadarrListCommand::MetadataProfiles; + + let result = ReadarrListCommandHandler::with( + &app_arc, + list_metadata_profiles_command, + &mut mock_network, + ) + .handle() + .await; + + assert_ok!(&result); + } + #[tokio::test] async fn test_handle_list_quality_profiles_command() { let mut mock_network = MockNetworkTrait::new(); diff --git a/src/network/readarr_network/mod.rs b/src/network/readarr_network/mod.rs index 1b1b0cb8..5ebf41c2 100644 --- a/src/network/readarr_network/mod.rs +++ b/src/network/readarr_network/mod.rs @@ -3,7 +3,7 @@ use log::info; use super::{NetworkEvent, NetworkResource}; use crate::models::readarr_models::{ReadarrSerdeable, ReadarrTaskName}; -use crate::models::servarr_models::QualityProfile; +use crate::models::servarr_models::{MetadataProfile, QualityProfile}; use crate::network::{Network, RequestMethod}; mod system; @@ -17,6 +17,7 @@ pub enum ReadarrEvent { GetDiskSpace, GetHostConfig, GetLogs(u64), + GetMetadataProfiles, GetQualityProfiles, GetQueuedEvents, GetSecurityConfig, @@ -35,6 +36,7 @@ impl NetworkResource for ReadarrEvent { ReadarrEvent::GetDiskSpace => "/diskspace", ReadarrEvent::HealthCheck => "/health", ReadarrEvent::GetLogs(_) => "/log", + ReadarrEvent::GetMetadataProfiles => "/metadataprofile", ReadarrEvent::GetQualityProfiles => "/qualityprofile", ReadarrEvent::GetStatus => "/system/status", ReadarrEvent::GetTasks => "/system/task", @@ -67,6 +69,10 @@ impl Network<'_, '_> { .get_readarr_logs(events) .await .map(ReadarrSerdeable::from), + ReadarrEvent::GetMetadataProfiles => self + .get_readarr_metadata_profiles() + .await + .map(ReadarrSerdeable::from), ReadarrEvent::GetQualityProfiles => self .get_readarr_quality_profiles() .await @@ -108,6 +114,24 @@ impl Network<'_, '_> { .await } + async fn get_readarr_metadata_profiles(&mut self) -> Result> { + info!("Fetching Readarr metadata profiles"); + let event = ReadarrEvent::GetMetadataProfiles; + + let request_props = self + .request_props_from(event, RequestMethod::Get, None::<()>, None, None) + .await; + + self + .handle_request::<(), Vec>(request_props, |metadata_profiles, mut app| { + app.data.readarr_data.metadata_profile_map = metadata_profiles + .into_iter() + .map(|profile| (profile.id, profile.name)) + .collect(); + }) + .await + } + async fn get_readarr_quality_profiles(&mut self) -> Result> { info!("Fetching Readarr quality profiles"); let event = ReadarrEvent::GetQualityProfiles; diff --git a/src/network/readarr_network/readarr_network_tests.rs b/src/network/readarr_network/readarr_network_tests.rs index 732b375c..6bb3b8a9 100644 --- a/src/network/readarr_network/readarr_network_tests.rs +++ b/src/network/readarr_network/readarr_network_tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use crate::models::readarr_models::ReadarrSerdeable; - use crate::models::servarr_models::QualityProfile; + use crate::models::servarr_models::{MetadataProfile, QualityProfile}; use crate::network::network_tests::test_utils::{MockServarrApi, test_network}; use crate::network::{NetworkEvent, NetworkResource, readarr_network::ReadarrEvent}; use pretty_assertions::{assert_eq, assert_str_eq}; @@ -16,6 +16,7 @@ mod tests { #[case(ReadarrEvent::GetDiskSpace, "/diskspace")] #[case(ReadarrEvent::HealthCheck, "/health")] #[case(ReadarrEvent::GetLogs(500), "/log")] + #[case(ReadarrEvent::GetMetadataProfiles, "/metadataprofile")] #[case(ReadarrEvent::GetQualityProfiles, "/qualityprofile")] #[case(ReadarrEvent::GetStatus, "/system/status")] #[case(ReadarrEvent::GetTasks, "/system/task")] @@ -64,6 +65,93 @@ mod tests { assert_err!(result); } + #[tokio::test] + async fn test_handle_get_metadata_profiles_event() { + let metadata_profiles_json = json!([ + { + "id": 1, + "name": "Standard" + }, + { + "id": 2, + "name": "None" + } + ]); + let response: Vec = + serde_json::from_value(metadata_profiles_json.clone()).unwrap(); + let (mock, app, _server) = MockServarrApi::get() + .returns(metadata_profiles_json) + .build_for(ReadarrEvent::GetMetadataProfiles) + .await; + app.lock().await.server_tabs.set_index(3); + let mut network = test_network(&app); + + let result = network + .handle_readarr_event(ReadarrEvent::GetMetadataProfiles) + .await; + + mock.assert_async().await; + + let ReadarrSerdeable::MetadataProfiles(metadata_profiles) = result.unwrap() else { + panic!("Expected MetadataProfiles"); + }; + + assert_eq!(metadata_profiles, response); + + let app = app.lock().await; + + assert_some_eq_x!( + app.data.readarr_data.metadata_profile_map.get_by_left(&1), + &"Standard".to_owned() + ); + assert_some_eq_x!( + app.data.readarr_data.metadata_profile_map.get_by_left(&2), + &"None".to_owned() + ); + } + + #[tokio::test] + async fn test_handle_get_metadata_profiles_event_empty_response() { + let (mock, app, _server) = MockServarrApi::get() + .returns(json!([])) + .build_for(ReadarrEvent::GetMetadataProfiles) + .await; + app.lock().await.server_tabs.set_index(3); + let mut network = test_network(&app); + + let result = network + .handle_readarr_event(ReadarrEvent::GetMetadataProfiles) + .await; + + mock.assert_async().await; + + let ReadarrSerdeable::MetadataProfiles(metadata_profiles) = result.unwrap() else { + panic!("Expected MetadataProfiles"); + }; + + assert_is_empty!(metadata_profiles); + assert_is_empty!(app.lock().await.data.readarr_data.metadata_profile_map); + } + + #[tokio::test] + async fn test_handle_get_metadata_profiles_event_failure() { + let (mock, app, _server) = MockServarrApi::get() + .returns(json!({})) + .status(500) + .build_for(ReadarrEvent::GetMetadataProfiles) + .await; + app.lock().await.server_tabs.set_index(3); + let mut network = test_network(&app); + + let result = network + .handle_readarr_event(ReadarrEvent::GetMetadataProfiles) + .await; + + mock.assert_async().await; + assert_err!(result); + assert_is_empty!(app.lock().await.data.readarr_data.metadata_profile_map); + } + #[tokio::test] async fn test_handle_get_quality_profiles_event() { let quality_profiles_json = json!([