use reqwest::Response;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::errors::{
api_key_missing_error, authorization_failed_error, invalid_query_error, unknown_error,
};
#[derive(Debug, Serialize, Deserialize)]
pub struct SupabaseErrorResponse {
pub code: Option<String>,
pub message: String,
pub details: Option<String>,
pub hint: Option<String>,
}
pub async fn handle_response(response: Response) -> Result<Vec<Value>, String> {
if response.status().is_success() {
let headers: &reqwest::header::HeaderMap = response.headers();
let content_range_option: Option<&str> =
headers.get("content-range").and_then(|v| v.to_str().ok());
let mut total_records: Option<i32> = None;
if let Some(content_range) = content_range_option {
total_records = content_range
.split('/')
.nth(1)
.and_then(|v| v.parse::<i32>().ok());
}
let mut records: Vec<Value> = match response.json::<Vec<Value>>().await {
Ok(records) => records,
Err(error) => return Err(error.to_string()),
};
if let Some(count) = total_records {
records.push(json!({"total_records_count": count}));
}
Ok(records)
} else {
let status_code = response.status().as_u16();
let error_text = response.text().await.unwrap_or_default();
if let Ok(error_response) = serde_json::from_str::<SupabaseErrorResponse>(&error_text) {
let mut error_msg = format!("Error: {}", error_response.message);
if let Some(code) = &error_response.code {
error_msg = format!(
"Error {} ({}): {}",
code, status_code, error_response.message
);
}
if let Some(details) = &error_response.details {
error_msg.push_str(&format!("\nDetails: {}", details));
}
if let Some(hint) = &error_response.hint {
error_msg.push_str(&format!("\nHint: {}", hint));
}
return Err(error_msg);
}
let error_message = match status_code {
401 => authorization_failed_error()
.await
.map_err(|e| e.to_string()),
403 => api_key_missing_error().await.map_err(|e| e.to_string()),
400 => invalid_query_error().await.map_err(|e| e.to_string()),
_ => unknown_error().await.map_err(|e| e.to_string()),
};
if !error_text.is_empty() {
let fallback_error = error_message
.expect_err("Expected an error, but got Ok when handling Supabase error response");
return Err(format!("{}\nResponse: {}", fallback_error, error_text));
}
Err(error_message
.expect_err("Expected an error, but got Ok when handling Supabase error response"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_supabase_error_response_with_hint() {
let error_json = json!({
"code": "42703",
"details": null,
"hint": "Perhaps you meant to reference the column \"jortt_invoices.amount_side\".",
"message": "column jortt_invoices.account_side does not exist"
});
let error_response: SupabaseErrorResponse = serde_json::from_value(error_json).unwrap();
assert_eq!(error_response.code, Some("42703".to_owned()));
assert_eq!(
error_response.message,
"column jortt_invoices.account_side does not exist"
);
assert_eq!(error_response.details, None);
assert_eq!(
error_response.hint,
Some(
"Perhaps you meant to reference the column \"jortt_invoices.amount_side\"."
.to_owned()
)
);
}
#[test]
fn test_supabase_error_response_without_hint() {
let error_json = json!({
"code": "23505",
"message": "duplicate key value violates unique constraint",
"details": "Key (email)=(test@example.com) already exists."
});
let error_response: SupabaseErrorResponse = serde_json::from_value(error_json).unwrap();
assert_eq!(error_response.code, Some("23505".to_owned()));
assert_eq!(
error_response.message,
"duplicate key value violates unique constraint"
);
assert_eq!(
error_response.details,
Some("Key (email)=(test@example.com) already exists.".to_owned())
);
assert_eq!(error_response.hint, None);
}
}