diff --git a/src/cli/readarr/mod.rs b/src/cli/readarr/mod.rs index b57cd562..529871b4 100644 --- a/src/cli/readarr/mod.rs +++ b/src/cli/readarr/mod.rs @@ -70,6 +70,17 @@ pub enum ReadarrCommand { )] task_name: ReadarrTaskName, }, + #[command( + about = "Toggle monitoring for the specified author corresponding to the given author ID" + )] + ToggleAuthorMonitoring { + #[arg( + long, + help = "The Readarr ID of the author to toggle monitoring on", + required = true + )] + author_id: i64, + }, } impl From for Command { @@ -138,6 +149,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, ReadarrCommand> for ReadarrCliHandler<'a, .await?; serde_json::to_string_pretty(&resp)? } + ReadarrCommand::ToggleAuthorMonitoring { author_id } => { + let resp = self + .network + .handle_network_event(ReadarrEvent::ToggleAuthorMonitoring(author_id).into()) + .await?; + serde_json::to_string_pretty(&resp)? + } }; Ok(result) diff --git a/src/cli/readarr/readarr_command_tests.rs b/src/cli/readarr/readarr_command_tests.rs index c0b25d9e..e702c4db 100644 --- a/src/cli/readarr/readarr_command_tests.rs +++ b/src/cli/readarr/readarr_command_tests.rs @@ -76,6 +76,31 @@ mod tests { assert_ok!(&result); } + + #[test] + fn test_toggle_author_monitoring_requires_author_id() { + let result = + Cli::command().try_get_matches_from(["managarr", "readarr", "toggle-author-monitoring"]); + + assert_err!(&result); + assert_eq!( + result.unwrap_err().kind(), + ErrorKind::MissingRequiredArgument + ); + } + + #[test] + fn test_toggle_author_monitoring_requirements_satisfied() { + let result = Cli::command().try_get_matches_from([ + "managarr", + "readarr", + "toggle-author-monitoring", + "--author-id", + "1", + ]); + + assert_ok!(&result); + } } mod handler { @@ -149,5 +174,34 @@ mod tests { assert_ok!(&result); } + + #[tokio::test] + async fn test_toggle_author_monitoring_command() { + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::( + ReadarrEvent::ToggleAuthorMonitoring(1).into(), + )) + .times(1) + .returning(|_| { + Ok(Serdeable::Readarr(ReadarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::test_default())); + let toggle_author_monitoring_command = + ReadarrCommand::ToggleAuthorMonitoring { author_id: 1 }; + + let result = ReadarrCliHandler::with( + &app_arc, + toggle_author_monitoring_command, + &mut mock_network, + ) + .handle() + .await; + + assert_ok!(&result); + } } } diff --git a/src/network/readarr_network/library/authors/mod.rs b/src/network/readarr_network/library/authors/mod.rs index 0339da47..0c9e8ca6 100644 --- a/src/network/readarr_network/library/authors/mod.rs +++ b/src/network/readarr_network/library/authors/mod.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use log::{debug, info}; +use log::{debug, info, warn}; use serde_json::{Value, json}; use crate::models::Route; @@ -277,4 +277,67 @@ impl Network<'_, '_> { result } + + pub(in crate::network::readarr_network) async fn toggle_author_monitoring( + &mut self, + author_id: i64, + ) -> Result<()> { + let event = ReadarrEvent::ToggleAuthorMonitoring(author_id); + + let detail_event = ReadarrEvent::GetAuthorDetails(author_id); + info!("Toggling author monitoring for author with ID: {author_id}"); + info!("Fetching author details for author with ID: {author_id}"); + + let request_props = self + .request_props_from( + detail_event, + RequestMethod::Get, + None::<()>, + Some(format!("/{author_id}")), + None, + ) + .await; + + let mut response = String::new(); + + self + .handle_request::<(), Value>(request_props, |detailed_author_body, _| { + response = detailed_author_body.to_string() + }) + .await?; + + info!("Constructing toggle author monitoring body"); + + match serde_json::from_str::(&response) { + Ok(mut detailed_author_body) => { + let monitored = detailed_author_body + .get("monitored") + .unwrap() + .as_bool() + .unwrap(); + + *detailed_author_body.get_mut("monitored").unwrap() = json!(!monitored); + + debug!("Toggle author monitoring body: {detailed_author_body:?}"); + + let request_props = self + .request_props_from( + event, + RequestMethod::Put, + Some(detailed_author_body), + Some(format!("/{author_id}")), + None, + ) + .await; + + self + .handle_request::(request_props, |_, _| ()) + .await + } + Err(_) => { + warn!("Request for detailed author body was interrupted"); + Ok(()) + } + } + } } diff --git a/src/network/readarr_network/library/authors/readarr_authors_network_tests.rs b/src/network/readarr_network/library/authors/readarr_authors_network_tests.rs index 83ecfcc7..34199ad8 100644 --- a/src/network/readarr_network/library/authors/readarr_authors_network_tests.rs +++ b/src/network/readarr_network/library/authors/readarr_authors_network_tests.rs @@ -680,4 +680,81 @@ mod tests { mock.assert_async().await; assert_err!(result); } + + #[rstest] + #[case(true, false)] + #[case(false, true)] + #[tokio::test] + async fn test_handle_toggle_author_monitoring_event( + #[case] initial_monitored: bool, + #[case] expected_monitored: bool, + ) { + let mut author_json: Value = serde_json::from_str(AUTHOR_JSON).unwrap(); + *author_json.get_mut("monitored").unwrap() = json!(initial_monitored); + let mut expected_body = author_json.clone(); + *expected_body.get_mut("monitored").unwrap() = json!(expected_monitored); + let (async_details_server, app, mut server) = MockServarrApi::get() + .returns(author_json) + .path("/1") + .build_for(ReadarrEvent::GetAuthorDetails(1)) + .await; + let async_toggle_server = server + .mock( + "PUT", + format!( + "/api/v1{}/1", + ReadarrEvent::ToggleAuthorMonitoring(1).resource() + ) + .as_str(), + ) + .with_status(202) + .match_header("X-Api-Key", "test1234") + .match_body(Matcher::Json(expected_body)) + .create_async() + .await; + app.lock().await.server_tabs.set_index(3); + let mut network = test_network(&app); + + let result = network + .handle_readarr_event(ReadarrEvent::ToggleAuthorMonitoring(1)) + .await; + + async_details_server.assert_async().await; + async_toggle_server.assert_async().await; + assert_ok!(result); + } + + #[tokio::test] + async fn test_handle_toggle_author_monitoring_event_failure() { + let (async_details_server, app, mut server) = MockServarrApi::get() + .returns(serde_json::from_str(AUTHOR_JSON).unwrap()) + .status(404) + .path("/1") + .build_for(ReadarrEvent::GetAuthorDetails(1)) + .await; + let async_toggle_server = server + .mock( + "PUT", + format!( + "/api/v1{}/1", + ReadarrEvent::ToggleAuthorMonitoring(1).resource() + ) + .as_str(), + ) + .with_status(202) + .match_header("X-Api-Key", "test1234") + .expect(0) + .create_async() + .await; + app.lock().await.server_tabs.set_index(3); + let mut network = test_network(&app); + + let result = network + .handle_readarr_event(ReadarrEvent::ToggleAuthorMonitoring(1)) + .await; + + async_details_server.assert_async().await; + async_toggle_server.assert_async().await; + assert_err!(result); + } } diff --git a/src/network/readarr_network/mod.rs b/src/network/readarr_network/mod.rs index 99738e84..fc2fc0cb 100644 --- a/src/network/readarr_network/mod.rs +++ b/src/network/readarr_network/mod.rs @@ -48,6 +48,7 @@ pub enum ReadarrEvent { ListAuthors, SearchNewAuthor(String), StartTask(ReadarrTaskName), + ToggleAuthorMonitoring(i64), } impl NetworkResource for ReadarrEvent { @@ -57,7 +58,8 @@ impl NetworkResource for ReadarrEvent { | ReadarrEvent::DeleteAuthor(_) | ReadarrEvent::EditAuthor(_) | ReadarrEvent::GetAuthorDetails(_) - | ReadarrEvent::ListAuthors => "/author", + | ReadarrEvent::ListAuthors + | ReadarrEvent::ToggleAuthorMonitoring(_) => "/author", ReadarrEvent::SearchNewAuthor(_) => "/author/lookup", ReadarrEvent::GetQueuedEvents | ReadarrEvent::StartTask(_) => "/command", ReadarrEvent::GetHostConfig | ReadarrEvent::GetSecurityConfig => "/config/host", @@ -163,6 +165,10 @@ impl Network<'_, '_> { .start_readarr_task(task_name) .await .map(ReadarrSerdeable::from), + ReadarrEvent::ToggleAuthorMonitoring(author_id) => self + .toggle_author_monitoring(author_id) + .await + .map(ReadarrSerdeable::from), } } diff --git a/src/network/readarr_network/readarr_network_tests.rs b/src/network/readarr_network/readarr_network_tests.rs index ae5ac1b2..05e1d7ce 100644 --- a/src/network/readarr_network/readarr_network_tests.rs +++ b/src/network/readarr_network/readarr_network_tests.rs @@ -20,6 +20,7 @@ mod tests { #[case(ReadarrEvent::EditAuthor(EditAuthorParams::default()), "/author")] #[case(ReadarrEvent::GetAuthorDetails(1), "/author")] #[case(ReadarrEvent::ListAuthors, "/author")] + #[case(ReadarrEvent::ToggleAuthorMonitoring(1), "/author")] #[case(ReadarrEvent::SearchNewAuthor(String::new()), "/author/lookup")] #[case(ReadarrEvent::GetQueuedEvents, "/command")] #[case(ReadarrEvent::StartTask(Default::default()), "/command")]