From 2e1b0a60f87ceddd7669a66f62ed2beea36438fd Mon Sep 17 00:00:00 2001 From: Miguel Palhas Date: Sat, 22 Aug 2026 20:00:51 +0100 Subject: [PATCH] fix(indexer): preserve request status --- crates/arr-indexer/src/lib.rs | 55 +++++++++++++++---- .../arr-indexer/tests/fixtures/indexers.json | 1 + 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/crates/arr-indexer/src/lib.rs b/crates/arr-indexer/src/lib.rs index d97b455..a09133e 100644 --- a/crates/arr-indexer/src/lib.rs +++ b/crates/arr-indexer/src/lib.rs @@ -3,7 +3,7 @@ use std::{collections::BTreeSet, time::Duration}; use quick_xml::events::Event; -use reqwest::Client; +use reqwest::{Client, StatusCode}; use serde::Deserialize; use thiserror::Error; @@ -95,9 +95,13 @@ impl ProwlarrClient { .header("X-Api-Key", &self.api_key) .send() .await - .map_err(|_| Error::Request)? + .map_err(|error| Error::Request { + status: error.status(), + })? .error_for_status() - .map_err(|_| Error::Request)?; + .map_err(|error| Error::Request { + status: error.status(), + })?; let indexers: Vec = response.json().await.map_err(|_| Error::Response)?; let mut result = Vec::with_capacity(indexers.len()); @@ -124,13 +128,19 @@ impl ProwlarrClient { .query(&[("apikey", self.api_key.as_str()), ("t", "caps")]) .send() .await - .map_err(|_| CapabilityError::Request)? + .map_err(|error| CapabilityError::Request { + status: error.status(), + })? .error_for_status() - .map_err(|_| CapabilityError::Request)?; + .map_err(|error| CapabilityError::Request { + status: error.status(), + })?; let body = response .text() .await - .map_err(|_| CapabilityError::Request)?; + .map_err(|error| CapabilityError::Request { + status: error.status(), + })?; parse_capabilities(&body) } } @@ -140,17 +150,20 @@ impl ProwlarrClient { pub enum Error { #[error("could not create Prowlarr HTTP client")] Client, - #[error("Prowlarr indexer request failed")] - Request, + #[error("Prowlarr indexer request failed (status: {status:?})")] + Request { status: Option }, #[error("could not parse Prowlarr indexer response")] Response, } /// A failed capabilities request for an otherwise usable Prowlarr indexer. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, Error, PartialEq)] pub enum CapabilityError { - Request, + #[error("capabilities request failed (status: {status:?})")] + Request { status: Option }, + #[error("capabilities response was not valid Torznab XML")] InvalidResponse, + #[error("tracker returned Torznab error {code:?}: {description}")] Torznab { code: Option, description: String, @@ -273,6 +286,7 @@ fn is_available(value: &str) -> bool { #[cfg(test)] mod tests { use super::{Capabilities, CapabilityError, ProwlarrClient}; + use reqwest::StatusCode; use wiremock::{ matchers::{header, method, path, query_param}, Mock, MockServer, ResponseTemplate, @@ -306,6 +320,7 @@ mod tests { include_str!("../tests/fixtures/broken-caps.xml"), ) .await; + mount_failure(&server, 29).await; let indexers = ProwlarrClient::new(server.uri(), API_KEY) .expect("HTTP client can be created") @@ -313,7 +328,7 @@ mod tests { .await .expect("recorded Prowlarr responses are valid"); - assert_eq!(indexers.len(), 3); + assert_eq!(indexers.len(), 4); assert_eq!(indexers[0].id, 3); assert_eq!(indexers[0].name, "Alpha Tracker"); assert!(indexers[0].capabilities.movie.available); @@ -337,6 +352,14 @@ mod tests { description: "Indexer unavailable".to_owned(), }) ); + + assert_eq!(indexers[3].id, 29); + assert_eq!( + indexers[3].capabilities_error, + Some(CapabilityError::Request { + status: Some(StatusCode::SERVICE_UNAVAILABLE), + }) + ); } async fn mount_capabilities(server: &MockServer, id: i64, body: &str) { @@ -348,4 +371,14 @@ mod tests { .mount(server) .await; } + + async fn mount_failure(server: &MockServer, id: i64) { + Mock::given(method("GET")) + .and(path(format!("/{id}/api"))) + .and(query_param("apikey", API_KEY)) + .and(query_param("t", "caps")) + .respond_with(ResponseTemplate::new(503)) + .mount(server) + .await; + } } diff --git a/crates/arr-indexer/tests/fixtures/indexers.json b/crates/arr-indexer/tests/fixtures/indexers.json index 106a129..68fd643 100644 --- a/crates/arr-indexer/tests/fixtures/indexers.json +++ b/crates/arr-indexer/tests/fixtures/indexers.json @@ -2,5 +2,6 @@ { "id": 3, "name": "Alpha Tracker", "enable": true }, { "id": 17, "name": "Text Only", "enable": true }, { "id": 23, "name": "Broken Tracker", "enable": true }, + { "id": 29, "name": "Unavailable Tracker", "enable": true }, { "id": 42, "name": "Disabled Tracker", "enable": false } ]