refactor: labrinth ApiError and error reporting (#6981)

* refactor: labrinth `ApiError` and error reporting

* fix clippy

* fix ci
This commit is contained in:
aecsocket
2026-08-07 15:47:54 +00:00
committed by GitHub
parent d344cbcb7a
commit 3fa6905006
102 changed files with 6449 additions and 3791 deletions
@@ -4,6 +4,7 @@
//! This keeps version filters correlated without duplicating every version
//! into its project document.
use crate::util::error::ApiContext as _;
use async_trait::async_trait;
use eyre::{Result, eyre};
use itertools::Itertools;
@@ -388,7 +389,7 @@ impl Elasticsearch {
.wrap_internal_err("failed to execute Elasticsearch search")?;
let mut body = response_json(response, "execute Elasticsearch search")
.await
.map_err(ApiError::Internal)?;
.wrap_internal_err("parsing Elasticsearch search response")?;
if sort_only && !body["hits"]["hits"].is_array() {
body["hits"]["hits"] = Value::Array(Vec::new());
}
@@ -410,12 +411,15 @@ impl Elasticsearch {
.wrap_internal_err("failed to open Elasticsearch point in time")?;
let body = response_json(response, "open Elasticsearch point in time")
.await
.map_err(ApiError::Internal)?;
body["id"].as_str().map(ToOwned::to_owned).ok_or_else(|| {
ApiError::Internal(eyre!(
"Elasticsearch point in time response did not contain an ID"
))
})
.wrap_internal_err(
"parsing Elasticsearch point-in-time response",
)?;
body["id"]
.as_str()
.map(ToOwned::to_owned)
.wrap_internal_err(
"finding ID in Elasticsearch point-in-time response",
)
}
async fn close_point_in_time(&self, id: &str) -> Result<(), ApiError> {
@@ -428,7 +432,9 @@ impl Elasticsearch {
.wrap_internal_err("failed to close Elasticsearch point in time")?;
response_json(response, "close Elasticsearch point in time")
.await
.map_err(ApiError::Internal)?;
.wrap_internal_err(
"parsing Elasticsearch point-in-time response",
)?;
Ok(())
}
@@ -456,7 +462,9 @@ impl Elasticsearch {
"execute Elasticsearch point in time search",
)
.await
.map_err(ApiError::Internal)?;
.wrap_internal_err(
"parsing Elasticsearch point-in-time search response",
)?;
if sort_only && !body["hits"]["hits"].is_array() {
body["hits"]["hits"] = Value::Array(Vec::new());
}
@@ -472,7 +480,10 @@ impl Elasticsearch {
offset: usize,
size: usize,
) -> Result<Value, ApiError> {
let mut point_in_time_id = self.open_point_in_time(alias).await?;
let mut point_in_time_id = self
.open_point_in_time(alias)
.await
.wrap_api_err("executing `open_point_in_time`")?;
let result = self
.execute_deep_search_with_point_in_time(
query,
@@ -517,7 +528,10 @@ impl Elasticsearch {
"id": point_in_time_id,
"keep_alive": "1m"
});
let body = self.execute_point_in_time_search(&body, true).await?;
let body = self
.execute_point_in_time_search(&body, true)
.await
.wrap_api_err("executing `execute_point_in_time_search`")?;
if let Some(id) = body["pit_id"].as_str() {
*point_in_time_id = id.to_string();
}
@@ -579,7 +593,10 @@ impl Elasticsearch {
"id": point_in_time_id,
"keep_alive": "1m"
});
let body = self.execute_point_in_time_search(&body, false).await?;
let body = self
.execute_point_in_time_search(&body, false)
.await
.wrap_api_err("executing `execute_point_in_time_search`")?;
if let Some(id) = body["pit_id"].as_str() {
*point_in_time_id = id.to_string();
}
@@ -832,10 +849,13 @@ impl Elasticsearch {
&self,
info: &SearchRequest,
) -> Result<SearchResults, ApiError> {
let parsed = parse_search_request(info)?;
let parsed = parse_search_request(info)
.wrap_api_err("executing `parse_search_request`")?;
let search_sort =
parse_search_index(parsed.index, info.new_filters.as_deref())?;
let filter = Self::build_filter(info)?;
parse_search_index(parsed.index, info.new_filters.as_deref())
.wrap_api_err("executing `parse_search_index`")?;
let filter = Self::build_filter(info)
.wrap_api_err("executing `Self::build_filter`")?;
let mut filters = vec![json!({"term": {"document_type": "project"}})];
if let Some(filter) = &filter {
filters.push(filter.query.clone());
@@ -858,7 +878,8 @@ impl Elasticsearch {
parsed.offset,
parsed.hits_per_page,
)
.await?
.await
.wrap_api_err("executing `execute_deep_search`")?
} else {
let body = Self::search_body(
&query,
@@ -869,7 +890,9 @@ impl Elasticsearch {
None,
true,
);
self.execute_search(&alias, &body, false).await?
self.execute_search(&alias, &body, false)
.await
.wrap_api_err("executing `execute_search`")?
};
let total_hits = body["hits"]["total"]["value"]
.as_u64()
@@ -137,6 +137,7 @@
//! enabled. This avoids repeating joins for popular queries at the cost of
//! results remaining stale for Typesense's default 60-second cache lifetime.
use crate::util::error::ApiContext as _;
use std::sync::LazyLock;
use async_trait::async_trait;
@@ -911,7 +912,8 @@ impl Typesense {
new_filters: Option<&str>,
request_config: &RequestConfig,
) -> Result<(String, String), ApiError> {
let sort = parse_search_index(index, new_filters)?;
let sort = parse_search_index(index, new_filters)
.wrap_api_err("executing `parse_search_index`")?;
let alias = self.config.get_alias_name("projects");
Ok((alias, self.get_sort_fields(sort.index, request_config)))
}
@@ -1047,14 +1049,18 @@ impl SearchBackend for Typesense {
&self,
info: &SearchRequest,
) -> Result<SearchResults, ApiError> {
let parsed = parse_search_request(info)?;
let (collection_alias, sort_by) = self.get_sort_index(
parsed.index,
info.new_filters.as_deref(),
&info.typesense_config,
)?;
let parsed = parse_search_request(info)
.wrap_api_err("executing `parse_search_request`")?;
let (collection_alias, sort_by) = self
.get_sort_index(
parsed.index,
info.new_filters.as_deref(),
&info.typesense_config,
)
.wrap_api_err("fetching sort index")?;
let versions_alias = self.config.get_alias_name("versions");
let filter_by = Self::build_filter(info, &versions_alias)?;
let filter_by = Self::build_filter(info, &versions_alias)
.wrap_api_err("executing `Self::build_filter`")?;
let q = if parsed.query.is_empty() {
"*"
+7 -2
View File
@@ -4,6 +4,8 @@ use crate::models::ids::{ProjectId, VersionId};
use crate::models::projects::DependencyType;
use crate::queue::server_ping;
use crate::routes::ApiError;
use crate::util::error::ApiContext as _;
use crate::util::error::Context as _;
use crate::{database::PgPool, env::ENV};
use ariadne::ids::base62_impl::parse_base62;
use async_trait::async_trait;
@@ -94,10 +96,13 @@ pub trait SearchBackend: Send + Sync {
info: &SearchRequest,
redis: &RedisPool,
) -> Result<SearchResults, ApiError> {
let mut results = self.search_for_project_raw(info).await?;
let mut results = self
.search_for_project_raw(info)
.await
.wrap_api_err("searching projects")?;
hydrate_search_results(&mut results.hits, redis)
.await
.map_err(ApiError::Internal)?;
.wrap_internal_err("hydrating search results from database")?;
Ok(results)
}