101 lines
3.2 KiB
Rust
101 lines
3.2 KiB
Rust
//! Failures, in the shapes Radarr produces them.
|
|
//!
|
|
//! Jellyseerr surfaces the response body verbatim in its own logs and settings
|
|
//! UI, so a validation failure that comes back in Radarr's shape is readable
|
|
//! there without anyone knowing this is not Radarr.
|
|
|
|
use axum::http::StatusCode;
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::Json;
|
|
use serde::Serialize;
|
|
|
|
/// Everything the shim can answer with instead of a result.
|
|
#[derive(Debug)]
|
|
pub enum CompatError {
|
|
/// The request was well-formed but names something that does not exist
|
|
/// here — usually a root folder path Jellyseerr was configured with by
|
|
/// hand. `property` is Radarr's field name, `PascalCase` as it sends it.
|
|
Validation {
|
|
/// Radarr's name for the offending field, e.g. `RootFolderPath`.
|
|
property: &'static str,
|
|
/// What is wrong with it, in a sentence.
|
|
message: String,
|
|
},
|
|
/// No such movie.
|
|
NotFound,
|
|
/// TMDB is unconfigured or would not answer.
|
|
Upstream(String),
|
|
/// The database would not answer.
|
|
Database(String),
|
|
}
|
|
|
|
/// One entry of Radarr's validation-failure body.
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct ValidationFailure {
|
|
property_name: &'static str,
|
|
error_message: String,
|
|
severity: &'static str,
|
|
}
|
|
|
|
/// Radarr's shape for everything that is not a validation failure.
|
|
#[derive(Debug, Serialize)]
|
|
struct Message {
|
|
message: String,
|
|
}
|
|
|
|
impl IntoResponse for CompatError {
|
|
fn into_response(self) -> Response {
|
|
match self {
|
|
// A list, not an object: Radarr's model binder reports every
|
|
// failing field at once and Jellyseerr's client reads index 0.
|
|
Self::Validation { property, message } => (
|
|
StatusCode::BAD_REQUEST,
|
|
Json(vec![ValidationFailure {
|
|
property_name: property,
|
|
error_message: message,
|
|
severity: "error",
|
|
}]),
|
|
)
|
|
.into_response(),
|
|
Self::NotFound => (
|
|
StatusCode::NOT_FOUND,
|
|
Json(Message {
|
|
message: "NotFound".into(),
|
|
}),
|
|
)
|
|
.into_response(),
|
|
Self::Upstream(detail) => {
|
|
tracing::warn!(%detail, "compat shim upstream failure");
|
|
(StatusCode::BAD_GATEWAY, Json(Message { message: detail })).into_response()
|
|
}
|
|
Self::Database(detail) => {
|
|
tracing::error!(%detail, "compat shim database error");
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(Message {
|
|
message: "database error".into(),
|
|
}),
|
|
)
|
|
.into_response()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<sqlx::Error> for CompatError {
|
|
fn from(error: sqlx::Error) -> Self {
|
|
if matches!(error, sqlx::Error::RowNotFound) {
|
|
Self::NotFound
|
|
} else {
|
|
Self::Database(error.to_string())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<arr_meta::Error> for CompatError {
|
|
fn from(error: arr_meta::Error) -> Self {
|
|
Self::Upstream(error.to_string())
|
|
}
|
|
}
|