From 5e63c34a9fcc2b6334988cd4a1fb23b710f181db Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 19 Nov 2024 16:56:48 -0700 Subject: [PATCH] feat(network): Added support for fetching series details for a given series ID in Sonarr --- src/models/sonarr_models.rs | 2 + src/models/sonarr_models_tests.rs | 18 +++++- src/network/sonarr_network.rs | 27 ++++++++- src/network/sonarr_network_tests.rs | 88 ++++++++++++++++++++++++----- 4 files changed, 118 insertions(+), 17 deletions(-) diff --git a/src/models/sonarr_models.rs b/src/models/sonarr_models.rs index ca74870..673e459 100644 --- a/src/models/sonarr_models.rs +++ b/src/models/sonarr_models.rs @@ -320,6 +320,7 @@ pub enum SonarrSerdeable { Releases(Vec), SecurityConfig(SecurityConfig), SeriesVec(Vec), + Series(Series), SystemStatus(SystemStatus), BlocklistResponse(BlocklistResponse), LogResponse(LogResponse), @@ -351,6 +352,7 @@ serde_enum_from!( Releases(Vec), SecurityConfig(SecurityConfig), SeriesVec(Vec), + Series(Series), SystemStatus(SystemStatus), BlocklistResponse(BlocklistResponse), LogResponse(LogResponse), diff --git a/src/models/sonarr_models_tests.rs b/src/models/sonarr_models_tests.rs index 9d864e2..2fbf265 100644 --- a/src/models/sonarr_models_tests.rs +++ b/src/models/sonarr_models_tests.rs @@ -144,15 +144,27 @@ mod tests { } #[test] - fn test_sonarr_serdeable_from_series() { - let series = vec![Series { + fn test_sonarr_serdeable_from_series_vec() { + let series_vec = vec![Series { id: 1, ..Series::default() }]; + let sonarr_serdeable: SonarrSerdeable = series_vec.clone().into(); + + assert_eq!(sonarr_serdeable, SonarrSerdeable::SeriesVec(series_vec)); + } + + #[test] + fn test_sonarr_serdeable_from_series() { + let series = Series { + id: 1, + ..Series::default() + }; + let sonarr_serdeable: SonarrSerdeable = series.clone().into(); - assert_eq!(sonarr_serdeable, SonarrSerdeable::SeriesVec(series)); + assert_eq!(sonarr_serdeable, SonarrSerdeable::Series(series)); } #[test] diff --git a/src/network/sonarr_network.rs b/src/network/sonarr_network.rs index 19ac1a4..45e298d 100644 --- a/src/network/sonarr_network.rs +++ b/src/network/sonarr_network.rs @@ -43,6 +43,7 @@ pub enum SonarrEvent { GetQueuedEvents, GetSeasonReleases(Option<(i64, i64)>), GetSecurityConfig, + GetSeriesDetails(Option), GetStatus, HealthCheck, ListSeries, @@ -65,7 +66,7 @@ impl NetworkResource for SonarrEvent { SonarrEvent::GetSeasonReleases(_) => "/release", SonarrEvent::GetStatus => "/system/status", SonarrEvent::HealthCheck => "/health", - SonarrEvent::ListSeries => "/series", + SonarrEvent::ListSeries | SonarrEvent::GetSeriesDetails(_) => "/series", } } } @@ -129,6 +130,10 @@ impl<'a, 'b> Network<'a, 'b> { .get_sonarr_security_config() .await .map(SonarrSerdeable::from), + SonarrEvent::GetSeriesDetails(series_id) => self + .get_series_details(series_id) + .await + .map(SonarrSerdeable::from), SonarrEvent::GetStatus => self.get_sonarr_status().await.map(SonarrSerdeable::from), SonarrEvent::HealthCheck => self .get_sonarr_healthcheck() @@ -608,6 +613,26 @@ impl<'a, 'b> Network<'a, 'b> { .await } + async fn get_series_details(&mut self, series_id: Option) -> Result { + let (id, _) = self.extract_series_id(series_id).await; + info!("Fetching details for Sonarr series with ID: {id}"); + let event = SonarrEvent::GetSeriesDetails(series_id); + + let request_props = self + .request_props_from( + event, + RequestMethod::Get, + None::<()>, + Some(format!("/{id}")), + None, + ) + .await; + + self + .handle_request::<(), Series>(request_props, |_, _| ()) + .await + } + async fn list_series(&mut self) -> Result> { info!("Fetching Sonarr library"); let event = SonarrEvent::ListSeries; diff --git a/src/network/sonarr_network_tests.rs b/src/network/sonarr_network_tests.rs index 2209cc8..b57548a 100644 --- a/src/network/sonarr_network_tests.rs +++ b/src/network/sonarr_network_tests.rs @@ -137,7 +137,9 @@ mod test { } #[rstest] - fn test_resource_series(#[values(SonarrEvent::ListSeries)] event: SonarrEvent) { + fn test_resource_series( + #[values(SonarrEvent::ListSeries, SonarrEvent::GetSeriesDetails(None))] event: SonarrEvent, + ) { assert_str_eq!(event.resource(), "/series"); } @@ -1010,7 +1012,8 @@ mod test { #[tokio::test] #[should_panic(expected = "Season details modal is empty")] - async fn test_handle_get_episode_details_event_requires_season_details_modal_to_be_some() { + async fn test_handle_get_episode_details_event_requires_season_details_modal_to_be_some_when_in_tui_mode( + ) { let (_async_server, app_arc, _server) = mock_servarr_api( RequestMethod::Get, None, @@ -1518,7 +1521,7 @@ mod test { #[rstest] #[tokio::test] - async fn test_handle_get_series_event(#[values(true, false)] use_custom_sorting: bool) { + async fn test_handle_list_series_event(#[values(true, false)] use_custom_sorting: bool) { let mut series_1: Value = serde_json::from_str(SERIES_JSON).unwrap(); let mut series_2: Value = serde_json::from_str(SERIES_JSON).unwrap(); *series_1.get_mut("id").unwrap() = json!(1); @@ -1597,6 +1600,75 @@ mod test { } } + #[tokio::test] + async fn test_handle_get_series_details_event() { + let expected_series: Series = serde_json::from_str(SERIES_JSON).unwrap(); + let (async_server, app_arc, _server) = mock_servarr_api( + RequestMethod::Get, + None, + Some(serde_json::from_str(SERIES_JSON).unwrap()), + None, + SonarrEvent::GetSeriesDetails(None), + Some("/1"), + None, + ) + .await; + app_arc + .lock() + .await + .data + .sonarr_data + .series + .set_items(vec![series()]); + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + if let SonarrSerdeable::Series(series) = network + .handle_sonarr_event(SonarrEvent::GetSeriesDetails(None)) + .await + .unwrap() + { + async_server.assert_async().await; + assert_eq!(series, expected_series); + } + } + + #[tokio::test] + async fn test_handle_get_series_details_event_uses_provided_series_id() { + let expected_series: Series = Series { + id: 2, + ..serde_json::from_str(SERIES_JSON).unwrap() + }; + let mut response: Value = serde_json::from_str(SERIES_JSON).unwrap(); + *response.get_mut("id").unwrap() = json!(2); + let (async_server, app_arc, _server) = mock_servarr_api( + RequestMethod::Get, + None, + Some(response), + None, + SonarrEvent::GetSeriesDetails(Some(2)), + Some("/2"), + None, + ) + .await; + app_arc + .lock() + .await + .data + .sonarr_data + .series + .set_items(vec![series()]); + let mut network = Network::new(&app_arc, CancellationToken::new(), Client::new()); + + if let SonarrSerdeable::Series(series) = network + .handle_sonarr_event(SonarrEvent::GetSeriesDetails(Some(2))) + .await + .unwrap() + { + async_server.assert_async().await; + assert_eq!(series, expected_series); + } + } + #[tokio::test] async fn test_handle_get_series_event_no_op_while_user_is_selecting_sort_options() { let mut series_1: Value = serde_json::from_str(SERIES_JSON).unwrap(); @@ -2183,14 +2255,4 @@ mod test { quality: quality_wrapper(), } } - - fn render(state: &mut TreeState, items: &[TreeItem]) - where - T: ToText + Clone + Default + Display + Hash + PartialEq + Eq, - { - let tree = Tree::new(items).unwrap(); - let area = Rect::new(0, 0, 10, 4); - let mut buffer = Buffer::empty(area); - StatefulWidget::render(tree, area, &mut buffer, state); - } }