diff --git a/src/models/radarr_models.rs b/src/models/radarr_models.rs index 283eeef..bb838b6 100644 --- a/src/models/radarr_models.rs +++ b/src/models/radarr_models.rs @@ -56,11 +56,6 @@ pub struct AddOptions { pub search_for_movie: bool, } -#[derive(Default, Serialize, Debug)] -pub struct AddRootFolderBody { - pub path: String, -} - #[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct BlocklistResponse { pub records: Vec, diff --git a/src/models/servarr_data/sonarr/sonarr_data.rs b/src/models/servarr_data/sonarr/sonarr_data.rs index cd274d2..2c1b820 100644 --- a/src/models/servarr_data/sonarr/sonarr_data.rs +++ b/src/models/servarr_data/sonarr/sonarr_data.rs @@ -21,6 +21,7 @@ mod sonarr_data_tests; pub struct SonarrData { pub blocklist: StatefulTable, pub downloads: StatefulTable, + pub edit_root_folder: Option, pub history: StatefulTable, pub indexers: StatefulTable, pub indexer_settings: Option, @@ -41,6 +42,7 @@ impl Default for SonarrData { SonarrData { blocklist: StatefulTable::default(), downloads: StatefulTable::default(), + edit_root_folder: None, history: StatefulTable::default(), indexers: StatefulTable::default(), indexer_settings: None, diff --git a/src/models/servarr_data/sonarr/sonarr_data_tests.rs b/src/models/servarr_data/sonarr/sonarr_data_tests.rs index 60fecc2..9be5338 100644 --- a/src/models/servarr_data/sonarr/sonarr_data_tests.rs +++ b/src/models/servarr_data/sonarr/sonarr_data_tests.rs @@ -36,6 +36,7 @@ mod tests { assert!(sonarr_data.blocklist.is_empty()); assert!(sonarr_data.downloads.is_empty()); + assert!(sonarr_data.edit_root_folder.is_none()); assert!(sonarr_data.history.is_empty()); assert!(sonarr_data.indexers.is_empty()); assert!(sonarr_data.indexer_settings.is_none()); diff --git a/src/models/servarr_models.rs b/src/models/servarr_models.rs index 12c5234..b7d92a0 100644 --- a/src/models/servarr_models.rs +++ b/src/models/servarr_models.rs @@ -11,6 +11,11 @@ use super::HorizontallyScrollableText; #[path = "servarr_models_tests.rs"] mod servarr_models_tests; +#[derive(Default, Serialize, Debug)] +pub struct AddRootFolderBody { + pub path: String, +} + #[derive(Serialize, Deserialize, Default, PartialEq, Eq, Clone, Copy, Debug, ValueEnum)] #[serde(rename_all = "camelCase")] pub enum AuthenticationMethod { diff --git a/src/network/radarr_network.rs b/src/network/radarr_network.rs index 0b9e087..40a50b0 100644 --- a/src/network/radarr_network.rs +++ b/src/network/radarr_network.rs @@ -7,11 +7,11 @@ use serde_json::{json, Value}; use urlencoding::encode; use crate::models::radarr_models::{ - AddMovieBody, AddMovieSearchResult, AddOptions, AddRootFolderBody, BlocklistResponse, Collection, - CollectionMovie, CommandBody, Credit, CreditType, DeleteMovieParams, DiskSpace, DownloadRecord, - DownloadsResponse, EditCollectionParams, EditIndexerParams, EditMovieParams, IndexerSettings, - IndexerTestResult, Movie, MovieCommandBody, MovieHistoryItem, RadarrSerdeable, - ReleaseDownloadBody, SystemStatus, Tag, Task, TaskName, Update, + AddMovieBody, AddMovieSearchResult, AddOptions, BlocklistResponse, Collection, CollectionMovie, + CommandBody, Credit, CreditType, DeleteMovieParams, DiskSpace, DownloadRecord, DownloadsResponse, + EditCollectionParams, EditIndexerParams, EditMovieParams, IndexerSettings, IndexerTestResult, + Movie, MovieCommandBody, MovieHistoryItem, RadarrSerdeable, ReleaseDownloadBody, SystemStatus, + Tag, Task, TaskName, Update, }; use crate::models::servarr_data::radarr::modals::{ AddMovieModal, EditCollectionModal, EditIndexerModal, EditMovieModal, IndexerTestResultModalItem, @@ -19,7 +19,8 @@ use crate::models::servarr_data::radarr::modals::{ }; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::servarr_models::{ - HostConfig, Indexer, LogResponse, QualityProfile, QueueEvent, Release, RootFolder, SecurityConfig, + AddRootFolderBody, HostConfig, Indexer, LogResponse, QualityProfile, QueueEvent, Release, + RootFolder, SecurityConfig, }; use crate::models::stateful_table::StatefulTable; use crate::models::{HorizontallyScrollableText, Route, Scrollable, ScrollableText}; @@ -143,9 +144,10 @@ impl<'a, 'b> Network<'a, 'b> { ) -> Result { match radarr_event { RadarrEvent::AddMovie(body) => self.add_movie(body).await.map(RadarrSerdeable::from), - RadarrEvent::AddRootFolder(path) => { - self.add_root_folder(path).await.map(RadarrSerdeable::from) - } + RadarrEvent::AddRootFolder(path) => self + .add_radarr_root_folder(path) + .await + .map(RadarrSerdeable::from), RadarrEvent::AddTag(tag) => self.add_tag(tag).await.map(RadarrSerdeable::from), RadarrEvent::ClearBlocklist => self .clear_radarr_blocklist() @@ -371,7 +373,7 @@ impl<'a, 'b> Network<'a, 'b> { .await } - async fn add_root_folder(&mut self, root_folder: Option) -> Result { + async fn add_radarr_root_folder(&mut self, root_folder: Option) -> Result { info!("Adding new root folder to Radarr"); let event = RadarrEvent::AddRootFolder(None); let body = if let Some(path) = root_folder { diff --git a/src/network/radarr_network_tests.rs b/src/network/radarr_network_tests.rs index b1f6219..709fcd8 100644 --- a/src/network/radarr_network_tests.rs +++ b/src/network/radarr_network_tests.rs @@ -3633,7 +3633,7 @@ mod test { } #[tokio::test] - async fn test_handle_add_root_folder_event() { + async fn test_handle_add_radarr_root_folder_event() { let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Post, Some(json!({ @@ -3666,7 +3666,7 @@ mod test { } #[tokio::test] - async fn test_handle_add_root_folder_event_uses_provided_path() { + async fn test_handle_add_radarr_root_folder_event_uses_provided_path() { let (async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Post, Some(json!({ diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index b51406d..0f7bb6b 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -10,8 +10,8 @@ use crate::{ sonarr_data::ActiveSonarrBlock, }, servarr_models::{ - HostConfig, Indexer, LogResponse, QualityProfile, QueueEvent, Release, RootFolder, - SecurityConfig, + AddRootFolderBody, HostConfig, Indexer, LogResponse, QualityProfile, QueueEvent, Release, + RootFolder, SecurityConfig, }, sonarr_models::{ BlocklistResponse, DownloadRecord, DownloadsResponse, Episode, IndexerSettings, Series, @@ -31,6 +31,7 @@ mod sonarr_network_tests; #[derive(Debug, Eq, PartialEq, Clone)] pub enum SonarrEvent { + AddRootFolder(Option), ClearBlocklist, DeleteBlocklistItem(Option), DeleteDownload(Option), @@ -74,7 +75,9 @@ impl NetworkResource for SonarrEvent { SonarrEvent::GetLogs(_) => "/log", SonarrEvent::GetQualityProfiles => "/qualityprofile", SonarrEvent::GetQueuedEvents => "/command", - SonarrEvent::GetRootFolders | SonarrEvent::DeleteRootFolder(_) => "/rootfolder", + SonarrEvent::GetRootFolders + | SonarrEvent::DeleteRootFolder(_) + | SonarrEvent::AddRootFolder(_) => "/rootfolder", SonarrEvent::GetSeasonReleases(_) | SonarrEvent::GetEpisodeReleases(_) => "/release", SonarrEvent::GetSeriesHistory(_) => "/history/series", SonarrEvent::GetStatus => "/system/status", @@ -96,6 +99,10 @@ impl<'a, 'b> Network<'a, 'b> { sonarr_event: SonarrEvent, ) -> Result { match sonarr_event { + SonarrEvent::AddRootFolder(path) => self + .add_sonarr_root_folder(path) + .await + .map(SonarrSerdeable::from), SonarrEvent::ClearBlocklist => self .clear_sonarr_blocklist() .await @@ -188,6 +195,38 @@ impl<'a, 'b> Network<'a, 'b> { } } + async fn add_sonarr_root_folder(&mut self, root_folder: Option) -> Result { + info!("Adding new root folder to Sonarr"); + let event = SonarrEvent::AddRootFolder(None); + let body = if let Some(path) = root_folder { + AddRootFolderBody { path } + } else { + let mut app = self.app.lock().await; + let path = app + .data + .sonarr_data + .edit_root_folder + .as_ref() + .unwrap() + .text + .clone(); + + app.data.sonarr_data.edit_root_folder = None; + + AddRootFolderBody { path } + }; + + debug!("Add root folder body: {body:?}"); + + let request_props = self + .request_props_from(event, RequestMethod::Post, Some(body), None, None) + .await; + + self + .handle_request::(request_props, |_, _| ()) + .await + } + async fn clear_sonarr_blocklist(&mut self) -> Result<()> { info!("Clearing Sonarr blocklist"); let event = SonarrEvent::ClearBlocklist; diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index e2c026a..f1460f3 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -174,7 +174,12 @@ mod test { #[rstest] fn test_resource_root_folder( - #[values(SonarrEvent::GetRootFolders, SonarrEvent::DeleteRootFolder(None))] event: SonarrEvent, + #[values( + SonarrEvent::GetRootFolders, + SonarrEvent::DeleteRootFolder(None), + SonarrEvent::AddRootFolder(None) + )] + event: SonarrEvent, ) { assert_str_eq!(event.resource(), "/rootfolder"); } @@ -211,6 +216,71 @@ mod test { ); } + #[tokio::test] + async fn test_handle_add_sonarr_root_folder_event() { + let (async_server, app_arc, _server) = mock_servarr_api( + RequestMethod::Post, + Some(json!({ + "path": "/nfs/test" + })), + Some(json!({})), + None, + SonarrEvent::AddRootFolder(None), + None, + None, + ) + .await; + + app_arc.lock().await.data.sonarr_data.edit_root_folder = Some("/nfs/test".into()); + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + assert!(network + .handle_sonarr_event(SonarrEvent::AddRootFolder(None)) + .await + .is_ok()); + + async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .data + .sonarr_data + .edit_root_folder + .is_none()); + } + + #[tokio::test] + async fn test_handle_add_sonarr_root_folder_event_uses_provided_path() { + let (async_server, app_arc, _server) = mock_servarr_api( + RequestMethod::Post, + Some(json!({ + "path": "/test/test" + })), + Some(json!({})), + None, + SonarrEvent::AddRootFolder(None), + None, + None, + ) + .await; + + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + assert!(network + .handle_sonarr_event(SonarrEvent::AddRootFolder(Some("/test/test".to_owned()))) + .await + .is_ok()); + + async_server.assert_async().await; + assert!(app_arc + .lock() + .await + .data + .sonarr_data + .edit_root_folder + .is_none()); + } + #[tokio::test] async fn test_handle_clear_radarr_blocklist_event() { let blocklist_items = vec![