From a751d6f84ae96855031686a0aaa8c342997ca8ee Mon Sep 17 00:00:00 2001 From: aecsocket <43144841+aecsocket@users.noreply.github.com> Date: Wed, 16 Sep 2026 14:31:42 +0400 Subject: [PATCH] refactor: replace `DatabaseError` with `eyre::Report`s --- apps/labrinth/AGENTS.md | 1 + apps/labrinth/src/auth/checks.rs | 4 +- apps/labrinth/src/auth/mod.rs | 6 - apps/labrinth/src/auth/oauth/errors.rs | 4 +- apps/labrinth/src/background_task.rs | 4 +- .../database/models/affiliate_code_item.rs | 41 +- .../database/models/analytics_event_item.rs | 55 +- .../src/database/models/categories.rs | 139 +++-- .../src/database/models/charge_item.rs | 100 ++-- .../src/database/models/collection_item.rs | 61 ++- .../src/database/models/delphi_report_item.rs | 30 +- .../labrinth/src/database/models/flow_item.rs | 58 +- apps/labrinth/src/database/models/ids.rs | 20 +- .../src/database/models/image_item.rs | 55 +- .../database/models/legacy_loader_fields.rs | 61 ++- .../src/database/models/loader_fields.rs | 500 +++++++++++------- apps/labrinth/src/database/models/mod.rs | 20 - .../database/models/moderation_note_item.rs | 92 +++- .../src/database/models/notification_item.rs | 158 ++++-- .../models/notifications_deliveries_item.rs | 22 +- .../models/notifications_template_item.rs | 48 +- .../models/notifications_type_item.rs | 27 +- .../models/oauth_client_authorization_item.rs | 25 +- .../src/database/models/oauth_client_item.rs | 49 +- .../src/database/models/oauth_token_item.rs | 13 +- .../src/database/models/organization_item.rs | 74 ++- .../src/database/models/passkey_item.rs | 37 +- apps/labrinth/src/database/models/pat_item.rs | 69 ++- .../src/database/models/payout_item.rs | 21 +- .../models/payouts_values_notifications.rs | 18 +- .../src/database/models/product_item.rs | 184 ++++--- .../models/project_disclosure_item.rs | 65 +-- .../src/database/models/project_item.rs | 188 ++++--- .../src/database/models/session_item.rs | 76 ++- .../labrinth/src/database/models/team_item.rs | 127 +++-- .../src/database/models/thread_item.rs | 26 +- .../labrinth/src/database/models/user_item.rs | 101 ++-- .../database/models/user_subscription_item.rs | 71 ++- .../users_notifications_preferences_item.rs | 18 +- .../src/database/models/version_item.rs | 234 +++++--- apps/labrinth/src/models/v2/projects.rs | 13 +- apps/labrinth/src/queue/billing.rs | 17 +- apps/labrinth/src/queue/email/templates.rs | 37 +- apps/labrinth/src/queue/session.rs | 50 +- apps/labrinth/src/routes/analytics.rs | 6 +- .../src/routes/internal/attribution.rs | 2 +- .../src/routes/internal/moderation/mod.rs | 12 +- apps/labrinth/src/routes/internal/statuses.rs | 51 +- apps/labrinth/src/routes/maven.rs | 8 +- apps/labrinth/src/routes/updates.rs | 2 +- apps/labrinth/src/routes/v2/projects.rs | 4 +- .../src/routes/v3/analytics_get/mod.rs | 4 +- apps/labrinth/src/routes/v3/collections.rs | 2 +- apps/labrinth/src/routes/v3/images.rs | 2 +- apps/labrinth/src/routes/v3/oauth_clients.rs | 38 +- apps/labrinth/src/routes/v3/organizations.rs | 6 +- .../src/routes/v3/project_creation.rs | 16 +- apps/labrinth/src/routes/v3/projects/mod.rs | 34 +- apps/labrinth/src/routes/v3/teams.rs | 4 +- apps/labrinth/src/routes/v3/threads.rs | 2 +- apps/labrinth/src/routes/v3/users.rs | 6 +- apps/labrinth/src/routes/v3/version_file.rs | 6 +- apps/labrinth/src/routes/v3/versions.rs | 4 +- apps/labrinth/src/util/webhook.rs | 2 +- apps/labrinth/src/validate/mod.rs | 5 +- apps/labrinth/tests/redis.rs | 36 +- packages/xredis/src/cache.rs | 13 +- packages/xredis/src/lib.rs | 14 +- 68 files changed, 2008 insertions(+), 1290 deletions(-) diff --git a/apps/labrinth/AGENTS.md b/apps/labrinth/AGENTS.md index 3c11a2248b..02df6dee25 100644 --- a/apps/labrinth/AGENTS.md +++ b/apps/labrinth/AGENTS.md @@ -1,4 +1,5 @@ - Use `ApiError` as the error type for API routes +- Always use `cargo clippy` instead of `cargo check` - The return type of an HTTP route should not be `HttpResponse` if possible; always prefer more specific types - Use `web::Json` for JSON-encoded response - Use `()` for no content diff --git a/apps/labrinth/src/auth/checks.rs b/apps/labrinth/src/auth/checks.rs index daf9d35d00..0669ccfefd 100644 --- a/apps/labrinth/src/auth/checks.rs +++ b/apps/labrinth/src/auth/checks.rs @@ -298,7 +298,7 @@ pub async fn filter_visible_version_ids( let visible_project_ids = filter_visible_project_ids( DBProject::get_many_ids(&project_ids, pool, redis) .await - .wrap_api_err("fetching projects for visibility filtering")? + .wrap_internal_err("fetching projects for visibility filtering")? .iter() .map(|x| &x.inner) .collect(), @@ -355,7 +355,7 @@ pub async fn filter_enlisted_version_ids( let authorized_project_ids = filter_enlisted_projects_ids( DBProject::get_many_ids(&project_ids, pool, redis) .await - .wrap_api_err("fetching projects for membership filtering")? + .wrap_internal_err("fetching projects for membership filtering")? .iter() .map(|x| &x.inner) .collect(), diff --git a/apps/labrinth/src/auth/mod.rs b/apps/labrinth/src/auth/mod.rs index c58a032f13..5159934781 100644 --- a/apps/labrinth/src/auth/mod.rs +++ b/apps/labrinth/src/auth/mod.rs @@ -25,8 +25,6 @@ pub enum AuthenticationError { Internal(#[from] eyre::Report), #[error("An unknown database error occurred: {0}")] Sqlx(#[from] sqlx::Error), - #[error("Database Error: {0}")] - Database(#[from] crate::database::models::DatabaseError), #[error("Error while parsing JSON: {0}")] SerDe(#[from] serde_json::Error), #[error("Error while communicating to external provider")] @@ -66,9 +64,6 @@ impl actix_web::ResponseError for AuthenticationError { StatusCode::INTERNAL_SERVER_ERROR } AuthenticationError::Sqlx(..) => StatusCode::INTERNAL_SERVER_ERROR, - AuthenticationError::Database(..) => { - StatusCode::INTERNAL_SERVER_ERROR - } AuthenticationError::SerDe(..) => StatusCode::BAD_REQUEST, AuthenticationError::Reqwest(..) => { StatusCode::INTERNAL_SERVER_ERROR @@ -105,7 +100,6 @@ impl AuthenticationError { match self { AuthenticationError::Internal(..) => "internal_error", AuthenticationError::Sqlx(..) => "database_error", - AuthenticationError::Database(..) => "database_error", AuthenticationError::SerDe(..) => "invalid_input", AuthenticationError::Reqwest(..) => "network_error", AuthenticationError::InvalidCredentials => "invalid_credentials", diff --git a/apps/labrinth/src/auth/oauth/errors.rs b/apps/labrinth/src/auth/oauth/errors.rs index 09691afe84..e178c9f682 100644 --- a/apps/labrinth/src/auth/oauth/errors.rs +++ b/apps/labrinth/src/auth/oauth/errors.rs @@ -155,8 +155,8 @@ pub enum OAuthErrorType { AccessDenied, } -impl From for OAuthErrorType { - fn from(value: crate::database::models::DatabaseError) -> Self { +impl From for OAuthErrorType { + fn from(value: eyre::Report) -> Self { OAuthErrorType::AuthenticationError(value.into()) } } diff --git a/apps/labrinth/src/background_task.rs b/apps/labrinth/src/background_task.rs index c1b9906420..6ecada1244 100644 --- a/apps/labrinth/src/background_task.rs +++ b/apps/labrinth/src/background_task.rs @@ -418,8 +418,8 @@ mod version_updater { pub enum VersionIndexingError { #[error("Network error while updating game versions list: {0}")] NetworkError(#[from] reqwest::Error), - #[error("Database error while updating game versions list: {0}")] - DatabaseError(#[from] crate::database::models::DatabaseError), + #[error("internal error while updating game versions list: {0}")] + InternalError(#[from] eyre::Report), } pub async fn update_versions( diff --git a/apps/labrinth/src/database/models/affiliate_code_item.rs b/apps/labrinth/src/database/models/affiliate_code_item.rs index b06e38c3d3..0028b06755 100644 --- a/apps/labrinth/src/database/models/affiliate_code_item.rs +++ b/apps/labrinth/src/database/models/affiliate_code_item.rs @@ -1,7 +1,8 @@ use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use futures::{StreamExt, TryStreamExt}; -use crate::database::models::{DBAffiliateCodeId, DBUserId, DatabaseError}; +use crate::database::models::{DBAffiliateCodeId, DBUserId}; #[derive(Debug)] pub struct DBAffiliateCode { @@ -16,14 +17,15 @@ impl DBAffiliateCode { pub async fn get_by_id( id: DBAffiliateCodeId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let record = sqlx::query!( "SELECT id, created_at, created_by, affiliate, source_name FROM affiliate_codes WHERE id = $1", id as DBAffiliateCodeId ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching affiliate code by id")?; Ok(record.map(|record| DBAffiliateCode { id: DBAffiliateCodeId(record.id), @@ -37,7 +39,7 @@ impl DBAffiliateCode { pub async fn get_by_affiliate( affiliate: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let records = sqlx::query!( "SELECT id, created_at, created_by, affiliate, source_name FROM affiliate_codes WHERE affiliate = $1", @@ -45,8 +47,8 @@ impl DBAffiliateCode { ) .fetch(exec) .map(|record| { - let record = record?; - Ok::<_, DatabaseError>(DBAffiliateCode { + let record = record.wrap_err("reading affiliate code record")?; + eyre::Ok(DBAffiliateCode { id: DBAffiliateCodeId(record.id), created_at: record.created_at, created_by: DBUserId(record.created_by), @@ -55,7 +57,8 @@ impl DBAffiliateCode { }) }) .try_collect::>() - .await?; + .await + .wrap_err("fetching affiliate codes by affiliate")?; Ok(records) } @@ -63,7 +66,7 @@ impl DBAffiliateCode { pub async fn insert( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( "INSERT INTO affiliate_codes (id, created_at, created_by, affiliate, source_name) VALUES ($1, $2, $3, $4, $5)", @@ -74,20 +77,22 @@ impl DBAffiliateCode { self.source_name ) .execute(exec) - .await?; + .await + .wrap_err("inserting affiliate code")?; Ok(()) } pub async fn remove( id: DBAffiliateCodeId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let result = sqlx::query!( "DELETE FROM affiliate_codes WHERE id = $1", id as DBAffiliateCodeId ) .execute(exec) - .await?; + .await + .wrap_err("removing affiliate code")?; if result.rows_affected() > 0 { Ok(Some(())) @@ -100,29 +105,30 @@ impl DBAffiliateCode { id: DBAffiliateCodeId, source_name: &str, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result { + ) -> Result { let result = sqlx::query!( "UPDATE affiliate_codes SET source_name = $1 WHERE id = $2", source_name, id as DBAffiliateCodeId ) .execute(exec) - .await?; + .await + .wrap_err("updating affiliate code source name")?; Ok(result.rows_affected() > 0) } pub async fn get_all( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let records = sqlx::query!( "SELECT id, created_at, created_by, affiliate, source_name FROM affiliate_codes ORDER BY created_at DESC" ) .fetch(exec) .map(|record| { - let record = record?; - Ok::<_, DatabaseError>(DBAffiliateCode { + let record = record.wrap_err("reading affiliate code record")?; + eyre::Ok(DBAffiliateCode { id: DBAffiliateCodeId(record.id), created_at: record.created_at, created_by: DBUserId(record.created_by), @@ -131,7 +137,8 @@ impl DBAffiliateCode { }) }) .try_collect::>() - .await?; + .await + .wrap_err("fetching all affiliate codes")?; Ok(records) } diff --git a/apps/labrinth/src/database/models/analytics_event_item.rs b/apps/labrinth/src/database/models/analytics_event_item.rs index 47493582aa..614ce680ab 100644 --- a/apps/labrinth/src/database/models/analytics_event_item.rs +++ b/apps/labrinth/src/database/models/analytics_event_item.rs @@ -1,10 +1,11 @@ use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use futures::{StreamExt, TryStreamExt}; use sqlx::types::Json; use xredis::RedisPool; use crate::{ - database::models::{DBAnalyticsEventId, DatabaseError}, + database::models::DBAnalyticsEventId, models::v3::analytics_event::AnalyticsEventMeta, }; use serde::{Deserialize, Serialize}; @@ -24,7 +25,7 @@ impl DBAnalyticsEvent { pub async fn insert( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO analytics_events (id, meta, starts, ends) @@ -36,7 +37,8 @@ impl DBAnalyticsEvent { self.ends, ) .execute(exec) - .await?; + .await + .wrap_err("inserting analytics event")?; Ok(()) } @@ -44,7 +46,7 @@ impl DBAnalyticsEvent { pub async fn update( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result { + ) -> Result { let result = sqlx::query!( " UPDATE analytics_events @@ -57,7 +59,8 @@ impl DBAnalyticsEvent { self.ends, ) .execute(exec) - .await?; + .await + .wrap_err("updating analytics event")?; Ok(result.rows_affected() > 0) } @@ -65,7 +68,7 @@ impl DBAnalyticsEvent { pub async fn remove( id: DBAnalyticsEventId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result { + ) -> Result { let result = sqlx::query!( " DELETE FROM analytics_events @@ -74,7 +77,8 @@ impl DBAnalyticsEvent { id as DBAnalyticsEventId, ) .execute(exec) - .await?; + .await + .wrap_err("removing analytics event")?; Ok(result.rows_affected() > 0) } @@ -82,13 +86,20 @@ impl DBAnalyticsEvent { pub async fn get_all( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, redis: &RedisPool, - ) -> Result, DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for analytics events")?; let key = redis .key() .metadata(ANALYTICS_EVENTS_NAMESPACE, ANALYTICS_EVENTS_ALL_KEY); - if let Some(events) = redis.get_deserialized(&key).await? { + if let Some(events) = redis + .get_deserialized(&key) + .await + .wrap_err("getting cached analytics events")? + { return Ok(events); } @@ -101,9 +112,9 @@ impl DBAnalyticsEvent { ) .fetch(exec) .map(|record| { - let record = record?; + let record = record.wrap_err("reading analytics event record")?; - Ok::<_, DatabaseError>(DBAnalyticsEvent { + eyre::Ok(DBAnalyticsEvent { id: DBAnalyticsEventId(record.id), meta: record.meta.0, starts: record.starts, @@ -111,19 +122,29 @@ impl DBAnalyticsEvent { }) }) .try_collect::>() - .await?; + .await + .wrap_err("fetching analytics events from database")?; - redis.set_serialized(&key, &events, None).await?; + redis + .set_serialized(&key, &events, None) + .await + .wrap_err("caching analytics events")?; Ok(events) } - pub async fn clear_cache(redis: &RedisPool) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + pub async fn clear_cache(redis: &RedisPool) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear analytics event cache")?; let key = redis .key() .metadata(ANALYTICS_EVENTS_NAMESPACE, ANALYTICS_EVENTS_ALL_KEY); - redis.delete(&key).await?; + redis + .delete(&key) + .await + .wrap_err("clearing analytics event cache")?; Ok(()) } } diff --git a/apps/labrinth/src/database/models/categories.rs b/apps/labrinth/src/database/models/categories.rs index c2b60ebeb8..718fae879a 100644 --- a/apps/labrinth/src/database/models/categories.rs +++ b/apps/labrinth/src/database/models/categories.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; +use eyre::{Result, WrapErr}; +use futures::TryStreamExt; use xredis::RedisPool; -use super::DatabaseError; use super::ids::*; -use futures::TryStreamExt; use serde::{Deserialize, Serialize}; const TAGS_NAMESPACE: &str = "tags:v4"; @@ -42,7 +42,7 @@ impl Category { pub async fn get_ids<'a, E>( name: &str, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -54,7 +54,8 @@ impl Category { name, ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching category ids")?; let mut map = HashMap::new(); for r in result { @@ -68,7 +69,7 @@ impl Category { name: &str, project_type: ProjectTypeId, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -81,7 +82,8 @@ impl Category { project_type as ProjectTypeId ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching category id")?; Ok(result.map(|r| CategoryId(r.id))) } @@ -89,16 +91,21 @@ impl Category { pub async fn list<'a, E>( exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached categories")?; let key = redis.key().metadata(TAGS_NAMESPACE, "category"); - let res: Option> = - redis.get_deserialized(&key).await?; + let res: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached categories")?; if let Some(res) = res { return Ok(res); @@ -122,12 +129,19 @@ impl Category { header: c.category_header }) .try_collect::>() - .await?; + .await + .wrap_err("fetching categories")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache categories")?; let key = redis.key().metadata(TAGS_NAMESPACE, "category"); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching categories")?; Ok(result) } @@ -137,7 +151,7 @@ impl LinkPlatform { pub async fn get_id<'a, E>( id: &str, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -149,7 +163,8 @@ impl LinkPlatform { id ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching link platform id")?; Ok(result.map(|r| LinkPlatformId(r.id))) } @@ -157,16 +172,21 @@ impl LinkPlatform { pub async fn list<'a, E>( exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached link platforms")?; let key = redis.key().metadata(TAGS_NAMESPACE, "link_platform"); - let res: Option> = - redis.get_deserialized(&key).await?; + let res: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached link platforms")?; if let Some(res) = res { return Ok(res); @@ -185,12 +205,19 @@ impl LinkPlatform { donation: c.donation, }) .try_collect::>() - .await?; + .await + .wrap_err("fetching link platforms")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache link platforms")?; let key = redis.key().metadata(TAGS_NAMESPACE, "link_platform"); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching link platforms")?; Ok(result) } @@ -200,7 +227,7 @@ impl ReportType { pub async fn get_id<'a, E>( name: &str, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -212,23 +239,28 @@ impl ReportType { name ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching report type id")?; Ok(result.map(|r| ReportTypeId(r.id))) } - pub async fn list<'a, E>( - exec: E, - redis: &RedisPool, - ) -> Result, DatabaseError> + pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached report types")?; let key = redis.key().metadata(TAGS_NAMESPACE, "report_type"); - let res: Option> = redis.get_deserialized(&key).await?; + let res: Option> = + redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached report types")?; if let Some(res) = res { return Ok(res); @@ -243,12 +275,19 @@ impl ReportType { .fetch(exec) .map_ok(|c| c.name) .try_collect::>() - .await?; + .await + .wrap_err("fetching report types")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache report types")?; let key = redis.key().metadata(TAGS_NAMESPACE, "report_type"); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching report types")?; Ok(result) } @@ -258,7 +297,7 @@ impl ProjectType { pub async fn get_id<'a, E>( name: &str, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -270,23 +309,28 @@ impl ProjectType { name ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching project type id")?; Ok(result.map(|r| ProjectTypeId(r.id))) } - pub async fn list<'a, E>( - exec: E, - redis: &RedisPool, - ) -> Result, DatabaseError> + pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached project types")?; let key = redis.key().metadata(TAGS_NAMESPACE, "project_type"); - let res: Option> = redis.get_deserialized(&key).await?; + let res: Option> = + redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached project types")?; if let Some(res) = res { return Ok(res); @@ -301,12 +345,19 @@ impl ProjectType { .fetch(exec) .map_ok(|c| c.name) .try_collect::>() - .await?; + .await + .wrap_err("fetching project types")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache project types")?; let key = redis.key().metadata(TAGS_NAMESPACE, "project_type"); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching project types")?; Ok(result) } diff --git a/apps/labrinth/src/database/models/charge_item.rs b/apps/labrinth/src/database/models/charge_item.rs index 1f5f292297..5f79d26a4a 100644 --- a/apps/labrinth/src/database/models/charge_item.rs +++ b/apps/labrinth/src/database/models/charge_item.rs @@ -1,11 +1,12 @@ use crate::database::PgTransaction; use crate::database::models::{ - DBChargeId, DBProductPriceId, DBUserId, DBUserSubscriptionId, DatabaseError, + DBChargeId, DBProductPriceId, DBUserId, DBUserSubscriptionId, }; use crate::models::billing::{ ChargeStatus, ChargeType, PaymentPlatform, PriceDuration, }; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use std::convert::{TryFrom, TryInto}; #[derive(Clone)] @@ -125,7 +126,7 @@ impl DBCharge { pub async fn upsert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { sqlx::query!( r#" INSERT INTO charges (id, user_id, price_id, amount, currency_code, charge_type, status, due, last_attempt, subscription_id, subscription_interval, payment_platform, payment_platform_id, parent_charge_id, net, tax_amount, tax_platform_id, tax_last_updated, tax_drift_loss, tax_transaction_version, tax_platform_accounting_time) @@ -175,7 +176,8 @@ impl DBCharge { self.tax_platform_accounting_time, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("upserting charge")?; Ok(self.id) } @@ -183,11 +185,12 @@ impl DBCharge { pub async fn get( id: DBChargeId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let id = id.0; let res = select_charges_with_predicate!("WHERE id = $1", id) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching charge")?; Ok(res.and_then(|r| r.try_into().ok())) } @@ -195,43 +198,45 @@ impl DBCharge { pub async fn get_from_user( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let user_id = user_id.0; let res = select_charges_with_predicate!( "WHERE user_id = $1 ORDER BY due DESC", user_id ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching charges for user")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing charges for user") } pub async fn get_children( charge_id: DBChargeId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let charge_id = charge_id.0; let res = select_charges_with_predicate!( "WHERE parent_charge_id = $1", charge_id ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching child charges")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing child charges") } pub async fn get_open_subscription( user_subscription_id: DBUserSubscriptionId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let user_subscription_id = user_subscription_id.0; let res = select_charges_with_predicate!( "WHERE @@ -241,14 +246,15 @@ impl DBCharge { user_subscription_id ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching open subscription charge")?; Ok(res.and_then(|r| r.try_into().ok())) } pub async fn get_chargeable( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let charge_type = ChargeType::Subscription.as_str(); let res = select_charges_with_predicate!( r#" @@ -262,17 +268,18 @@ impl DBCharge { charge_type ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching chargeable charges")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing chargeable charges") } pub async fn get_unprovision( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let charge_type = ChargeType::Subscription.as_str(); let res = select_charges_with_predicate!( r#" @@ -289,17 +296,18 @@ impl DBCharge { charge_type ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching charges to unprovision")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing charges to unprovision") } pub async fn get_cancellable( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let charge_type = ChargeType::Subscription.as_str(); let res = select_charges_with_predicate!( r#" @@ -310,12 +318,13 @@ impl DBCharge { charge_type ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching cancellable charges")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing cancellable charges") } /// Returns all charges that need to have their tax amount updated. @@ -330,7 +339,7 @@ impl DBCharge { pub async fn get_updateable_lock( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, limit: i64, - ) -> Result, DatabaseError> { + ) -> Result> { let res = select_charges_with_predicate!( " INNER JOIN users u ON u.id = charges.user_id @@ -347,12 +356,13 @@ impl DBCharge { limit ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching charges requiring tax updates")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing charges requiring tax updates") } /// Returns all charges which are missing a tax identifier, that is, are succeeded and haven't been assigned a tax identifier yet. @@ -362,7 +372,7 @@ impl DBCharge { exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, offset: i64, limit: i64, - ) -> Result, DatabaseError> { + ) -> Result> { let res = select_charges_with_predicate!( " WHERE @@ -378,18 +388,19 @@ impl DBCharge { limit ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching charges missing tax identifiers")?; - Ok(res - .into_iter() + res.into_iter() .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .collect::, serde_json::Error>>() + .wrap_err("parsing charges missing tax identifiers") } pub async fn remove( id: DBChargeId, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " DELETE FROM charges @@ -398,7 +409,8 @@ impl DBCharge { id.0 as i64 ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("removing charge")?; Ok(()) } diff --git a/apps/labrinth/src/database/models/collection_item.rs b/apps/labrinth/src/database/models/collection_item.rs index 6f89b75e71..6d212cb56c 100644 --- a/apps/labrinth/src/database/models/collection_item.rs +++ b/apps/labrinth/src/database/models/collection_item.rs @@ -1,9 +1,9 @@ use super::ids::*; -use crate::database::models::DatabaseError; use crate::database::{PgTransaction, models}; use crate::models::collections::CollectionStatus; use chrono::{DateTime, Utc}; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use serde::{Deserialize, Serialize}; use xredis::RedisPool; @@ -24,7 +24,7 @@ impl CollectionBuilder { pub async fn insert( self, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { let collection_struct = DBCollection { id: self.collection_id, name: self.name, @@ -38,7 +38,10 @@ impl CollectionBuilder { status: self.status, projects: self.projects, }; - collection_struct.insert(transaction).await?; + collection_struct + .insert(transaction) + .await + .wrap_err("inserting built collection")?; Ok(self.collection_id) } @@ -62,7 +65,7 @@ impl DBCollection { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO collections ( @@ -84,7 +87,8 @@ impl DBCollection { self.status.to_string(), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting collection")?; let (collection_ids, project_ids): (Vec<_>, Vec<_>) = self.projects.iter().map(|p| (self.id.0, p.0)).unzip(); @@ -98,7 +102,8 @@ impl DBCollection { &project_ids[..], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting collection projects")?; Ok(()) } @@ -107,8 +112,10 @@ impl DBCollection { id: DBCollectionId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { - let collection = Self::get(id, &mut *transaction, redis).await?; + ) -> Result> { + let collection = Self::get(id, &mut *transaction, redis) + .await + .wrap_err("fetching collection to remove")?; if let Some(collection) = collection { sqlx::query!( @@ -119,7 +126,8 @@ impl DBCollection { id as DBCollectionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting collection projects")?; sqlx::query!( " @@ -129,9 +137,12 @@ impl DBCollection { id as DBCollectionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting collection")?; - models::DBCollection::clear_cache(collection.id, redis).await?; + models::DBCollection::clear_cache(collection.id, redis) + .await + .wrap_err("clearing removed collection cache")?; Ok(Some(())) } else { @@ -143,12 +154,13 @@ impl DBCollection { id: DBCollectionId, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { DBCollection::get_many(&[id], executor, redis) .await + .wrap_err("fetching collection") .map(|x| x.into_iter().next()) } @@ -156,7 +168,7 @@ impl DBCollection { collection_ids: &[DBCollectionId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -165,7 +177,7 @@ impl DBCollection { COLLECTIONS_NAMESPACE, &collection_ids.iter().map(|x| x.0).collect::>(), |collection_ids| async move { - let collections = sqlx::query!( + sqlx::query!( " SELECT c.id id, c.name name, c.description description, c.icon_url icon_url, c.raw_icon_url raw_icon_url, c.color color, c.created created, c.user_id user_id, @@ -200,14 +212,13 @@ impl DBCollection { }; acc.insert(m.id, collection); - async move { Ok(acc) } + async move { Ok::<_, sqlx::Error>(acc) } }) - .await?; - - Ok::<_, DatabaseError>(collections) + .await }, ) - .await?; + .await + .wrap_err("fetching cached collections")?; Ok(val) } @@ -215,11 +226,17 @@ impl DBCollection { pub async fn clear_cache( id: DBCollectionId, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear collection cache")?; let key = redis.key().entity(COLLECTIONS_NAMESPACE, id.0); - redis.delete(&key).await?; + redis + .delete(&key) + .await + .wrap_err("clearing collection cache")?; Ok(()) } } diff --git a/apps/labrinth/src/database/models/delphi_report_item.rs b/apps/labrinth/src/database/models/delphi_report_item.rs index fb5ff96b75..c954205e65 100644 --- a/apps/labrinth/src/database/models/delphi_report_item.rs +++ b/apps/labrinth/src/database/models/delphi_report_item.rs @@ -4,14 +4,15 @@ use std::{ }; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use sqlx::types::Json; use crate::database::{ PgTransaction, models::{ - DBFileId, DBProjectId, DatabaseError, DelphiReportId, - DelphiReportIssueDetailsId, DelphiReportIssueId, + DBFileId, DBProjectId, DelphiReportId, DelphiReportIssueDetailsId, + DelphiReportIssueId, }, }; @@ -36,7 +37,7 @@ impl DBDelphiReport { pub async fn upsert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { Ok(DelphiReportId(sqlx::query_scalar!( " INSERT INTO delphi_reports (file_id, delphi_version, artifact_url, severity) @@ -51,7 +52,8 @@ impl DBDelphiReport { self.severity as DelphiSeverity, ) .fetch_one(&mut *transaction) - .await?)) + .await + .wrap_err("upserting delphi report")?)) } } @@ -189,7 +191,7 @@ impl DBDelphiReportIssue { pub async fn upsert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { Ok(DelphiReportIssueId( sqlx::query_scalar!( " @@ -203,14 +205,15 @@ impl DBDelphiReportIssue { self.issue_type, ) .fetch_one(&mut *transaction) - .await?, + .await + .wrap_err("upserting delphi report issue")?, )) } pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { Ok(DelphiReportIssueId( sqlx::query_scalar!( " @@ -222,7 +225,8 @@ impl DBDelphiReportIssue { self.issue_type, ) .fetch_one(&mut *transaction) - .await?, + .await + .wrap_err("inserting delphi report issue")?, )) } } @@ -270,7 +274,7 @@ impl ReportIssueDetail { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { Ok(DelphiReportIssueDetailsId(sqlx::query_scalar!( " INSERT INTO delphi_report_issue_details (issue_id, key, jar, file_path, decompiled_source, data, severity) @@ -286,19 +290,21 @@ impl ReportIssueDetail { self.severity as DelphiSeverity, ) .fetch_one(&mut *transaction) - .await?)) + .await + .wrap_err("inserting delphi report issue detail")?)) } pub async fn remove_all_by_issue_id( issue_id: DelphiReportIssueId, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { Ok(sqlx::query!( "DELETE FROM delphi_report_issue_details WHERE issue_id = $1", issue_id as DelphiReportIssueId, ) .execute(&mut *transaction) - .await? + .await + .wrap_err("removing delphi report issue details")? .rows_affected()) } } diff --git a/apps/labrinth/src/database/models/flow_item.rs b/apps/labrinth/src/database/models/flow_item.rs index dd675941ae..995c0235a3 100644 --- a/apps/labrinth/src/database/models/flow_item.rs +++ b/apps/labrinth/src/database/models/flow_item.rs @@ -1,9 +1,9 @@ use super::ids::*; use crate::auth::oauth::uris::OAuthRedirectUris; -use crate::database::models::DatabaseError; use crate::models::pats::Scopes; use crate::{auth::AuthProvider, routes::internal::flows::TempUser}; use chrono::Duration; +use eyre::{Result, WrapErr}; use rand::Rng; use rand::distributions::Alphanumeric; use rand_chacha::ChaCha20Rng; @@ -99,13 +99,17 @@ impl DBFlow { expires: Duration, redis: &RedisPool, state: &str, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to insert flow")?; let key = redis.key().entity(FLOWS_NAMESPACE, state); redis .set_serialized(&key, &self, Some(expires.num_seconds())) - .await?; + .await + .wrap_err("inserting flow into redis")?; Ok(()) } @@ -113,25 +117,30 @@ impl DBFlow { &self, expires: Duration, redis: &RedisPool, - ) -> Result { + ) -> Result { let state = ChaCha20Rng::from_entropy() .sample_iter(&Alphanumeric) .take(32) .map(char::from) .collect::(); - self.insert_with_state(expires, redis, &state).await?; + self.insert_with_state(expires, redis, &state) + .await + .wrap_err("inserting flow with generated state")?; Ok(state) } - pub async fn get( - id: &str, - redis: &RedisPool, - ) -> Result, DatabaseError> { - let mut redis = redis.connect().await?; + pub async fn get(id: &str, redis: &RedisPool) -> Result> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to get flow")?; let key = redis.key().entity(FLOWS_NAMESPACE, id); - redis.get_deserialized(&key).await.map_err(Into::into) + redis + .get_deserialized(&key) + .await + .wrap_err("getting flow from redis") } /// Gets the flow and removes it from the cache, but only removes if the flow was present and the predicate returned true @@ -140,24 +149,31 @@ impl DBFlow { id: &str, predicate: impl FnOnce(&DBFlow) -> bool, redis: &RedisPool, - ) -> Result, DatabaseError> { - let flow = Self::get(id, redis).await?; + ) -> Result> { + let flow = Self::get(id, redis) + .await + .wrap_err("getting flow before conditional removal")?; if let Some(flow) = flow.as_ref() && predicate(flow) { - Self::remove(id, redis).await?; + Self::remove(id, redis) + .await + .wrap_err("removing flow after predicate matched")?; } Ok(flow) } - pub async fn remove( - id: &str, - redis: &RedisPool, - ) -> Result, DatabaseError> { - let mut redis = redis.connect().await?; + pub async fn remove(id: &str, redis: &RedisPool) -> Result> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to remove flow")?; let key = redis.key().entity(FLOWS_NAMESPACE, id); - redis.delete(&key).await?; + redis + .delete(&key) + .await + .wrap_err("removing flow from redis")?; Ok(Some(())) } } diff --git a/apps/labrinth/src/database/models/ids.rs b/apps/labrinth/src/database/models/ids.rs index bd953e7e2d..0357c810b0 100644 --- a/apps/labrinth/src/database/models/ids.rs +++ b/apps/labrinth/src/database/models/ids.rs @@ -1,4 +1,3 @@ -use super::DatabaseError; use crate::database::PgTransaction; use crate::models::ids::{ AffiliateCodeId, AnalyticsEventId, AttributionGroupId, CampaignDonationId, @@ -11,6 +10,7 @@ use crate::models::ids::{ use ariadne::ids::base62_impl::to_base62; use ariadne::ids::{UserId, random_base62_rng, random_base62_rng_range}; use censor::Censor; +use eyre::{Result, WrapErr}; use paste::paste; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; @@ -23,7 +23,7 @@ macro_rules! generate_ids { ($function_name:ident, $return_type:ident, $select_stmnt:expr) => { pub async fn $function_name( con: &mut PgTransaction<'_>, - ) -> Result<$return_type, DatabaseError> { + ) -> Result<$return_type> { let mut rng = ChaCha20Rng::from_entropy(); let length = 8; let mut id = random_base62_rng(&mut rng, length); @@ -34,7 +34,8 @@ macro_rules! generate_ids { loop { let results = sqlx::query!($select_stmnt, id as i64) .fetch_one(&mut *con) - .await?; + .await + .wrap_err("checking generated ID uniqueness")?; if results.exists.unwrap_or(true) || censor.check(&*to_base62(id)) @@ -46,7 +47,9 @@ macro_rules! generate_ids { retry_count += 1; if retry_count > ID_RETRY_COUNT { - return Err(DatabaseError::RandomId); + return Err(eyre::eyre!( + "failed to generate a unique random ID" + )); } } @@ -60,7 +63,7 @@ macro_rules! generate_bulk_ids { pub async fn $function_name( count: usize, con: &mut PgTransaction<'_>, - ) -> Result, DatabaseError> { + ) -> Result> { let mut retry_count = 0; // Check if ID is unique @@ -75,7 +78,8 @@ macro_rules! generate_bulk_ids { let results = sqlx::query!($select_stmnt, &ids) .fetch_one(&mut *con) - .await?; + .await + .wrap_err("checking generated ID uniqueness")?; if !results.exists.unwrap_or(true) { return Ok(ids @@ -86,7 +90,9 @@ macro_rules! generate_bulk_ids { retry_count += 1; if retry_count > ID_RETRY_COUNT { - return Err(DatabaseError::RandomId); + return Err(eyre::eyre!( + "failed to generate unique random IDs" + )); } } } diff --git a/apps/labrinth/src/database/models/image_item.rs b/apps/labrinth/src/database/models/image_item.rs index f3403ed7fc..ae24846222 100644 --- a/apps/labrinth/src/database/models/image_item.rs +++ b/apps/labrinth/src/database/models/image_item.rs @@ -1,8 +1,9 @@ use super::ids::*; use crate::database::PgTransaction; -use crate::{database::models::DatabaseError, models::images::ImageContext}; +use crate::models::images::ImageContext; use chrono::{DateTime, Utc}; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use xredis::RedisPool; @@ -30,7 +31,7 @@ impl DBImage { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO uploaded_images ( @@ -53,7 +54,8 @@ impl DBImage { self.report_id.map(|x| x.0), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting uploaded image")?; Ok(()) } @@ -62,8 +64,10 @@ impl DBImage { id: DBImageId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { - let image = Self::get(id, &mut *transaction, redis).await?; + ) -> Result> { + let image = Self::get(id, &mut *transaction, redis) + .await + .wrap_err("fetching uploaded image to remove")?; if let Some(image) = image { sqlx::query!( @@ -74,9 +78,12 @@ impl DBImage { id as DBImageId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting uploaded image")?; - DBImage::clear_cache(image.id, redis).await?; + DBImage::clear_cache(image.id, redis) + .await + .wrap_err("clearing removed uploaded image cache")?; Ok(Some(())) } else { @@ -87,7 +94,7 @@ impl DBImage { pub async fn get_many_contexted( context: ImageContext, transaction: &mut PgTransaction<'_>, - ) -> Result, sqlx::Error> { + ) -> std::result::Result, sqlx::Error> { // Set all of project_id, version_id, thread_message_id, report_id to None // Then set the one that is relevant to Some @@ -164,12 +171,13 @@ impl DBImage { id: DBImageId, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { DBImage::get_many(&[id], executor, redis) .await + .wrap_err("fetching uploaded image") .map(|x| x.into_iter().next()) } @@ -177,7 +185,7 @@ impl DBImage { image_ids: &[DBImageId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -187,7 +195,7 @@ impl DBImage { IMAGES_NAMESPACE, &image_ids.iter().map(|x| x.0).collect::>(), |image_ids| async move { - let images = sqlx::query!( + sqlx::query!( " SELECT id, url, raw_url, size, created, owner_id, context, mod_id, version_id, thread_message_id, report_id FROM uploaded_images @@ -213,25 +221,28 @@ impl DBImage { }; acc.insert(i.id, img); - async move { Ok(acc) } + async move { Ok::<_, sqlx::Error>(acc) } }) - .await?; - - Ok::<_, DatabaseError>(images) + .await }, - ).await?; + ) + .await + .wrap_err("fetching cached uploaded images")?; Ok(val) } - pub async fn clear_cache( - id: DBImageId, - redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + pub async fn clear_cache(id: DBImageId, redis: &RedisPool) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear uploaded image cache")?; let key = redis.key().entity(IMAGES_NAMESPACE, id.0); - redis.delete(&key).await?; + redis + .delete(&key) + .await + .wrap_err("clearing uploaded image cache")?; Ok(()) } } diff --git a/apps/labrinth/src/database/models/legacy_loader_fields.rs b/apps/labrinth/src/database/models/legacy_loader_fields.rs index f24291c25e..95ac9c01be 100644 --- a/apps/labrinth/src/database/models/legacy_loader_fields.rs +++ b/apps/labrinth/src/database/models/legacy_loader_fields.rs @@ -5,6 +5,7 @@ // These fields only apply to minecraft-java, and are hardcoded to the minecraft-java game. use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -12,7 +13,7 @@ use serde_json::json; use xredis::RedisPool; use super::{ - DatabaseError, LoaderFieldEnumValueId, + LoaderFieldEnumValueId, loader_fields::{ LoaderFieldEnum, LoaderFieldEnumValue, VersionField, VersionFieldValue, }, @@ -41,22 +42,24 @@ impl MinecraftGameVersion { major_option: Option, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { - let mut exec = exec.acquire().await?; + let mut exec = exec + .acquire() + .await + .wrap_err("acquiring database connection")?; let game_version_enum = LoaderFieldEnum::get(Self::FIELD_NAME, &mut exec, redis) - .await? - .ok_or_else(|| { - DatabaseError::SchemaError( - "Could not find game version enum.".to_string(), - ) - })?; + .await + .wrap_err("fetching game version enum")? + .ok_or_else(|| eyre::eyre!("Could not find game version enum.")) + .wrap_err("finding game version enum")?; let game_version_enum_values = LoaderFieldEnumValue::list(game_version_enum.id, &mut exec, redis) - .await?; + .await + .wrap_err("fetching game version enum values")?; let game_versions = game_version_enum_values .into_iter() @@ -82,13 +85,13 @@ impl MinecraftGameVersion { // Clones on success pub fn try_from_version_field( version_field: &VersionField, - ) -> Result, DatabaseError> { + ) -> Result> { if version_field.field_name != Self::FIELD_NAME { - return Err(DatabaseError::SchemaError(format!( + return Err(eyre::eyre!( "Field name {} is not {}", version_field.field_name, Self::FIELD_NAME - ))); + )); } let game_versions = match version_field.clone() { VersionField { @@ -102,9 +105,9 @@ impl MinecraftGameVersion { vec![Self::from_enum_value(value)] } _ => { - return Err(DatabaseError::SchemaError(format!( + return Err(eyre::eyre!( "Game version requires field value to be an enum: {version_field:?}" - ))); + )); } }; Ok(game_versions) @@ -138,7 +141,7 @@ impl<'a> MinecraftGameVersionBuilder<'a> { pub fn version( self, version: &'a str, - ) -> Result, DatabaseError> { + ) -> Result> { Ok(Self { version: Some(version), ..self @@ -148,7 +151,7 @@ impl<'a> MinecraftGameVersionBuilder<'a> { pub fn version_type( self, version_type: &'a str, - ) -> Result, DatabaseError> { + ) -> Result> { Ok(Self { version_type: Some(version_type), ..self @@ -169,16 +172,18 @@ impl<'a> MinecraftGameVersionBuilder<'a> { self, exec: E, redis: &RedisPool, - ) -> Result + ) -> Result where E: crate::database::Executor<'b, Database = sqlx::Postgres> + Copy, { let game_versions_enum = LoaderFieldEnum::get("game_versions", exec, redis) - .await? - .ok_or(DatabaseError::SchemaError( - "Missing loaders field: 'game_versions'".to_string(), - ))?; + .await + .wrap_err("fetching game versions loader field")? + .ok_or_else(|| { + eyre::eyre!("Missing loaders field: 'game_versions'") + }) + .wrap_err("finding game versions loader field")?; // Get enum id for game versions let metadata = json!({ @@ -208,14 +213,20 @@ impl<'a> MinecraftGameVersionBuilder<'a> { metadata ) .fetch_one(exec) - .await?; + .await + .wrap_err("inserting game version")?; - let mut conn = redis.connect().await?; + let mut conn = redis + .connect() + .await + .wrap_err("connecting to redis to clear game version cache")?; let key = conn.key().entity( crate::database::models::loader_fields::LOADER_FIELD_ENUM_VALUES_NAMESPACE, game_versions_enum.id.0, ); - conn.delete(&key).await?; + conn.delete(&key) + .await + .wrap_err("clearing cached game versions")?; Ok(LoaderFieldEnumValueId(result.id)) } diff --git a/apps/labrinth/src/database/models/loader_fields.rs b/apps/labrinth/src/database/models/loader_fields.rs index bb599a1764..4ddbd90e4b 100644 --- a/apps/labrinth/src/database/models/loader_fields.rs +++ b/apps/labrinth/src/database/models/loader_fields.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; use std::hash::Hasher; -use super::DatabaseError; use super::ids::*; use crate::database::PgTransaction; use chrono::DateTime; use chrono::Utc; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -35,28 +35,31 @@ impl Game { slug: &str, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Ok(Self::list(exec, redis) - .await? + .await + .wrap_err("listing games")? .into_iter() .find(|x| x.slug == slug)) } - pub async fn list<'a, E>( - exec: E, - redis: &RedisPool, - ) -> Result, DatabaseError> + pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached games")?; let key = redis.key().metadata(GAMES_LIST_NAMESPACE, "games"); - let cached_games: Option> = - redis.get_deserialized(&key).await?; + let cached_games: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached games")?; if let Some(cached_games) = cached_games { return Ok(cached_games); } @@ -76,12 +79,19 @@ impl Game { banner_url: x.banner_url, }) .try_collect::>() - .await?; + .await + .wrap_err("fetching games")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache games")?; let key = redis.key().metadata(GAMES_LIST_NAMESPACE, "games"); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching games")?; Ok(result) } @@ -107,14 +117,20 @@ impl Loader { name: &str, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached loader id")?; let key = redis.key().metadata(LOADER_ID, name); - let cached_id: Option = redis.get_deserialized(&key).await?; + let cached_id: Option = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached loader id")?; if let Some(cached_id) = cached_id { return Ok(Some(LoaderId(cached_id))); } @@ -128,30 +144,39 @@ impl Loader { name ) .fetch_optional(exec) - .await? + .await + .wrap_err("fetching loader id")? .map(|r| LoaderId(r.id)); if let Some(result) = result { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache loader id")?; let key = redis.key().metadata(LOADER_ID, name); - redis.set_serialized(&key, &result.0, None).await?; + redis + .set_serialized(&key, &result.0, None) + .await + .wrap_err("caching loader id")?; } Ok(result) } - pub async fn list<'a, E>( - exec: E, - redis: &RedisPool, - ) -> Result, DatabaseError> + pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached loaders")?; let key = redis.key().metadata(LOADERS_LIST_NAMESPACE, "all"); - let cached_loaders: Option> = - redis.get_deserialized(&key).await?; + let cached_loaders: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached loaders")?; if let Some(cached_loaders) = cached_loaders { return Ok(cached_loaders); } @@ -190,12 +215,19 @@ impl Loader { }, }) .try_collect::>() - .await?; + .await + .wrap_err("fetching loaders")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache loaders")?; let key = redis.key().metadata(LOADERS_LIST_NAMESPACE, "all"); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching loaders")?; Ok(result) } @@ -376,11 +408,13 @@ impl LoaderField { loader_ids: &[LoaderId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { - let fields = Self::get_fields(loader_ids, exec, redis).await?; + let fields = Self::get_fields(loader_ids, exec, redis) + .await + .wrap_err("fetching loader fields")?; Ok(fields.into_iter().find(|f| f.field == field)) } @@ -390,12 +424,14 @@ impl LoaderField { loader_ids: &[LoaderId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { let found_loader_fields = - Self::get_fields_per_loader(loader_ids, exec, redis).await?; + Self::get_fields_per_loader(loader_ids, exec, redis) + .await + .wrap_err("fetching loader fields by loader")?; let result = found_loader_fields .into_values() .flatten() @@ -408,7 +444,7 @@ impl LoaderField { loader_ids: &[LoaderId], exec: E, redis: &RedisPool, - ) -> Result>, DatabaseError> + ) -> Result>> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -426,6 +462,7 @@ impl LoaderField { &loader_ids, ) .fetch(exec) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc: DashMap>, r| { if let Some(field_type) = LoaderFieldType::build(&r.field_type, r.enum_type) { let loader_field = LoaderField { @@ -442,15 +479,16 @@ impl LoaderField { .push(loader_field); } - async move { - Ok(acc) - } + async move { eyre::Ok(acc) } }) - .await?; + .await + .wrap_err("fetching loader fields")?; - Ok::<_, DatabaseError>(result) + eyre::Ok(result) }, - ).await?; + ) + .await + .wrap_err("fetching cached loader fields")?; Ok(val.into_iter().map(|x| (LoaderId(x.0), x.1)).collect()) } @@ -461,16 +499,21 @@ impl LoaderField { pub async fn get_fields_all<'a, E>( exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached loader fields")?; let key = redis.key().metadata(LOADER_FIELDS_NAMESPACE_ALL, ""); - let cached_fields: Option> = - redis.get_deserialized(&key).await?; + let cached_fields: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached loader fields")?; if let Some(cached_fields) = cached_fields { return Ok(cached_fields); @@ -485,25 +528,34 @@ impl LoaderField { ) .fetch(exec) .map_ok(|r| { - Some(LoaderField { - id: LoaderFieldId(r.id), - field_type: LoaderFieldType::build(&r.field_type, r.enum_type)?, - field: r.field, - optional: r.optional, - min_val: r.min_val, - max_val: r.max_val, + LoaderFieldType::build(&r.field_type, r.enum_type).map(|field_type| { + LoaderField { + id: LoaderFieldId(r.id), + field_type, + field: r.field, + optional: r.optional, + min_val: r.min_val, + max_val: r.max_val, + } }) }) .try_collect::>>() - .await? + .await + .wrap_err("fetching all loader fields")? .into_iter() .flatten() .collect(); - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache loader fields")?; let key = redis.key().metadata(LOADER_FIELDS_NAMESPACE_ALL, ""); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching loader fields")?; Ok(result) } @@ -513,17 +565,23 @@ impl LoaderFieldEnum { enum_name: &str, // Note: NOT loader field name exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for cached loader field enum")?; let key = redis .key() .metadata(LOADER_FIELD_ENUMS_ID_NAMESPACE, enum_name); - let cached_enum = redis.get_deserialized(&key).await?; + let cached_enum = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached loader field enum")?; if let Some(cached_enum) = cached_enum { return Ok(cached_enum); } @@ -539,7 +597,8 @@ impl LoaderFieldEnum { enum_name ) .fetch_optional(exec) - .await? + .await + .wrap_err("fetching loader field enum")? .map(|l| LoaderFieldEnum { id: LoaderFieldEnumId(l.id), enum_name: l.enum_name, @@ -547,12 +606,18 @@ impl LoaderFieldEnum { hidable: l.hidable, }); - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache loader field enum")?; let key = redis .key() .metadata(LOADER_FIELD_ENUMS_ID_NAMESPACE, enum_name); - redis.set_serialized(&key, &result, None).await?; + redis + .set_serialized(&key, &result, None) + .await + .wrap_err("caching loader field enum")?; Ok(result) } @@ -563,12 +628,13 @@ impl LoaderFieldEnumValue { loader_field_enum_id: LoaderFieldEnumId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Ok(Self::list_many(&[loader_field_enum_id], exec, redis) - .await? + .await + .wrap_err("fetching loader field enum values")? .into_iter() .next() .map(|x| x.1) @@ -579,7 +645,7 @@ impl LoaderFieldEnumValue { loader_fields: &[LoaderField], exec: E, redis: &RedisPool, - ) -> Result>, DatabaseError> + ) -> Result>> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -595,7 +661,8 @@ impl LoaderFieldEnumValue { .filter_map(get_enum_id) .collect::>(); let values = Self::list_many(&enum_ids, exec, redis) - .await? + .await + .wrap_err("fetching loader field enum values")? .into_iter() .collect::>(); @@ -615,10 +682,7 @@ impl LoaderFieldEnumValue { loader_field_enum_ids: &[LoaderFieldEnumId], exec: E, redis: &RedisPool, - ) -> Result< - HashMap>, - DatabaseError, - > + ) -> Result>> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -642,6 +706,7 @@ impl LoaderFieldEnumValue { &loader_field_enum_ids ) .fetch(exec) + .map_err(eyre::Report::from) .try_fold( DashMap::new(), |acc: DashMap>, c| { @@ -657,15 +722,17 @@ impl LoaderFieldEnumValue { acc.entry(c.enum_id).or_default().push(value); - async move { Ok(acc) } + async move { eyre::Ok(acc) } }, ) - .await?; + .await + .wrap_err("fetching loader field enum values")?; - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ) - .await?; + .await + .wrap_err("fetching cached loader field enum values")?; Ok(val .into_iter() @@ -679,12 +746,13 @@ impl LoaderFieldEnumValue { filter: HashMap, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { let result = Self::list(loader_field_enum_id, exec, redis) - .await? + .await + .wrap_err("fetching loader field enum values to filter")? .into_iter() .filter(|x| { filter.iter().all(|(key, value)| match key.as_str() { @@ -708,7 +776,7 @@ impl VersionField { pub async fn insert_many( items: Vec, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let mut query_version_fields = vec![]; for item in items { let base = QueryVersionField { @@ -788,7 +856,8 @@ impl VersionField { &enum_values[..] as &[i32] ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting version fields")?; Ok(()) } @@ -911,12 +980,13 @@ impl VersionField { loader_field: LoaderField, query_version_fields: Vec, query_loader_field_enum_values: &[QueryLoaderFieldEnumValue], - ) -> Result { + ) -> Result { let (version_id, value) = VersionFieldValue::build( &loader_field.field_type, query_version_fields, query_loader_field_enum_values, - )?; + ) + .wrap_err("building version field value")?; Ok(VersionField { version_id, field_id: loader_field.id, @@ -929,12 +999,13 @@ impl VersionField { loader_field: LoaderField, query_version_fields: Vec, query_loader_field_enum_values: &[QueryLoaderFieldEnumValue], - ) -> Result, DatabaseError> { + ) -> Result> { let values = VersionFieldValue::build_many( &loader_field.field_type, query_version_fields, query_loader_field_enum_values, - )?; + ) + .wrap_err("building version field values")?; Ok(values .into_iter() .map(|(version_id, value)| VersionField { @@ -1044,37 +1115,39 @@ impl VersionFieldValue { field_type: &LoaderFieldType, qvfs: Vec, qlfev: &[QueryLoaderFieldEnumValue], - ) -> Result<(DBVersionId, VersionFieldValue), DatabaseError> { + ) -> Result<(DBVersionId, VersionFieldValue)> { match field_type { LoaderFieldType::Integer | LoaderFieldType::Text | LoaderFieldType::Boolean | LoaderFieldType::Enum(_) => { - let mut fields = Self::build_many(field_type, qvfs, qlfev)?; + let mut fields = Self::build_many(field_type, qvfs, qlfev) + .wrap_err("building singleton version field values")?; if fields.len() > 1 { - return Err(DatabaseError::SchemaError(format!( + return Err(eyre::eyre!( "Multiple fields for field {}", field_type.to_str() - ))); + )); } fields.pop().ok_or_else(|| { - DatabaseError::SchemaError(format!( + eyre::eyre!( "No version fields for field {}", field_type.to_str() - )) + ) }) } LoaderFieldType::ArrayInteger | LoaderFieldType::ArrayText | LoaderFieldType::ArrayBoolean | LoaderFieldType::ArrayEnum(_) => { - let fields = Self::build_many(field_type, qvfs, qlfev)?; - Ok(fields.into_iter().next().ok_or_else(|| { - DatabaseError::SchemaError(format!( + let fields = Self::build_many(field_type, qvfs, qlfev) + .wrap_err("building array version field values")?; + fields.into_iter().next().ok_or_else(|| { + eyre::eyre!( "No version fields for field {}", field_type.to_str() - )) - })?) + ) + }) } } } @@ -1088,12 +1161,12 @@ impl VersionFieldValue { field_type: &LoaderFieldType, qvfs: Vec, qlfev: &[QueryLoaderFieldEnumValue], - ) -> Result, DatabaseError> { + ) -> Result> { let field_name = field_type.to_str(); let did_not_exist_error = |field_name: &str, desired_field: &str| { - DatabaseError::SchemaError(format!( + eyre::eyre!( "Field name {desired_field} for field {field_name} in does not exist" - )) + ) }; // Check errors- version_id must all be the same @@ -1108,98 +1181,109 @@ impl VersionFieldValue { .unwrap_or(DBVersionId(0)); if qvfs.iter().map(|qvf| qvf.field_id).unique().count() > 1 { - return Err(DatabaseError::SchemaError(format!( + return Err(eyre::eyre!( "Multiple field ids for field {field_name}" - ))); + )); } let mut value = match field_type { // Singleton fields // If there are multiple, we assume multiple versions are being concatenated - LoaderFieldType::Integer => { - qvfs.into_iter() - .map(|qvf| { - Ok(( - qvf.version_id, - VersionFieldValue::Integer(qvf.int_value.ok_or( - did_not_exist_error(field_name, "int_value"), - )?), - )) - }) - .collect::, - DatabaseError, - >>()? - } - LoaderFieldType::Text => { - qvfs.into_iter() - .map(|qvf| { - Ok(( - qvf.version_id, - VersionFieldValue::Text(qvf.string_value.ok_or( - did_not_exist_error(field_name, "string_value"), - )?), - )) - }) - .collect::, - DatabaseError, - >>()? - } - LoaderFieldType::Boolean => { - qvfs.into_iter() - .map(|qvf| { - Ok(( - qvf.version_id, - VersionFieldValue::Boolean( - qvf.int_value.ok_or(did_not_exist_error( - field_name, - "int_value", - ))? != 0, - ), - )) - }) - .collect::, - DatabaseError, - >>()? - } - LoaderFieldType::Enum(id) => { - qvfs.into_iter() - .map(|qvf| { - Ok(( - qvf.version_id, - VersionFieldValue::Enum(*id, { - let enum_id = qvf.enum_value.ok_or( + LoaderFieldType::Integer => qvfs + .into_iter() + .map(|qvf| { + eyre::Ok(( + qvf.version_id, + VersionFieldValue::Integer( + qvf.int_value + .ok_or_else(|| { + did_not_exist_error(field_name, "int_value") + }) + .wrap_err( + "reading integer version field value", + )?, + ), + )) + }) + .collect::>>() + .wrap_err("building integer version field values")?, + LoaderFieldType::Text => qvfs + .into_iter() + .map(|qvf| { + eyre::Ok(( + qvf.version_id, + VersionFieldValue::Text( + qvf.string_value + .ok_or_else(|| { + did_not_exist_error( + field_name, + "string_value", + ) + }) + .wrap_err("reading text version field value")?, + ), + )) + }) + .collect::>>() + .wrap_err("building text version field values")?, + LoaderFieldType::Boolean => qvfs + .into_iter() + .map(|qvf| { + eyre::Ok(( + qvf.version_id, + VersionFieldValue::Boolean( + qvf.int_value + .ok_or_else(|| { + did_not_exist_error(field_name, "int_value") + }) + .wrap_err( + "reading boolean version field value", + )? + != 0, + ), + )) + }) + .collect::>>() + .wrap_err("building boolean version field values")?, + LoaderFieldType::Enum(id) => qvfs + .into_iter() + .map(|qvf| { + eyre::Ok(( + qvf.version_id, + VersionFieldValue::Enum(*id, { + let enum_id = qvf + .enum_value + .ok_or_else(|| { did_not_exist_error( field_name, "enum_value", - ), - )?; - let lfev = qlfev - .iter() - .find(|x| x.id == enum_id) - .ok_or(did_not_exist_error( + ) + }) + .wrap_err("reading enum version field value")?; + let lfev = qlfev + .iter() + .find(|x| x.id == enum_id) + .ok_or_else(|| { + did_not_exist_error( field_name, "enum_value", - ))?; - LoaderFieldEnumValue { - id: lfev.id, - enum_id: lfev.enum_id, - value: lfev.value.clone(), - ordering: lfev.ordering, - created: lfev.created, - ty: lfev.ty.clone(), - major: lfev.major, - } - }), - )) - }) - .collect::, - DatabaseError, - >>()? - } + ) + }) + .wrap_err("finding loader field enum value")?; + LoaderFieldEnumValue { + id: lfev.id, + enum_id: lfev.enum_id, + value: lfev.value.clone(), + ordering: lfev.ordering, + created: lfev.created, + ty: lfev.ty.clone(), + major: lfev.major, + } + }), + )) + }) + .collect::>>() + .wrap_err("building enum version field values")?, // Array fields // We concatenate into one array @@ -1208,12 +1292,12 @@ impl VersionFieldValue { VersionFieldValue::ArrayInteger( qvfs.into_iter() .map(|qvf| { - qvf.int_value.ok_or(did_not_exist_error( - field_name, - "int_value", - )) + qvf.int_value.ok_or_else(|| { + did_not_exist_error(field_name, "int_value") + }) }) - .collect::>()?, + .collect::>() + .wrap_err("building integer array version field value")?, ), )], LoaderFieldType::ArrayText => vec![( @@ -1221,12 +1305,12 @@ impl VersionFieldValue { VersionFieldValue::ArrayText( qvfs.into_iter() .map(|qvf| { - qvf.string_value.ok_or(did_not_exist_error( - field_name, - "string_value", - )) + qvf.string_value.ok_or_else(|| { + did_not_exist_error(field_name, "string_value") + }) }) - .collect::>()?, + .collect::>() + .wrap_err("building text array version field value")?, ), )], LoaderFieldType::ArrayBoolean => vec![( @@ -1234,14 +1318,22 @@ impl VersionFieldValue { VersionFieldValue::ArrayBoolean( qvfs.into_iter() .map(|qvf| { - Ok::( - qvf.int_value.ok_or(did_not_exist_error( - field_name, - "int_value", - ))? != 0, + eyre::Ok( + qvf.int_value + .ok_or_else(|| { + did_not_exist_error( + field_name, + "int_value", + ) + }) + .wrap_err( + "reading boolean array version field value", + )? + != 0, ) }) - .collect::>()?, + .collect::>() + .wrap_err("building boolean array version field value")?, ), )], LoaderFieldType::ArrayEnum(id) => vec![( @@ -1250,17 +1342,24 @@ impl VersionFieldValue { *id, qvfs.into_iter() .map(|qvf| { - let enum_id = qvf.enum_value.ok_or( - did_not_exist_error(field_name, "enum_value"), - )?; + let enum_id = qvf + .enum_value + .ok_or_else(|| { + did_not_exist_error(field_name, "enum_value") + }) + .wrap_err( + "reading enum array version field value", + )?; let lfev = qlfev .iter() .find(|x| x.id == enum_id) - .ok_or(did_not_exist_error( - field_name, - "enum_value", - ))?; - Ok::<_, DatabaseError>(LoaderFieldEnumValue { + .ok_or_else(|| { + did_not_exist_error(field_name, "enum_value") + }) + .wrap_err( + "finding loader field enum array value", + )?; + eyre::Ok(LoaderFieldEnumValue { id: lfev.id, enum_id: lfev.enum_id, value: lfev.value.clone(), @@ -1270,7 +1369,8 @@ impl VersionFieldValue { major: lfev.major, }) }) - .collect::>()?, + .collect::>() + .wrap_err("building enum array version field value")?, ), )], }; diff --git a/apps/labrinth/src/database/models/mod.rs b/apps/labrinth/src/database/models/mod.rs index e647428669..464604ebf3 100644 --- a/apps/labrinth/src/database/models/mod.rs +++ b/apps/labrinth/src/database/models/mod.rs @@ -1,5 +1,3 @@ -use thiserror::Error; - pub mod affiliate_code_item; pub mod analytics_event_item; pub mod blocked_user_item; @@ -65,21 +63,3 @@ pub use version_item::DBVersion; pub use moderation_lock_item::{DBModerationLock, ModerationLockWithUser}; pub use moderation_note_item::DBModerationNote; - -#[derive(Error, Debug)] -pub enum DatabaseError { - #[error(transparent)] - Internal(#[from] eyre::Report), - #[error("Error while interacting with the database: {0}")] - Database(#[from] sqlx::Error), - #[error("Error while trying to generate random ID")] - RandomId, - #[error("Error while interacting with the cache: {0}")] - CacheError(#[from] redis::RedisError), - #[error("Error while serializing with the cache: {0}")] - SerdeCacheError(#[from] serde_json::Error), - #[error("error while encoding or decoding the cache: {0}")] - PostcardCacheError(#[from] postcard::Error), - #[error("Schema error: {0}")] - SchemaError(String), -} diff --git a/apps/labrinth/src/database/models/moderation_note_item.rs b/apps/labrinth/src/database/models/moderation_note_item.rs index b0f14d175e..5ddd580d08 100644 --- a/apps/labrinth/src/database/models/moderation_note_item.rs +++ b/apps/labrinth/src/database/models/moderation_note_item.rs @@ -1,11 +1,12 @@ use std::collections::HashMap; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use xredis::RedisPool; -use super::{DBOrganizationId, DBUserId, DatabaseError}; +use super::{DBOrganizationId, DBUserId}; const MODERATION_NOTES_USERS_NAMESPACE: &str = "moderation_notes_users:v4"; const MODERATION_NOTES_ORGANIZATIONS_NAMESPACE: &str = @@ -28,19 +29,24 @@ impl DBModerationNote { user_ids: &[DBUserId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { let cached = { - let mut redis = redis.connect().await?; + let mut redis = redis.connect().await.wrap_err( + "connecting to redis to fetch user moderation notes", + )?; let keys = user_ids .iter() .map(|id| { redis.key().entity(MODERATION_NOTES_USERS_NAMESPACE, id.0) }) .collect::>(); - redis.get_many_deserialized::(&keys).await? + redis + .get_many_deserialized::(&keys) + .await + .wrap_err("fetching cached user moderation notes")? }; let mut notes = HashMap::new(); @@ -66,9 +72,13 @@ impl DBModerationNote { &missing_ids, ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching user moderation notes")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache user moderation notes")?; for row in rows { let note = Self { user_id: row.user_id.map(DBUserId), @@ -85,7 +95,10 @@ impl DBModerationNote { let key = redis .key() .entity(MODERATION_NOTES_USERS_NAMESPACE, user_id.0); - redis.set_serialized(&key, ¬e, None).await?; + redis + .set_serialized(&key, ¬e, None) + .await + .wrap_err("caching user moderation note")?; notes.insert(user_id, note); } } @@ -97,12 +110,13 @@ impl DBModerationNote { user_id: DBUserId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Ok(Self::get_many_users(&[user_id], exec, redis) - .await? + .await + .wrap_err("fetching user moderation note")? .remove(&user_id)) } @@ -110,12 +124,14 @@ impl DBModerationNote { organization_ids: &[DBOrganizationId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { let cached = { - let mut redis = redis.connect().await?; + let mut redis = redis.connect().await.wrap_err( + "connecting to redis to fetch organization moderation notes", + )?; let keys = organization_ids .iter() .map(|id| { @@ -124,7 +140,10 @@ impl DBModerationNote { .entity(MODERATION_NOTES_ORGANIZATIONS_NAMESPACE, id.0) }) .collect::>(); - redis.get_many_deserialized::(&keys).await? + redis + .get_many_deserialized::(&keys) + .await + .wrap_err("fetching cached organization moderation notes")? }; let mut notes = HashMap::new(); @@ -150,9 +169,12 @@ impl DBModerationNote { &missing_ids, ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching organization moderation notes")?; - let mut redis = redis.connect().await?; + let mut redis = redis.connect().await.wrap_err( + "connecting to redis to cache organization moderation notes", + )?; for row in rows { let note = Self { user_id: row.user_id.map(DBUserId), @@ -170,7 +192,10 @@ impl DBModerationNote { MODERATION_NOTES_ORGANIZATIONS_NAMESPACE, organization_id.0, ); - redis.set_serialized(&key, ¬e, None).await?; + redis + .set_serialized(&key, ¬e, None) + .await + .wrap_err("caching organization moderation note")?; notes.insert(organization_id, note); } } @@ -182,13 +207,14 @@ impl DBModerationNote { organization_id: DBOrganizationId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Ok( Self::get_many_organizations(&[organization_id], exec, redis) - .await? + .await + .wrap_err("fetching organization moderation note")? .remove(&organization_id), ) } @@ -200,7 +226,7 @@ impl DBModerationNote { notes: Option<&str>, user_rating: Option, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -225,7 +251,8 @@ impl DBModerationNote { user_rating, ) .fetch_optional(exec) - .await?; + .await + .wrap_err("inserting moderation note")?; Ok(result) } @@ -238,7 +265,7 @@ impl DBModerationNote { notes: Option<&str>, user_rating: Option, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -266,7 +293,8 @@ impl DBModerationNote { expected_current_version ) .fetch_optional(exec) - .await?; + .await + .wrap_err("updating moderation note")?; Ok(result) } @@ -274,23 +302,33 @@ impl DBModerationNote { pub async fn clear_user_cache( user_id: DBUserId, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis.connect().await.wrap_err( + "connecting to redis to clear user moderation note cache", + )?; let key = redis .key() .entity(MODERATION_NOTES_USERS_NAMESPACE, user_id.0); - redis.delete(&key).await.map_err(Into::into) + redis + .delete(&key) + .await + .wrap_err("clearing user moderation note cache") } pub async fn clear_organization_cache( organization_id: DBOrganizationId, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis.connect().await.wrap_err( + "connecting to redis to clear organization moderation note cache", + )?; let key = redis.key().entity( MODERATION_NOTES_ORGANIZATIONS_NAMESPACE, organization_id.0, ); - redis.delete(&key).await.map_err(Into::into) + redis + .delete(&key) + .await + .wrap_err("clearing organization moderation note cache") } } diff --git a/apps/labrinth/src/database/models/notification_item.rs b/apps/labrinth/src/database/models/notification_item.rs index 6421a6d0bf..6826d7c16a 100644 --- a/apps/labrinth/src/database/models/notification_item.rs +++ b/apps/labrinth/src/database/models/notification_item.rs @@ -1,11 +1,11 @@ use super::ids::*; use crate::database::PgTransaction; -use crate::database::models::DatabaseError; use crate::models::notifications::{ NotificationBody, NotificationChannel, NotificationDeliveryStatus, NotificationType, }; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use serde::{Deserialize, Serialize}; use xredis::RedisPool; @@ -40,8 +40,10 @@ impl NotificationBuilder { user: DBUserId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - self.insert_many(vec![user], transaction, redis).await?; + ) -> Result<()> { + self.insert_many(vec![user], transaction, redis) + .await + .wrap_err("inserting notification")?; Ok(()) } @@ -50,10 +52,13 @@ impl NotificationBuilder { dates_available: Vec>, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let notification_ids = generate_many_notification_ids(users.len(), &mut *transaction) - .await?; + .await + .wrap_err( + "generating notification ids for payout notifications", + )?; let users_raw_ids = users.iter().map(|x| x.0).collect::>(); let notification_ids = @@ -98,7 +103,8 @@ impl NotificationBuilder { &dates_available[..], ) .fetch_all(&mut *transaction) - .await?; + .await + .wrap_err("inserting payout notifications")?; if inserted_rows.is_empty() { return Ok(()); @@ -126,7 +132,8 @@ impl NotificationBuilder { ¬ification_types, &inserted_users, ) - .await?; + .await + .wrap_err("inserting payout notification deliveries")?; Ok(()) } @@ -135,12 +142,14 @@ impl NotificationBuilder { &self, users: &[DBUserId], transaction: &mut PgTransaction<'_>, - ) -> Result, DatabaseError> { + ) -> Result> { let notification_ids = generate_many_notification_ids(users.len(), &mut *transaction) - .await?; + .await + .wrap_err("generating notification ids")?; - let body = serde_json::value::to_value(&self.body)?; + let body = serde_json::value::to_value(&self.body) + .wrap_err("serializing notification body")?; let bodies = notification_ids .iter() .map(|_| body.clone()) @@ -162,7 +171,8 @@ impl NotificationBuilder { &bodies[..], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting notification records")?; Ok(notification_ids) } @@ -172,9 +182,11 @@ impl NotificationBuilder { users: Vec, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { - let notification_ids = - self.insert_many_records(&users, transaction).await?; + ) -> Result> { + let notification_ids = self + .insert_many_records(&users, transaction) + .await + .wrap_err("inserting notification records")?; let users_raw_ids = users.iter().map(|x| x.0).collect::>(); let notification_ids_raw = @@ -193,7 +205,8 @@ impl NotificationBuilder { ¬ification_types, &users, ) - .await?; + .await + .wrap_err("inserting notification deliveries")?; Ok(notification_ids) } @@ -205,10 +218,14 @@ impl NotificationBuilder { users: Vec, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { - let notification_ids = - self.insert_many_records(&users, transaction).await?; - DBNotification::clear_user_notifications_cache(&users, redis).await?; + ) -> Result> { + let notification_ids = self + .insert_many_records(&users, transaction) + .await + .wrap_err("inserting notification records without delivery")?; + DBNotification::clear_user_notifications_cache(&users, redis) + .await + .wrap_err("clearing notification caches")?; Ok(notification_ids) } @@ -219,7 +236,7 @@ impl NotificationBuilder { users_raw_ids: &[i64], notification_types: &[&str], users: &[DBUserId], - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let notification_channels = NotificationChannel::list() .iter() .map(|x| x.as_str()) @@ -297,9 +314,16 @@ impl NotificationBuilder { NotificationDeliveryStatus::SkippedDefault.as_str(), ); - query.execute(&mut *transaction).await?; + query + .execute(&mut *transaction) + .await + .wrap_err("inserting notification deliveries")?; - DBNotification::clear_user_notifications_cache(users, redis).await?; + DBNotification::clear_user_notifications_cache(users, redis) + .await + .wrap_err( + "clearing notification caches after inserting deliveries", + )?; Ok(()) } @@ -374,7 +398,7 @@ impl DBNotification { pub async fn get_all_user<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { @@ -418,7 +442,8 @@ impl DBNotification { } }) .try_collect::>() - .await?; + .await + .wrap_err("fetching all user notifications")?; Ok(db_notifications) } @@ -428,17 +453,22 @@ impl DBNotification { user_id: DBUserId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for user notifications")?; let key = redis.key().entity(USER_NOTIFICATIONS_NAMESPACE, user_id.0); - let cached_notifications: Option> = - redis.get_deserialized(&key).await?; + let cached_notifications: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached user notifications")?; if let Some(notifications) = cached_notifications { return Ok(notifications); @@ -487,12 +517,19 @@ impl DBNotification { } }) .try_collect::>() - .await?; + .await + .wrap_err("fetching site-exposed user notifications")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache user notifications")?; let key = redis.key().entity(USER_NOTIFICATIONS_NAMESPACE, user_id.0); - redis.set_serialized(&key, &db_notifications, None).await?; + redis + .set_serialized(&key, &db_notifications, None) + .await + .wrap_err("caching site-exposed user notifications")?; Ok(db_notifications) } @@ -501,15 +538,17 @@ impl DBNotification { id: DBNotificationId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { - Self::read_many(&[id], transaction, redis).await + ) -> Result> { + Self::read_many(&[id], transaction, redis) + .await + .wrap_err("marking notification as read") } pub async fn read_many( notification_ids: &[DBNotificationId], transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { + ) -> Result> { let notification_ids_parsed: Vec = notification_ids.iter().map(|x| x.0).collect(); @@ -525,13 +564,17 @@ impl DBNotification { .fetch(&mut *transaction) .map_ok(|x| DBUserId(x.user_id)) .try_collect::>() - .await?; + .await + .wrap_err("marking notifications as read")?; DBNotification::clear_user_notifications_cache( affected_users.iter(), redis, ) - .await?; + .await + .wrap_err( + "clearing notification caches after marking notifications as read", + )?; Ok(Some(())) } @@ -540,15 +583,17 @@ impl DBNotification { id: DBNotificationId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { - Self::remove_many(&[id], transaction, redis).await + ) -> Result> { + Self::remove_many(&[id], transaction, redis) + .await + .wrap_err("removing notification") } pub async fn remove_many( notification_ids: &[DBNotificationId], transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, DatabaseError> { + ) -> Result> { let notification_ids_parsed: Vec = notification_ids.iter().map(|x| x.0).collect(); @@ -560,7 +605,8 @@ impl DBNotification { ¬ification_ids_parsed ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("removing notification deliveries")?; sqlx::query!( " @@ -570,7 +616,8 @@ impl DBNotification { ¬ification_ids_parsed ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("removing notification actions")?; let affected_users = sqlx::query!( " @@ -583,13 +630,17 @@ impl DBNotification { .fetch(&mut *transaction) .map_ok(|x| DBUserId(x.user_id)) .try_collect::>() - .await?; + .await + .wrap_err("removing notifications")?; DBNotification::clear_user_notifications_cache( affected_users.iter(), redis, ) - .await?; + .await + .wrap_err( + "clearing notification caches after removing notifications", + )?; Ok(Some(())) } @@ -599,7 +650,7 @@ impl DBNotification { users: &[DBUserId], transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result { + ) -> Result { let user_ids = users.iter().map(|x| x.0).collect::>(); let ids = sqlx::query!( @@ -615,13 +666,16 @@ impl DBNotification { .fetch(&mut *transaction) .map_ok(|x| DBNotificationId(x.id)) .try_collect::>() - .await?; + .await + .wrap_err("fetching notifications matching body")?; if ids.is_empty() { return Ok(0); } - Self::remove_many(&ids, transaction, redis).await?; + Self::remove_many(&ids, transaction, redis) + .await + .wrap_err("removing notifications matching body")?; Ok(ids.len()) } @@ -629,14 +683,20 @@ impl DBNotification { pub async fn clear_user_notifications_cache( user_ids: impl IntoIterator, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear notification caches")?; let keys = user_ids .into_iter() .map(|id| redis.key().entity(USER_NOTIFICATIONS_NAMESPACE, id.0)) .collect::>(); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing notification caches")?; Ok(()) } diff --git a/apps/labrinth/src/database/models/notifications_deliveries_item.rs b/apps/labrinth/src/database/models/notifications_deliveries_item.rs index 616f70736a..d463d417f2 100644 --- a/apps/labrinth/src/database/models/notifications_deliveries_item.rs +++ b/apps/labrinth/src/database/models/notifications_deliveries_item.rs @@ -1,9 +1,9 @@ use super::ids::*; -use crate::database::models::DatabaseError; use crate::models::v3::notifications::{ NotificationChannel, NotificationDeliveryStatus, }; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; pub struct DBNotificationDelivery { pub id: i64, @@ -61,14 +61,15 @@ impl DBNotificationDelivery { pub async fn get_all_user( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let user_id = user_id.0; let results = select_notification_deliveries_with_predicate!( "WHERE user_id = $1", user_id ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching notification deliveries for user")?; Ok(results.into_iter().map(|r| r.into()).collect()) } @@ -79,7 +80,7 @@ impl DBNotificationDelivery { channel: NotificationChannel, limit: i64, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { // This follows the `idx_notifications_deliveries_composite_queue` index. Ok(select_notification_deliveries_with_predicate!( "WHERE @@ -98,7 +99,8 @@ impl DBNotificationDelivery { NotificationDeliveryStatus::Pending.as_str() ) .fetch_all(exec) - .await? + .await + .wrap_err("locking processable notification deliveries")? .into_iter() .map(Into::into) .collect()) @@ -108,7 +110,7 @@ impl DBNotificationDelivery { pub async fn insert( &mut self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let id = sqlx::query_scalar!( " INSERT INTO notifications_deliveries ( @@ -126,7 +128,8 @@ impl DBNotificationDelivery { self.attempt_count, ) .fetch_one(exec) - .await?; + .await + .wrap_err("inserting notification delivery")?; self.id = id; @@ -137,7 +140,7 @@ impl DBNotificationDelivery { pub async fn update( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " UPDATE notifications_deliveries @@ -155,7 +158,8 @@ impl DBNotificationDelivery { self.attempt_count, ) .execute(exec) - .await?; + .await + .wrap_err("updating notification delivery")?; Ok(()) } diff --git a/apps/labrinth/src/database/models/notifications_template_item.rs b/apps/labrinth/src/database/models/notifications_template_item.rs index 3c9ef97f70..f87df0bfd3 100644 --- a/apps/labrinth/src/database/models/notifications_template_item.rs +++ b/apps/labrinth/src/database/models/notifications_template_item.rs @@ -1,8 +1,8 @@ -use crate::database::models::DatabaseError; use crate::models::v3::notifications::{NotificationChannel, NotificationType}; use crate::routes::ApiError; use crate::util::error::ApiContext as _; use crate::util::error::Context as _; +use eyre::Result; use serde::{Deserialize, Serialize}; use xredis::RedisPool; @@ -54,13 +54,19 @@ impl NotificationTemplate { channel: NotificationChannel, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, redis: &RedisPool, - ) -> Result, DatabaseError> { + ) -> Result> { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to Redis for notification templates")?; let key = redis.key().metadata(TEMPLATES_NAMESPACE, channel.as_str()); - let maybe_cached_templates = redis.get_deserialized(&key).await?; + let maybe_cached_templates = redis + .get_deserialized(&key) + .await + .wrap_err("fetching notification templates from cache")?; if let Some(cached) = maybe_cached_templates { return Ok(cached); @@ -75,16 +81,21 @@ impl NotificationTemplate { channel.as_str(), ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching notification templates")?; let templates = results.into_iter().map(Into::into).collect(); - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to Redis to cache notification templates")?; let key = redis.key().metadata(TEMPLATES_NAMESPACE, channel.as_str()); redis .set_serialized(&key, &templates, Some(TEMPLATES_CACHE_EXPIRY)) - .await?; + .await + .wrap_err("caching notification templates")?; Ok(templates) } @@ -92,23 +103,30 @@ impl NotificationTemplate { pub async fn get_cached_html_data( &self, redis: &RedisPool, - ) -> Result, DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result> { + let mut redis = redis.connect().await.wrap_err( + "connecting to Redis for cached notification template HTML", + )?; let key = redis.key().metadata(TEMPLATES_HTML_DATA_NAMESPACE, self.id); - redis.get_deserialized(&key).await.map_err(Into::into) + redis + .get_deserialized(&key) + .await + .wrap_err("fetching cached notification template HTML") } pub async fn set_cached_html_data( &self, data: String, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis.connect().await.wrap_err( + "connecting to Redis to cache notification template HTML", + )?; let key = redis.key().metadata(TEMPLATES_HTML_DATA_NAMESPACE, self.id); redis .set_serialized(&key, &data, Some(HTML_DATA_CACHE_EXPIRY)) .await - .map_err(Into::into) + .wrap_err("caching notification template HTML") } } @@ -116,9 +134,9 @@ pub async fn get_or_set_cached_dynamic_html( redis: &RedisPool, key: &str, get: impl FnOnce() -> F, -) -> Result +) -> std::result::Result where - F: Future>, + F: Future>, { #[derive(Debug, Clone, Serialize, Deserialize)] struct HtmlBody { diff --git a/apps/labrinth/src/database/models/notifications_type_item.rs b/apps/labrinth/src/database/models/notifications_type_item.rs index c2d036a3f7..fb513d819e 100644 --- a/apps/labrinth/src/database/models/notifications_type_item.rs +++ b/apps/labrinth/src/database/models/notifications_type_item.rs @@ -1,5 +1,5 @@ -use crate::database::models::DatabaseError; use crate::models::v3::notifications::NotificationType; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use xredis::RedisPool; @@ -35,15 +35,21 @@ impl NotificationTypeItem { pub async fn list<'a, E>( exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to Redis for notification types")?; let key = redis.key().metadata(NOTIFICATION_TYPES_NAMESPACE, "all"); - let cached_types = redis.get_deserialized(&key).await?; + let cached_types = redis + .get_deserialized(&key) + .await + .wrap_err("fetching notification types from cache")?; if let Some(types) = cached_types { return Ok(types); @@ -55,14 +61,21 @@ impl NotificationTypeItem { "SELECT * FROM notifications_types" ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching notification types")?; let types = results.into_iter().map(Into::into).collect(); - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to Redis to cache notification types")?; let key = redis.key().metadata(NOTIFICATION_TYPES_NAMESPACE, "all"); - redis.set_serialized(&key, &types, None).await?; + redis + .set_serialized(&key, &types, None) + .await + .wrap_err("caching notification types")?; Ok(types) } diff --git a/apps/labrinth/src/database/models/oauth_client_authorization_item.rs b/apps/labrinth/src/database/models/oauth_client_authorization_item.rs index 64a4bf9896..de692a3541 100644 --- a/apps/labrinth/src/database/models/oauth_client_authorization_item.rs +++ b/apps/labrinth/src/database/models/oauth_client_authorization_item.rs @@ -1,12 +1,11 @@ use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use crate::{database::PgTransaction, models::pats::Scopes}; -use super::{ - DBOAuthClientAuthorizationId, DBOAuthClientId, DBUserId, DatabaseError, -}; +use super::{DBOAuthClientAuthorizationId, DBOAuthClientId, DBUserId}; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct DBOAuthClientAuthorization { @@ -42,7 +41,7 @@ impl DBOAuthClientAuthorization { client_id: DBOAuthClientId, user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let value = sqlx::query_as!( DBAuthClientAuthorizationQueryResult, " @@ -54,7 +53,8 @@ impl DBOAuthClientAuthorization { user_id.0, ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching oauth client authorization")?; Ok(value.map(|r| r.into())) } @@ -62,7 +62,7 @@ impl DBOAuthClientAuthorization { pub async fn get_all_for_user( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let results = sqlx::query_as!( DBAuthClientAuthorizationQueryResult, " @@ -73,7 +73,8 @@ impl DBOAuthClientAuthorization { user_id.0 ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching oauth client authorizations for user")?; Ok(results.into_iter().map(|r| r.into()).collect_vec()) } @@ -84,7 +85,7 @@ impl DBOAuthClientAuthorization { user_id: DBUserId, scopes: Scopes, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO oauth_client_authorizations ( @@ -102,7 +103,8 @@ impl DBOAuthClientAuthorization { scopes.bits() as i64, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("upserting oauth client authorization")?; Ok(()) } @@ -111,7 +113,7 @@ impl DBOAuthClientAuthorization { client_id: DBOAuthClientId, user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " DELETE FROM oauth_client_authorizations @@ -121,7 +123,8 @@ impl DBOAuthClientAuthorization { user_id.0 ) .execute(exec) - .await?; + .await + .wrap_err("removing oauth client authorization")?; Ok(()) } diff --git a/apps/labrinth/src/database/models/oauth_client_item.rs b/apps/labrinth/src/database/models/oauth_client_item.rs index a677a254ea..47d2589454 100644 --- a/apps/labrinth/src/database/models/oauth_client_item.rs +++ b/apps/labrinth/src/database/models/oauth_client_item.rs @@ -1,9 +1,10 @@ use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use sha2::Digest; -use super::{DBOAuthClientId, DBOAuthRedirectUriId, DBUserId, DatabaseError}; +use super::{DBOAuthClientId, DBOAuthRedirectUriId, DBUserId}; use crate::{database::PgTransaction, models::pats::Scopes}; #[derive(Deserialize, Serialize, Clone, Debug)] @@ -81,14 +82,18 @@ impl DBOAuthClient { pub async fn get( id: DBOAuthClientId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - Ok(Self::get_many(&[id], exec).await?.into_iter().next()) + ) -> Result> { + Ok(Self::get_many(&[id], exec) + .await + .wrap_err("fetching oauth client")? + .into_iter() + .next()) } pub async fn get_many( ids: &[DBOAuthClientId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let ids = ids.iter().map(|id| id.0).collect_vec(); let ids_ref: &[i64] = &ids; let results = select_clients_with_predicate!( @@ -96,7 +101,8 @@ impl DBOAuthClient { ids_ref ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching oauth clients")?; Ok(results.into_iter().map(|r| r.into()).collect_vec()) } @@ -104,14 +110,15 @@ impl DBOAuthClient { pub async fn get_all_user_clients( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let user_id_param = user_id.0; let clients = select_clients_with_predicate!( "WHERE created_by = $1", user_id_param ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching oauth clients for user")?; Ok(clients.into_iter().map(|r| r.into()).collect()) } @@ -119,7 +126,7 @@ impl DBOAuthClient { pub async fn remove( id: DBOAuthClientId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { // Cascades to oauth_client_redirect_uris, oauth_client_authorizations sqlx::query!( " @@ -129,7 +136,8 @@ impl DBOAuthClient { id.0 ) .execute(exec) - .await?; + .await + .wrap_err("removing oauth client")?; Ok(()) } @@ -137,7 +145,7 @@ impl DBOAuthClient { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO oauth_clients ( @@ -156,10 +164,12 @@ impl DBOAuthClient { self.created_by.0 ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting oauth client")?; Self::insert_redirect_uris(&self.redirect_uris, &mut *transaction) - .await?; + .await + .wrap_err("inserting oauth client redirect uris")?; Ok(()) } @@ -167,7 +177,7 @@ impl DBOAuthClient { pub async fn update_editable_fields( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " UPDATE oauth_clients @@ -183,7 +193,8 @@ impl DBOAuthClient { self.id.0, ) .execute(exec) - .await?; + .await + .wrap_err("updating oauth client")?; Ok(()) } @@ -191,7 +202,7 @@ impl DBOAuthClient { pub async fn remove_redirect_uris( ids: impl IntoIterator, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let ids = ids.into_iter().map(|id| id.0).collect_vec(); sqlx::query!( " @@ -202,7 +213,8 @@ impl DBOAuthClient { &ids[..] ) .execute(exec) - .await?; + .await + .wrap_err("removing oauth client redirect uris")?; Ok(()) } @@ -210,7 +222,7 @@ impl DBOAuthClient { pub async fn insert_redirect_uris( uris: &[DBOAuthRedirectUri], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let (ids, client_ids, uris): (Vec<_>, Vec<_>, Vec<_>) = uris .iter() .map(|r| (r.id.0, r.client_id.0, r.uri.clone())) @@ -225,7 +237,8 @@ impl DBOAuthClient { &uris[..], ) .execute(exec) - .await?; + .await + .wrap_err("inserting oauth client redirect uris")?; Ok(()) } diff --git a/apps/labrinth/src/database/models/oauth_token_item.rs b/apps/labrinth/src/database/models/oauth_token_item.rs index b4191bff09..dc2a29407e 100644 --- a/apps/labrinth/src/database/models/oauth_token_item.rs +++ b/apps/labrinth/src/database/models/oauth_token_item.rs @@ -1,9 +1,10 @@ use super::{ DBOAuthAccessTokenId, DBOAuthClientAuthorizationId, DBOAuthClientId, - DBUserId, DatabaseError, + DBUserId, }; use crate::models::pats::Scopes; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use sha2::Digest; @@ -26,7 +27,7 @@ impl DBOAuthAccessToken { pub async fn get( token_hash: String, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let value = sqlx::query!( " SELECT @@ -47,7 +48,8 @@ impl DBOAuthAccessToken { token_hash ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching oauth access token")?; Ok(value.map(|r| DBOAuthAccessToken { id: DBOAuthAccessTokenId(r.id), @@ -66,7 +68,7 @@ impl DBOAuthAccessToken { pub async fn insert( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result { + ) -> Result { let r = sqlx::query!( " INSERT INTO oauth_access_tokens ( @@ -84,7 +86,8 @@ impl DBOAuthAccessToken { Option::>::None ) .fetch_one(exec) - .await?; + .await + .wrap_err("inserting oauth access token")?; let (created, expires) = (r.created, r.expires); let time_until_expiration = expires - created; diff --git a/apps/labrinth/src/database/models/organization_item.rs b/apps/labrinth/src/database/models/organization_item.rs index 72329f6eb1..b538db65c0 100644 --- a/apps/labrinth/src/database/models/organization_item.rs +++ b/apps/labrinth/src/database/models/organization_item.rs @@ -1,6 +1,7 @@ use crate::database::PgTransaction; use ariadne::ids::base62_impl::parse_base62; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use std::fmt::{Debug, Display}; use std::hash::Hash; @@ -40,7 +41,7 @@ impl DBOrganization { pub async fn insert( self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), super::DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO organizations (id, slug, name, team_id, description, icon_url, raw_icon_url, color) @@ -56,7 +57,8 @@ impl DBOrganization { self.color.map(|x| x as i32), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting organization")?; Ok(()) } @@ -65,12 +67,13 @@ impl DBOrganization { string: &str, exec: E, redis: &RedisPool, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Self::get_many(&[string], exec, redis) .await + .wrap_err("fetching organization") .map(|x| x.into_iter().next()) } @@ -78,12 +81,13 @@ impl DBOrganization { id: DBOrganizationId, exec: E, redis: &RedisPool, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Self::get_many_ids(&[id], exec, redis) .await + .wrap_err("fetching organization by id") .map(|x| x.into_iter().next()) } @@ -91,7 +95,7 @@ impl DBOrganization { organization_ids: &[DBOrganizationId], exec: E, redis: &RedisPool, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -99,7 +103,9 @@ impl DBOrganization { .iter() .map(|x| crate::models::ids::OrganizationId::from(*x)) .collect::>(); - Self::get_many(&ids, exec, redis).await + Self::get_many(&ids, exec, redis) + .await + .wrap_err("fetching organizations by id") } pub async fn get_many< @@ -110,7 +116,7 @@ impl DBOrganization { organization_strings: &[T], exec: E, redis: &RedisPool, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -131,7 +137,7 @@ impl DBOrganization { .map(|x| x.to_string().to_lowercase()) .collect::>(); - let organizations = sqlx::query!( + sqlx::query!( " SELECT o.id, o.slug, o.name, o.team_id, o.description, o.icon_url, o.raw_icon_url, o.color FROM organizations o @@ -155,16 +161,13 @@ impl DBOrganization { }; acc.insert(m.id, (Some(m.slug), org)); - async move { Ok(acc) } + async move { Ok::<_, sqlx::Error>(acc) } }) - .await?; - - Ok::<_, crate::database::models::DatabaseError>( - organizations, - ) + .await }, ) - .await?; + .await + .wrap_err("fetching cached organizations")?; Ok(val) } @@ -173,7 +176,7 @@ impl DBOrganization { pub async fn get_associated_organization_project_id<'a, 'b, E>( project_id: DBProjectId, exec: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -188,7 +191,8 @@ impl DBOrganization { project_id as DBProjectId, ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching organization associated with project")?; if let Some(result) = result { Ok(Some(DBOrganization { @@ -209,7 +213,7 @@ impl DBOrganization { pub async fn get_projects<'a, E>( organization_id: DBOrganizationId, exec: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -226,7 +230,8 @@ impl DBOrganization { .fetch(exec) .map_ok(|m| DBProjectId(m.id)) .try_collect::>() - .await?; + .await + .wrap_err("fetching organization projects")?; Ok(db_projects) } @@ -235,8 +240,10 @@ impl DBOrganization { id: DBOrganizationId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, super::DatabaseError> { - let organization = Self::get_id(id, &mut *transaction, redis).await?; + ) -> Result> { + let organization = Self::get_id(id, &mut *transaction, redis) + .await + .wrap_err("fetching organization to remove")?; if let Some(organization) = organization { sqlx::query!( @@ -247,9 +254,12 @@ impl DBOrganization { id as DBOrganizationId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting organization")?; - DBTeamMember::clear_cache(organization.team_id, redis).await?; + DBTeamMember::clear_cache(organization.team_id, redis) + .await + .wrap_err("clearing removed organization team cache")?; sqlx::query!( " @@ -259,7 +269,8 @@ impl DBOrganization { organization.team_id as DBTeamId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting organization team members")?; sqlx::query!( " @@ -269,7 +280,8 @@ impl DBOrganization { organization.team_id as DBTeamId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting organization team")?; Ok(Some(())) } else { @@ -281,8 +293,11 @@ impl DBOrganization { id: DBOrganizationId, slug: Option, redis: &RedisPool, - ) -> Result<(), super::DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear organization cache")?; let mut keys = vec![redis.key().entity(ORGANIZATIONS_NAMESPACE, id.0)]; if let Some(slug) = slug { keys.push( @@ -293,7 +308,10 @@ impl DBOrganization { ); } - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing organization cache")?; Ok(()) } } diff --git a/apps/labrinth/src/database/models/passkey_item.rs b/apps/labrinth/src/database/models/passkey_item.rs index 063e0724f4..48650696ed 100644 --- a/apps/labrinth/src/database/models/passkey_item.rs +++ b/apps/labrinth/src/database/models/passkey_item.rs @@ -1,7 +1,7 @@ use super::ids::*; use crate::database::PgTransaction; -use crate::database::models::DatabaseError; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use serde::{Deserialize, Serialize}; use sqlx::types::Json; @@ -22,7 +22,7 @@ impl DBPasskey { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO user_passkeys ( @@ -41,7 +41,8 @@ impl DBPasskey { self.last_used, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting passkey")?; Ok(()) } @@ -49,7 +50,7 @@ impl DBPasskey { pub async fn get_by_credential_id<'a, E>( credential_id: &[u8], exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -64,7 +65,8 @@ impl DBPasskey { credential_id, ) .fetch_optional(exec) - .await? + .await + .wrap_err("fetching passkey by credential id")? .map(|x| DBPasskey { id: DBPasskeyId(x.id), user_id: DBUserId(x.user_id), @@ -81,7 +83,7 @@ impl DBPasskey { pub async fn get_for_user<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -107,7 +109,8 @@ impl DBPasskey { last_used: x.last_used, }) .try_collect::>() - .await?; + .await + .wrap_err("fetching passkeys for user")?; Ok(passkeys) } @@ -117,7 +120,7 @@ impl DBPasskey { user_id: DBUserId, name: &str, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { let result = sqlx::query!( " UPDATE user_passkeys SET name = $1 @@ -128,7 +131,8 @@ impl DBPasskey { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("renaming passkey")?; Ok(result.rows_affected() > 0) } @@ -137,7 +141,7 @@ impl DBPasskey { id: DBPasskeyId, passkey: Passkey, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { let result = sqlx::query!( " UPDATE user_passkeys @@ -148,7 +152,8 @@ impl DBPasskey { id as DBPasskeyId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating passkey after authentication")?; Ok(result.rows_affected() > 0) } @@ -156,7 +161,7 @@ impl DBPasskey { pub async fn remove( id: DBPasskeyId, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { let result = sqlx::query!( " DELETE FROM user_passkeys @@ -165,7 +170,8 @@ impl DBPasskey { id as DBPasskeyId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("removing passkey")?; Ok(result.rows_affected() > 0) } @@ -174,7 +180,7 @@ impl DBPasskey { id: DBPasskeyId, user_id: DBUserId, transaction: &mut PgTransaction<'_>, - ) -> Result { + ) -> Result { let result = sqlx::query!( " DELETE FROM user_passkeys @@ -184,7 +190,8 @@ impl DBPasskey { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("removing passkey for user")?; Ok(result.rows_affected() > 0) } diff --git a/apps/labrinth/src/database/models/pat_item.rs b/apps/labrinth/src/database/models/pat_item.rs index deb6f4a0b2..d2151afc63 100644 --- a/apps/labrinth/src/database/models/pat_item.rs +++ b/apps/labrinth/src/database/models/pat_item.rs @@ -1,10 +1,11 @@ use super::ids::*; use crate::database::PgTransaction; -use crate::database::models::DatabaseError; + use crate::models::pats::Scopes; use ariadne::ids::base62_impl::parse_base62; use chrono::{DateTime, Utc}; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Display}; @@ -31,7 +32,7 @@ impl DBPersonalAccessToken { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO pats ( @@ -51,7 +52,8 @@ impl DBPersonalAccessToken { self.expires ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting personal access token")?; Ok(()) } @@ -64,12 +66,13 @@ impl DBPersonalAccessToken { id: T, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Self::get_many(&[id], exec, redis) .await + .wrap_err("fetching personal access token") .map(|x| x.into_iter().next()) } @@ -77,7 +80,7 @@ impl DBPersonalAccessToken { pat_ids: &[DBPatId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -85,7 +88,9 @@ impl DBPersonalAccessToken { .iter() .map(|x| crate::models::ids::PatId::from(*x)) .collect::>(); - DBPersonalAccessToken::get_many(&ids, exec, redis).await + DBPersonalAccessToken::get_many(&ids, exec, redis) + .await + .wrap_err("fetching personal access tokens by ID") } pub async fn get_many< @@ -96,7 +101,7 @@ impl DBPersonalAccessToken { pat_strings: &[T], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -125,6 +130,7 @@ impl DBPersonalAccessToken { &slugs, ) .fetch(exec) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc, x| { let pat = DBPersonalAccessToken { id: DBPatId(x.id), @@ -138,13 +144,15 @@ impl DBPersonalAccessToken { }; acc.insert(x.id, (Some(x.access_token), pat)); - async move { Ok(acc) } + async move { eyre::Ok(acc) } }) - .await?; - Ok::<_, DatabaseError>(pats) + .await + .wrap_err("fetching personal access tokens from database")?; + eyre::Ok(pats) }, ) - .await?; + .await + .wrap_err("fetching personal access tokens")?; Ok(val) } @@ -153,15 +161,20 @@ impl DBPersonalAccessToken { user_id: DBUserId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis.connect().await.wrap_err( + "connecting to Redis for user personal access tokens", + )?; let key = redis.key().entity(PATS_USERS_NAMESPACE, user_id.0); - let res = redis.get_deserialized::>(&key).await?; + let res = redis + .get_deserialized::>(&key) + .await + .wrap_err("fetching user personal access tokens from cache")?; if let Some(res) = res { return Ok(res.into_iter().map(DBPatId).collect()); @@ -180,20 +193,28 @@ impl DBPersonalAccessToken { .fetch(exec) .map_ok(|x| DBPatId(x.id)) .try_collect::>() - .await?; + .await + .wrap_err("fetching user personal access tokens from database")?; - let mut redis = redis.connect().await?; + let mut redis = redis.connect().await.wrap_err( + "connecting to Redis to cache user personal access tokens", + )?; let key = redis.key().entity(PATS_USERS_NAMESPACE, user_id.0); - redis.set_serialized(&key, &db_pats, None).await?; + redis + .set_serialized(&key, &db_pats, None) + .await + .wrap_err("caching user personal access tokens")?; Ok(db_pats) } pub async fn clear_cache( clear_pats: Vec<(Option, Option, Option)>, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis.connect().await.wrap_err( + "connecting to Redis to clear personal access token cache", + )?; if clear_pats.is_empty() { return Ok(()); @@ -215,7 +236,10 @@ impl DBPersonalAccessToken { .flatten() }) .collect::>(); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing personal access token cache")?; Ok(()) } @@ -223,7 +247,7 @@ impl DBPersonalAccessToken { pub async fn remove( id: DBPatId, transaction: &mut PgTransaction<'_>, - ) -> Result, sqlx::error::Error> { + ) -> Result> { sqlx::query!( " DELETE FROM pats WHERE id = $1 @@ -231,7 +255,8 @@ impl DBPersonalAccessToken { id as DBPatId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("removing personal access token")?; Ok(Some(())) } diff --git a/apps/labrinth/src/database/models/payout_item.rs b/apps/labrinth/src/database/models/payout_item.rs index df9d5816e2..48541a2dce 100644 --- a/apps/labrinth/src/database/models/payout_item.rs +++ b/apps/labrinth/src/database/models/payout_item.rs @@ -3,10 +3,11 @@ use crate::{ models::payouts::{PayoutMethodType, PayoutStatus}, }; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; -use super::{DBPayoutId, DBUserId, DatabaseError}; +use super::{DBPayoutId, DBUserId}; #[derive(Deserialize, Serialize, Clone, Debug)] pub struct DBPayout { @@ -30,7 +31,7 @@ impl DBPayout { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO payouts ( @@ -51,7 +52,8 @@ impl DBPayout { self.platform_id, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting payout")?; Ok(()) } @@ -59,19 +61,20 @@ impl DBPayout { pub async fn get<'a, 'b, E>( id: DBPayoutId, executor: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { DBPayout::get_many(&[id], executor) .await + .wrap_err("fetching payout") .map(|x| x.into_iter().next()) } pub async fn get_many<'a, E>( payout_ids: &[DBPayoutId], exec: E, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -99,7 +102,8 @@ impl DBPayout { fee: r.fee, }) .try_collect::>() - .await?; + .await + .wrap_err("fetching payouts")?; Ok(results) } @@ -107,7 +111,7 @@ impl DBPayout { pub async fn get_all_for_user( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let results = sqlx::query!( " SELECT id @@ -117,7 +121,8 @@ impl DBPayout { user_id.0 ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching payouts for user")?; Ok(results .into_iter() diff --git a/apps/labrinth/src/database/models/payouts_values_notifications.rs b/apps/labrinth/src/database/models/payouts_values_notifications.rs index 12dbf3f9e8..8c2a84ec65 100644 --- a/apps/labrinth/src/database/models/payouts_values_notifications.rs +++ b/apps/labrinth/src/database/models/payouts_values_notifications.rs @@ -1,5 +1,6 @@ -use crate::database::models::{DBUserId, DatabaseError}; +use crate::database::models::DBUserId; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; pub struct PayoutsValuesNotification { pub id: i32, @@ -11,7 +12,7 @@ impl PayoutsValuesNotification { pub async fn unnotified_users_with_available_payouts_with_limit( exec: impl sqlx::PgExecutor<'_>, limit: i64, - ) -> Result, DatabaseError> { + ) -> Result> { Ok(sqlx::query_as!( QueryResult, " @@ -29,7 +30,8 @@ impl PayoutsValuesNotification { limit, ) .fetch_all(exec) - .await? + .await + .wrap_err("fetching users with unnotified available payouts")? .into_iter() .map(Into::into) .collect()) @@ -38,7 +40,7 @@ impl PayoutsValuesNotification { pub async fn set_notified_many( ids: &[i32], exec: impl sqlx::PgExecutor<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " UPDATE payouts_values_notifications @@ -48,7 +50,8 @@ impl PayoutsValuesNotification { &ids[..], ) .execute(exec) - .await?; + .await + .wrap_err("marking payout value notifications as notified")?; Ok(()) } @@ -57,7 +60,7 @@ impl PayoutsValuesNotification { pub async fn synchronize_future_payout_values( exec: impl sqlx::PgExecutor<'_>, limit: i64, -) -> Result<(), DatabaseError> { +) -> Result<()> { sqlx::query!( " INSERT INTO payouts_values_notifications (date_available, user_id, notified) @@ -70,7 +73,8 @@ pub async fn synchronize_future_payout_values( limit, ) .execute(exec) - .await?; + .await + .wrap_err("synchronizing future payout value notifications")?; Ok(()) } diff --git a/apps/labrinth/src/database/models/product_item.rs b/apps/labrinth/src/database/models/product_item.rs index a1f4cc291e..687b72d9d3 100644 --- a/apps/labrinth/src/database/models/product_item.rs +++ b/apps/labrinth/src/database/models/product_item.rs @@ -1,8 +1,7 @@ -use crate::database::models::{ - DBProductId, DBProductPriceId, DatabaseError, product_item, -}; +use crate::database::models::{DBProductId, DBProductPriceId, product_item}; use crate::models::billing::{Price, ProductMetadata}; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; @@ -42,7 +41,9 @@ macro_rules! select_products_with_predicate { impl TryFrom for DBProduct { type Error = serde_json::Error; - fn try_from(r: ProductQueryResult) -> Result { + fn try_from( + r: ProductQueryResult, + ) -> std::result::Result { Ok(DBProduct { id: DBProductId(r.id), metadata: serde_json::from_value(r.metadata)?, @@ -56,31 +57,34 @@ impl DBProduct { pub async fn get( id: DBProductId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - Ok(Self::get_many(&[id], exec).await?.into_iter().next()) + ) -> Result> { + Ok(Self::get_many(&[id], exec) + .await + .wrap_err("fetching product")? + .into_iter() + .next()) } pub async fn get_price( id: DBProductPriceId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let maybe_row = select_products_with_predicate!( "INNER JOIN products_prices pp ON pp.id = $1 WHERE products.id = pp.product_id", id.0 ) .fetch_optional(exec) - .await?; + .await + .wrap_err("fetching product by price")?; maybe_row - .map(|r| r.try_into().map_err(Into::into)) + .map(TryInto::try_into) .transpose() + .wrap_err("deserializing product metadata") } - pub async fn get_by_type<'a, E>( - exec: E, - r#type: &str, - ) -> Result, DatabaseError> + pub async fn get_by_type<'a, E>(exec: E, r#type: &str) -> Result> where E: sqlx::PgExecutor<'a>, { @@ -89,18 +93,20 @@ impl DBProduct { r#type ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching products by type")?; maybe_row .into_iter() - .map(|r| r.try_into().map_err(Into::into)) - .collect() + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing product metadata") } pub async fn get_many( ids: &[DBProductId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let ids = ids.iter().map(|id| id.0).collect_vec(); let ids_ref: &[i64] = &ids; let results = select_products_with_predicate!( @@ -108,26 +114,30 @@ impl DBProduct { ids_ref ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching products")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing product metadata") } pub async fn get_all( exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let one = 1; let results = select_products_with_predicate!("WHERE 1 = $1", one) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching all products")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing product metadata") } } @@ -146,29 +156,37 @@ impl QueryProductWithPrices { pub async fn list_purchaseable<'a, E>( exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to Redis for purchasable products")?; let key = redis.key().metadata(PRODUCTS_NAMESPACE, "all"); - let res: Option> = - redis.get_deserialized(&key).await?; + let res: Option> = redis + .get_deserialized(&key) + .await + .wrap_err("fetching purchasable products from cache")?; if let Some(res) = res { return Ok(res); } } - let all_products = product_item::DBProduct::get_all(exec).await?; + let all_products = product_item::DBProduct::get_all(exec) + .await + .wrap_err("fetching all products")?; let prices = product_item::DBProductPrice::get_all_public_products_prices( &all_products.iter().map(|x| x.id).collect::>(), exec, ) - .await?; + .await + .wrap_err("fetching public product prices")?; let products = all_products .into_iter() @@ -193,10 +211,16 @@ impl QueryProductWithPrices { }) .collect::>(); - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to Redis to cache purchasable products")?; let key = redis.key().metadata(PRODUCTS_NAMESPACE, "all"); - redis.set_serialized(&key, &products, None).await?; + redis + .set_serialized(&key, &products, None) + .await + .wrap_err("caching purchasable products")?; Ok(products) } @@ -204,16 +228,19 @@ impl QueryProductWithPrices { pub async fn list_by_product_type<'a, E>( exec: E, r#type: &str, - ) -> Result, DatabaseError> + ) -> Result> where E: sqlx::PgExecutor<'a> + Copy, { - let all_products = DBProduct::get_by_type(exec, r#type).await?; + let all_products = DBProduct::get_by_type(exec, r#type) + .await + .wrap_err("fetching products by type")?; let prices = DBProductPrice::get_all_products_prices( &all_products.iter().map(|x| x.id).collect::>(), exec, ) - .await?; + .await + .wrap_err("fetching product prices")?; let products = all_products .into_iter() @@ -278,7 +305,9 @@ macro_rules! select_prices_with_predicate { impl TryFrom for DBProductPrice { type Error = serde_json::Error; - fn try_from(r: ProductPriceQueryResult) -> Result { + fn try_from( + r: ProductPriceQueryResult, + ) -> std::result::Result { Ok(DBProductPrice { id: DBProductPriceId(r.id), product_id: DBProductId(r.product_id), @@ -292,14 +321,18 @@ impl DBProductPrice { pub async fn get( id: DBProductPriceId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - Ok(Self::get_many(&[id], exec).await?.into_iter().next()) + ) -> Result> { + Ok(Self::get_many(&[id], exec) + .await + .wrap_err("fetching product price")? + .into_iter() + .next()) } pub async fn get_many( ids: &[DBProductPriceId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let ids = ids.iter().map(|id| id.0).collect_vec(); let ids_ref: &[i64] = &ids; let results = select_prices_with_predicate!( @@ -307,19 +340,23 @@ impl DBProductPrice { ids_ref ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching product prices")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing product prices") } pub async fn get_all_product_prices( product_id: DBProductId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - let res = Self::get_all_products_prices(&[product_id], exec).await?; + ) -> Result> { + let res = Self::get_all_products_prices(&[product_id], exec) + .await + .wrap_err("fetching product prices")?; Ok(res.remove(&product_id).map(|x| x.1).unwrap_or_default()) } @@ -327,9 +364,10 @@ impl DBProductPrice { pub async fn get_all_public_product_prices( product_id: DBProductId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - let res = - Self::get_all_public_products_prices(&[product_id], exec).await?; + ) -> Result> { + let res = Self::get_all_public_products_prices(&[product_id], exec) + .await + .wrap_err("fetching public product prices")?; Ok(res.remove(&product_id).map(|x| x.1).unwrap_or_default()) } @@ -339,28 +377,30 @@ impl DBProductPrice { pub async fn get_all_public_products_prices( product_ids: &[DBProductId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result>, DatabaseError> { + ) -> Result>> { Self::get_all_products_prices_with_visibility( product_ids, Some(true), exec, ) .await + .wrap_err("fetching public product prices") } pub async fn get_all_products_prices( product_ids: &[DBProductId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result>, DatabaseError> { + ) -> Result>> { Self::get_all_products_prices_with_visibility(product_ids, None, exec) .await + .wrap_err("fetching product prices") } async fn get_all_products_prices_with_visibility( product_ids: &[DBProductId], public_filter: Option, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result>, DatabaseError> { + ) -> Result>> { let ids = product_ids.iter().map(|id| id.0).collect_vec(); let ids_ref: &[i64] = &ids; @@ -374,30 +414,28 @@ impl DBProductPrice { acc.entry(item.product_id).or_default().push(item); } - async move { Ok(acc) } + async move { Ok::<_, sqlx::Error>(acc) } }; let prices = match public_filter { - None => { - select_prices_with_predicate!( - "WHERE product_id = ANY($1::bigint[])", - ids_ref, - ) - .fetch(exec) - .try_fold(DashMap::new(), predicate) - .await? - } + None => select_prices_with_predicate!( + "WHERE product_id = ANY($1::bigint[])", + ids_ref, + ) + .fetch(exec) + .try_fold(DashMap::new(), predicate) + .await + .wrap_err("fetching product prices")?, - Some(public) => { - select_prices_with_predicate!( - "WHERE product_id = ANY($1::bigint[]) AND public = $2", - ids_ref, - public, - ) - .fetch(exec) - .try_fold(DashMap::new(), predicate) - .await? - } + Some(public) => select_prices_with_predicate!( + "WHERE product_id = ANY($1::bigint[]) AND public = $2", + ids_ref, + public, + ) + .fetch(exec) + .try_fold(DashMap::new(), predicate) + .await + .wrap_err("fetching public product prices")?, }; Ok(prices) diff --git a/apps/labrinth/src/database/models/project_disclosure_item.rs b/apps/labrinth/src/database/models/project_disclosure_item.rs index ef8cdf0866..d8df955312 100644 --- a/apps/labrinth/src/database/models/project_disclosure_item.rs +++ b/apps/labrinth/src/database/models/project_disclosure_item.rs @@ -2,10 +2,11 @@ use std::collections::{HashMap, HashSet}; use std::str::FromStr; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use crate::{ - database::models::{DBProjectId, DBUserId, DatabaseError}, + database::models::{DBProjectId, DBUserId}, models::v3::disclosures::{ DisclosureLockStatus, ProjectDisclosure, ProjectDisclosureType, }, @@ -26,13 +27,11 @@ impl DBProjectDisclosure { pub async fn upsert( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { - let (disclosure_type, metadata) = - self.disclosure.to_parts().map_err(|e| { - DatabaseError::Internal(eyre::Report::new(e).wrap_err( - "failed to serialize project disclosure metadata", - )) - })?; + ) -> Result<()> { + let (disclosure_type, metadata) = self + .disclosure + .to_parts() + .wrap_err("serializing project disclosure metadata")?; sqlx::query!( r#" @@ -54,7 +53,8 @@ impl DBProjectDisclosure { <&'static str>::from(self.lock_status), ) .execute(exec) - .await?; + .await + .wrap_err("upserting project disclosure")?; Ok(()) } @@ -63,7 +63,7 @@ impl DBProjectDisclosure { project_id: DBProjectId, include_deleted: bool, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let rows = sqlx::query!( r#" SELECT project_id, type AS "disclosure_type!", metadata, updated_at, updated_by, set_by_moderator, deleted_at, lock_status @@ -75,21 +75,18 @@ impl DBProjectDisclosure { include_deleted, ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching project disclosures")?; rows.into_iter() .map(|row| { - Ok(DBProjectDisclosure { + eyre::Ok(DBProjectDisclosure { project_id: DBProjectId(row.project_id), disclosure: ProjectDisclosure::from_parts( &row.disclosure_type, row.metadata, ) - .map_err(|e| { - DatabaseError::Internal(eyre::Report::new(e).wrap_err( - "failed to deserialize project disclosure metadata", - )) - })?, + .wrap_err("deserializing project disclosure metadata")?, updated_at: row.updated_at, updated_by: DBUserId(row.updated_by), set_by_moderator: row.set_by_moderator, @@ -97,11 +94,7 @@ impl DBProjectDisclosure { lock_status: DisclosureLockStatus::from_str( &row.lock_status, ) - .map_err(|e| { - DatabaseError::Internal(eyre::Report::new(e).wrap_err( - "failed to parse project disclosure lock status", - )) - })?, + .wrap_err("parsing project disclosure lock status")?, }) }) .collect() @@ -112,7 +105,7 @@ impl DBProjectDisclosure { disclosure_type: ProjectDisclosureType, project_ids: &[DBProjectId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let ids = project_ids.iter().map(|id| id.0).collect::>(); let rows = sqlx::query_scalar!( r#" @@ -124,7 +117,8 @@ impl DBProjectDisclosure { &ids, ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching projects with disclosure type")?; Ok(rows.into_iter().map(DBProjectId).collect()) } @@ -133,7 +127,7 @@ impl DBProjectDisclosure { project_id: DBProjectId, types: &[String], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let rows = sqlx::query!( r#" SELECT type AS "disclosure_type!", lock_status @@ -144,20 +138,16 @@ impl DBProjectDisclosure { types, ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching project disclosure lock statuses")?; rows.into_iter() .map(|row| { - let lock_status = DisclosureLockStatus::from_str( - &row.lock_status, - ) - .map_err(|e| { - DatabaseError::Internal(eyre::Report::new(e).wrap_err( - "failed to parse project disclosure lock status", - )) - })?; + let lock_status = + DisclosureLockStatus::from_str(&row.lock_status) + .wrap_err("parsing project disclosure lock status")?; - Ok((row.disclosure_type, lock_status)) + eyre::Ok((row.disclosure_type, lock_status)) }) .collect() } @@ -168,7 +158,7 @@ impl DBProjectDisclosure { updated_by: DBUserId, set_by_moderator: bool, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result { + ) -> Result { let result = sqlx::query!( r#" UPDATE project_disclosures @@ -181,7 +171,8 @@ impl DBProjectDisclosure { set_by_moderator, ) .execute(exec) - .await?; + .await + .wrap_err("removing project disclosure")?; Ok(result.rows_affected() > 0) } diff --git a/apps/labrinth/src/database/models/project_item.rs b/apps/labrinth/src/database/models/project_item.rs index 991388dd96..5572ac2f57 100644 --- a/apps/labrinth/src/database/models/project_item.rs +++ b/apps/labrinth/src/database/models/project_item.rs @@ -3,7 +3,7 @@ use super::loader_fields::{ VersionField, }; use super::{DBUser, ids::*}; -use crate::database::models::DatabaseError; + use crate::database::{PgTransaction, models}; use crate::file_hosting::FileHost; use crate::models::exp; @@ -11,11 +11,11 @@ use crate::models::ids::ProjectId; use crate::models::projects::{ MonetizationStatus, ProjectStatus, SideTypesMigrationReviewStatus, }; -use crate::routes::ApiError; -use crate::util::{error::Context, kafka::KafkaClientState}; +use crate::util::kafka::KafkaClientState; use ariadne::ids::base62_impl::parse_base62; use chrono::{DateTime, Utc}; use dashmap::{DashMap, DashSet}; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -41,7 +41,7 @@ impl LinkUrl { links: Vec, project_id: DBProjectId, transaction: &mut PgTransaction<'_>, - ) -> Result<(), sqlx::error::Error> { + ) -> Result<()> { let (project_ids, platform_ids, urls): (Vec<_>, Vec<_>, Vec<_>) = links .into_iter() .map(|url| (project_id.0, url.platform_id.0, url.url)) @@ -58,7 +58,8 @@ impl LinkUrl { &urls[..], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting project links into database")?; Ok(()) } @@ -80,7 +81,7 @@ impl DBGalleryItem { items: Vec, project_id: DBProjectId, transaction: &mut PgTransaction<'_>, - ) -> Result<(), sqlx::error::Error> { + ) -> Result<()> { let ( project_ids, image_urls, @@ -119,7 +120,8 @@ impl DBGalleryItem { &orderings[..] ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting project gallery items into database")?; Ok(()) } @@ -135,7 +137,7 @@ impl DBModCategory { pub async fn insert_many( items: Vec, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let (project_ids, category_ids, is_additionals): ( Vec<_>, Vec<_>, @@ -154,7 +156,8 @@ impl DBModCategory { &is_additionals[..] ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting project categories")?; Ok(()) } @@ -192,7 +195,7 @@ impl ProjectBuilder { redis: &RedisPool, file_host: &dyn FileHost, kafka_client: &KafkaClientState, - ) -> Result { + ) -> Result { let project_struct = DBProject { id: self.project_id, team_id: self.team_id, @@ -227,7 +230,10 @@ impl ProjectBuilder { loaders: vec![], components: self.components, }; - project_struct.insert(&mut *transaction).await?; + project_struct + .insert(&mut *transaction) + .await + .wrap_err("inserting project")?; let ProjectBuilder { link_urls, @@ -241,7 +247,8 @@ impl ProjectBuilder { version.project_id = self.project_id; version .insert(transaction, redis, file_host, kafka_client) - .await?; + .await + .wrap_err("inserting initial project version")?; } LinkUrl::insert_many_projects( @@ -249,14 +256,16 @@ impl ProjectBuilder { self.project_id, &mut *transaction, ) - .await?; + .await + .wrap_err("inserting project links")?; DBGalleryItem::insert_many( gallery_items, self.project_id, &mut *transaction, ) - .await?; + .await + .wrap_err("inserting project gallery items")?; let project_id = self.project_id; let mod_categories = categories @@ -274,7 +283,9 @@ impl ProjectBuilder { } })) .collect_vec(); - DBModCategory::insert_many(mod_categories, &mut *transaction).await?; + DBModCategory::insert_many(mod_categories, &mut *transaction) + .await + .wrap_err("inserting project categories")?; Ok(self.project_id) } @@ -314,7 +325,7 @@ impl DBProject { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO mods ( @@ -355,7 +366,8 @@ impl DBProject { serde_json::to_value(&self.components).expect("serialization shouldn't fail"), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting project")?; Ok(()) } @@ -364,15 +376,15 @@ impl DBProject { id: DBProjectId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, ApiError> { + ) -> Result> { let project = Self::get_id(id, &mut *transaction, redis) .await - .wrap_internal_err("failed to fetch project by ID")?; + .wrap_err("fetching project by id")?; if let Some(project) = project { DBProject::clear_cache(id, project.inner.slug, Some(true), redis) .await - .wrap_internal_err("failed to clear project cache")?; + .wrap_err("clearing project cache")?; sqlx::query!( " @@ -383,7 +395,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete project followers")?; + .wrap_err("deleting project followers")?; sqlx::query!( " @@ -394,7 +406,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete project gallery items")?; + .wrap_err("deleting project gallery items")?; sqlx::query!( " @@ -405,9 +417,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err( - "failed to delete duplicate project followers", - )?; + .wrap_err("deleting duplicate project followers")?; sqlx::query!( " @@ -419,7 +429,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to clear report project references")?; + .wrap_err("clearing report project references")?; sqlx::query!( " @@ -430,7 +440,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete project categories")?; + .wrap_err("deleting project categories")?; sqlx::query!( " @@ -441,12 +451,12 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete project links")?; + .wrap_err("deleting project links")?; for version in project.versions { super::DBVersion::remove_full(version, redis, transaction) .await - .wrap_internal_err("failed to remove project version")?; + .wrap_err("removing project version")?; } sqlx::query!( @@ -457,7 +467,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete dependency references")?; + .wrap_err("deleting dependency references")?; sqlx::query!( " @@ -469,7 +479,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to clear payout project references")?; + .wrap_err("clearing payout project references")?; sqlx::query!( " @@ -480,11 +490,11 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete project row")?; + .wrap_err("deleting project row")?; models::DBTeamMember::clear_cache(project.inner.team_id, redis) .await - .wrap_internal_err("failed to clear team member cache")?; + .wrap_err("clearing team member cache")?; let affected_user_ids = sqlx::query!( " @@ -498,11 +508,11 @@ impl DBProject { .map_ok(|x| DBUserId(x.user_id)) .try_collect::>() .await - .wrap_internal_err("failed to delete team members")?; + .wrap_err("deleting team members")?; DBUser::clear_project_cache(&affected_user_ids, redis) .await - .wrap_internal_err("failed to clear user project cache")?; + .wrap_err("clearing user project cache")?; sqlx::query!( " @@ -513,7 +523,7 @@ impl DBProject { ) .execute(&mut *transaction) .await - .wrap_internal_err("failed to delete team")?; + .wrap_err("deleting team")?; Ok(Some(())) } else { @@ -525,12 +535,13 @@ impl DBProject { string: &str, executor: E, redis: &RedisPool, - ) -> Result, ApiError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { DBProject::get_many(&[string], executor, redis) .await + .wrap_err("fetching project") .map(|x| x.into_iter().next()) } @@ -538,7 +549,7 @@ impl DBProject { id: DBProjectId, executor: E, redis: &RedisPool, - ) -> Result, ApiError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { @@ -548,6 +559,7 @@ impl DBProject { redis, ) .await + .wrap_err("fetching project by id") .map(|x| x.into_iter().next()) } @@ -555,7 +567,7 @@ impl DBProject { project_ids: &[DBProjectId], exec: E, redis: &RedisPool, - ) -> Result, ApiError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { @@ -563,7 +575,9 @@ impl DBProject { .iter() .map(|x| crate::models::ids::ProjectId::from(*x)) .collect::>(); - DBProject::get_many(&ids, exec, redis).await + DBProject::get_many(&ids, exec, redis) + .await + .wrap_err("fetching projects by id") } pub async fn get_many< @@ -574,11 +588,13 @@ impl DBProject { project_strings: &[T], exec: E, redis: &RedisPool, - ) -> Result, ApiError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { - Self::get_many_inner(project_strings, exec, redis, true).await + Self::get_many_inner(project_strings, exec, redis, true) + .await + .wrap_err("fetching projects") } pub async fn get_many_uncached< @@ -589,11 +605,13 @@ impl DBProject { project_strings: &[T], exec: E, redis: &RedisPool, - ) -> Result, ApiError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { - Self::get_many_inner(project_strings, exec, redis, false).await + Self::get_many_inner(project_strings, exec, redis, false) + .await + .wrap_err("fetching uncached projects") } async fn get_many_inner< @@ -605,7 +623,7 @@ impl DBProject { exec: E, redis: &RedisPool, use_cache: bool, - ) -> Result, ApiError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { @@ -618,7 +636,8 @@ impl DBProject { |ids| async move { let mut exec = exec .acquire() - .await?; + .await + .wrap_err("acquiring database connection for project query")?; let project_ids_parsed: Vec = ids .iter() .filter_map(|x| parse_base62(&x.to_string()).ok()) @@ -657,7 +676,8 @@ impl DBProject { async move { Ok(acc) } }, ) - .await?; + .await + .wrap_err("fetching project versions")?; let loader_field_enum_value_ids = DashSet::new(); let version_fields: DashMap> = sqlx::query!( @@ -689,7 +709,8 @@ impl DBProject { async move { Ok(acc) } }, ) - .await?; + .await + .wrap_err("fetching project version fields")?; let loader_field_enum_values: Vec = sqlx::query!( r#" @@ -716,7 +737,8 @@ impl DBProject { major: m.major, }) .try_collect() - .await?; + .await + .wrap_err("fetching project loader field enum values")?; let mods_gallery: DashMap> = sqlx::query!( " @@ -743,7 +765,8 @@ impl DBProject { async move { Ok(acc) } } ) - .await?; + .await + .wrap_err("fetching project gallery items")?; let links: DashMap> = sqlx::query!( " @@ -768,7 +791,8 @@ impl DBProject { async move { Ok(acc) } } ) - .await?; + .await + .wrap_err("fetching project links")?; #[derive(Default)] struct VersionLoaderData { @@ -821,7 +845,8 @@ impl DBProject { } ) .try_collect() - .await?; + .await + .wrap_err("fetching project loader metadata")?; let loader_fields: Vec = sqlx::query!( " @@ -842,7 +867,8 @@ impl DBProject { optional: m.optional, }) .try_collect() - .await?; + .await + .wrap_err("fetching project loader fields")?; let project_rows = sqlx::query!( r#" @@ -870,7 +896,8 @@ impl DBProject { &slugs, ) .fetch_all(&mut exec) - .await?; + .await + .wrap_err("fetching project rows")?; let project_components = project_rows .iter() @@ -891,7 +918,7 @@ impl DBProject { let projects = project_rows .into_iter() - .try_fold(DashMap::new(), |acc, m| -> Result<_, DatabaseError> { + .try_fold(DashMap::new(), |acc, m| { let id = m.id; let project_id = DBProjectId(id); let VersionLoaderData { @@ -938,9 +965,10 @@ impl DBProject { status: ProjectStatus::from_string( &m.status, ), - requested_status: m.requested_status.map(|x| ProjectStatus::from_string( - &x, - )), + requested_status: m + .requested_status + .as_deref() + .map(ProjectStatus::from_string), license: m.license.clone(), slug: m.slug.clone(), description: m.description.clone(), @@ -976,15 +1004,15 @@ impl DBProject { }; acc.insert(m.id, (m.slug, project)); - Ok(acc) + eyre::Ok(acc) }) - ?; + .wrap_err("building project query results")?; - Ok::<_, DatabaseError>(projects) + eyre::Ok(projects) }, ) .await - .wrap_internal_err("fetching cached projects")?; + .wrap_err("fetching cached projects")?; Ok(val) } @@ -999,7 +1027,6 @@ impl DBProject { Option, Option, )>, - DatabaseError, > where E: crate::database::Executor<'a, Database = sqlx::Postgres>, @@ -1011,11 +1038,15 @@ impl DBProject { )>; { - let mut redis = redis.connect().await?; + let mut redis = redis.connect().await.wrap_err( + "connecting to redis to fetch project dependencies", + )?; let key = redis.key().entity(PROJECTS_DEPENDENCIES_NAMESPACE, id.0); - let dependencies = - redis.get_deserialized::(&key).await?; + let dependencies = redis + .get_deserialized::(&key) + .await + .wrap_err("fetching cached project dependencies")?; if let Some(dependencies) = dependencies { return Ok(dependencies); } @@ -1044,12 +1075,19 @@ impl DBProject { ) }) .try_collect::() - .await?; + .await + .wrap_err("fetching project dependencies")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache project dependencies")?; let key = redis.key().entity(PROJECTS_DEPENDENCIES_NAMESPACE, id.0); - redis.set_serialized(&key, &dependencies, None).await?; + redis + .set_serialized(&key, &dependencies, None) + .await + .wrap_err("caching project dependencies")?; Ok(dependencies) } @@ -1058,8 +1096,11 @@ impl DBProject { slug: Option, clear_dependencies: Option, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear project cache")?; let mut keys = vec![redis.key().entity(PROJECTS_NAMESPACE, id.0)]; if let Some(slug) = slug { keys.push( @@ -1074,7 +1115,10 @@ impl DBProject { ); } - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing project cache")?; Ok(()) } } diff --git a/apps/labrinth/src/database/models/session_item.rs b/apps/labrinth/src/database/models/session_item.rs index 9ca8ae560d..a8fb2c62c3 100644 --- a/apps/labrinth/src/database/models/session_item.rs +++ b/apps/labrinth/src/database/models/session_item.rs @@ -1,9 +1,9 @@ use super::ids::*; use crate::database::PgTransaction; -use crate::database::models::DatabaseError; use ariadne::ids::base62_impl::parse_base62; use chrono::{DateTime, Utc}; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures_util::TryStreamExt; use serde::{Deserialize, Serialize}; use std::fmt::{Debug, Display}; @@ -37,8 +37,10 @@ impl SessionBuilder { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { - let id = generate_session_id(transaction).await?; + ) -> Result { + let id = generate_session_id(transaction) + .await + .wrap_err("generating session id")?; sqlx::query!( " @@ -68,7 +70,8 @@ impl SessionBuilder { .unwrap_or_else(|| Utc::now() + chrono::Duration::days(60)), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting session")?; Ok(id) } @@ -103,20 +106,21 @@ impl DBSession { id: T, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Self::get_many(&[id], exec, redis) .await .map(|x| x.into_iter().next()) + .wrap_err("getting session") } pub async fn get_id<'a, 'b, E>( id: DBSessionId, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -127,13 +131,14 @@ impl DBSession { ) .await .map(|x| x.into_iter().next()) + .wrap_err("getting session by id") } pub async fn get_many_ids<'a, E>( session_ids: &[DBSessionId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -141,7 +146,9 @@ impl DBSession { .iter() .map(|x| crate::models::ids::SessionId::from(*x)) .collect::>(); - DBSession::get_many(&ids, exec, redis).await + DBSession::get_many(&ids, exec, redis) + .await + .wrap_err("getting sessions by id") } pub async fn get_many< @@ -152,7 +159,7 @@ impl DBSession { session_strings: &[T], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -173,7 +180,8 @@ impl DBSession { .into_iter() .map(|x| x.to_string()) .collect::>(); - let db_sessions = sqlx::query!( + + sqlx::query!( " SELECT id, user_id, session, created, last_login, expires, refresh_expires, os, platform, city, country, ip, user_agent @@ -206,10 +214,9 @@ impl DBSession { async move { Ok(acc) } }) - .await?; - - Ok::<_, DatabaseError>(db_sessions) - }).await?; + .await + }).await + .wrap_err("getting sessions from cache or database")?; Ok(val) } @@ -218,15 +225,21 @@ impl DBSession { user_id: DBUserId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for user sessions")?; let key = redis.key().entity(SESSIONS_USERS_NAMESPACE, user_id.0); - let res = redis.get_deserialized::>(&key).await?; + let res = redis + .get_deserialized::>(&key) + .await + .wrap_err("getting cached user sessions")?; if let Some(res) = res { return Ok(res.into_iter().map(DBSessionId).collect()); @@ -246,12 +259,19 @@ impl DBSession { .fetch(exec) .map_ok(|x| DBSessionId(x.id)) .try_collect::>() - .await?; + .await + .wrap_err("fetching user sessions from database")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache user sessions")?; let key = redis.key().entity(SESSIONS_USERS_NAMESPACE, user_id.0); - redis.set_serialized(&key, &db_sessions, None).await?; + redis + .set_serialized(&key, &db_sessions, None) + .await + .wrap_err("caching user sessions")?; Ok(db_sessions) } @@ -263,8 +283,11 @@ impl DBSession { Option, )>, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear session caches")?; if clear_sessions.is_empty() { return Ok(()); @@ -286,14 +309,17 @@ impl DBSession { .flatten() }) .collect::>(); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing session caches")?; Ok(()) } pub async fn remove( id: DBSessionId, transaction: &mut PgTransaction<'_>, - ) -> Result, sqlx::error::Error> { + ) -> std::result::Result, sqlx::error::Error> { sqlx::query!( " DELETE FROM sessions WHERE id = $1 @@ -309,7 +335,7 @@ impl DBSession { pub async fn remove_all_for_user( user_id: DBUserId, transaction: &mut PgTransaction<'_>, - ) -> Result, sqlx::Error> { + ) -> std::result::Result, sqlx::Error> { let sessions = sqlx::query!( " DELETE FROM sessions WHERE user_id = $1 RETURNING id, session diff --git a/apps/labrinth/src/database/models/team_item.rs b/apps/labrinth/src/database/models/team_item.rs index 4246e60d7d..252559dbca 100644 --- a/apps/labrinth/src/database/models/team_item.rs +++ b/apps/labrinth/src/database/models/team_item.rs @@ -4,6 +4,7 @@ use crate::{ models::teams::{OrganizationPermissions, ProjectPermissions}, }; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use itertools::Itertools; use rust_decimal::Decimal; @@ -30,8 +31,10 @@ impl TeamBuilder { pub async fn insert( self, transaction: &mut PgTransaction<'_>, - ) -> Result { - let team_id = generate_team_id(transaction).await?; + ) -> Result { + let team_id = generate_team_id(transaction) + .await + .wrap_err("generating team id")?; let team = DBTeam { id: team_id }; @@ -43,11 +46,17 @@ impl TeamBuilder { team.id as DBTeamId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting team")?; let mut team_member_ids = Vec::new(); for _ in &self.members { - team_member_ids.push(generate_team_member_id(transaction).await?.0); + team_member_ids.push( + generate_team_member_id(transaction) + .await + .wrap_err("generating team member id")? + .0, + ); } let TeamBuilder { members } = self; let ( @@ -103,7 +112,8 @@ impl TeamBuilder { &orderings[..], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting team members")?; Ok(team_id) } @@ -125,7 +135,7 @@ impl DBTeam { pub async fn get_association<'a, 'b, E>( id: DBTeamId, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -144,7 +154,8 @@ impl DBTeam { id as DBTeamId ) .fetch_optional(executor) - .await?; + .await + .wrap_err("fetching team association")?; if let Some(t) = result { // Only one of project_id or organization_id will be set @@ -194,18 +205,20 @@ impl DBTeamMember { id: DBTeamId, executor: E, redis: &RedisPool, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { - Self::get_from_team_full_many(&[id], executor, redis).await + Self::get_from_team_full_many(&[id], executor, redis) + .await + .wrap_err("fetching full team members") } pub async fn get_from_team_full_many<'a, E>( team_ids: &[DBTeamId], exec: E, redis: &RedisPool, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { @@ -217,7 +230,7 @@ impl DBTeamMember { TEAMS_NAMESPACE, &team_ids.iter().map(|x| x.0).collect::>(), |team_ids| async move { - let teams = sqlx::query!( + sqlx::query!( " SELECT id, team_id, role AS member_role, is_owner, permissions, organization_permissions, accepted, payouts_split, @@ -250,24 +263,24 @@ impl DBTeamMember { .or_default() .push(member); - async move { Ok(acc) } + async move { Ok::<_, sqlx::Error>(acc) } }) - .await?; - - Ok::<_, crate::database::models::DatabaseError>(teams) + .await }, - ).await?; + ) + .await + .wrap_err("fetching cached team members")?; Ok(val.into_iter().flatten().collect()) } - pub async fn clear_cache( - id: DBTeamId, - redis: &RedisPool, - ) -> Result<(), super::DatabaseError> { - let mut redis = redis.connect().await?; + pub async fn clear_cache(id: DBTeamId, redis: &RedisPool) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear team cache")?; let key = redis.key().entity(TEAMS_NAMESPACE, id.0); - redis.delete(&key).await?; + redis.delete(&key).await.wrap_err("clearing team cache")?; Ok(()) } @@ -276,12 +289,13 @@ impl DBTeamMember { id: DBTeamId, user_id: DBUserId, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { Self::get_from_user_id_many(&[id], user_id, executor) .await + .wrap_err("fetching team member by user id") .map(|x| x.into_iter().next()) } @@ -290,7 +304,7 @@ impl DBTeamMember { team_ids: &[DBTeamId], user_id: DBUserId, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -325,7 +339,8 @@ impl DBTeamMember { ordering: m.ordering, }) .try_collect::>() - .await?; + .await + .wrap_err("fetching team members by user id")?; Ok(team_members) } @@ -335,7 +350,7 @@ impl DBTeamMember { id: DBTeamId, user_id: DBUserId, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -353,7 +368,8 @@ impl DBTeamMember { user_id as DBUserId ) .fetch_optional(executor) - .await?; + .await + .wrap_err("fetching pending team member by user id")?; if let Some(m) = result { Ok(Some(DBTeamMember { @@ -382,7 +398,7 @@ impl DBTeamMember { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), sqlx::error::Error> { + ) -> std::result::Result<(), sqlx::error::Error> { sqlx::query!( " INSERT INTO team_members ( @@ -412,7 +428,7 @@ impl DBTeamMember { id: DBTeamId, user_id: DBUserId, transaction: &mut PgTransaction<'_>, - ) -> Result<(), super::DatabaseError> { + ) -> Result<()> { sqlx::query!( " DELETE FROM team_members @@ -422,7 +438,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting team member")?; Ok(()) } @@ -439,7 +456,7 @@ impl DBTeamMember { new_ordering: Option, new_is_owner: Option, transaction: &mut PgTransaction<'_>, - ) -> Result<(), super::DatabaseError> { + ) -> Result<()> { if let Some(permissions) = new_permissions { sqlx::query!( " @@ -452,7 +469,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating team member project permissions")?; } if let Some(organization_permissions) = new_organization_permissions { @@ -467,7 +485,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating team member organization permissions")?; } if let Some(role) = new_role { @@ -482,7 +501,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating team member role")?; } if let Some(accepted) = new_accepted @@ -498,7 +518,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("accepting team member")?; } if let Some(payouts_split) = new_payouts_split { @@ -513,7 +534,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating team member payout split")?; } if let Some(ordering) = new_ordering { @@ -528,7 +550,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating team member ordering")?; } if let Some(is_owner) = new_is_owner { @@ -543,7 +566,8 @@ impl DBTeamMember { user_id as DBUserId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating team member ownership")?; } Ok(()) @@ -554,7 +578,7 @@ impl DBTeamMember { user_id: DBUserId, allow_pending: bool, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -576,7 +600,8 @@ impl DBTeamMember { &accepted ) .fetch_optional(executor) - .await?; + .await + .wrap_err("fetching project team member by user id")?; if let Some(m) = result { Ok(Some(DBTeamMember { @@ -607,7 +632,7 @@ impl DBTeamMember { user_id: DBUserId, allow_pending: bool, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -628,7 +653,8 @@ impl DBTeamMember { &accepted ) .fetch_optional(executor) - .await?; + .await + .wrap_err("fetching organization team member by user id")?; if let Some(m) = result { Ok(Some(DBTeamMember { @@ -658,7 +684,7 @@ impl DBTeamMember { id: DBVersionId, user_id: DBUserId, executor: E, - ) -> Result, super::DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -674,7 +700,8 @@ impl DBTeamMember { user_id as DBUserId ) .fetch_optional(executor) - .await?; + .await + .wrap_err("fetching version team member by user id")?; if let Some(m) = result { Ok(Some(DBTeamMember { @@ -707,23 +734,27 @@ impl DBTeamMember { project: &DBProject, user_id: DBUserId, executor: E, - ) -> Result<(Option, Option), super::DatabaseError> + ) -> Result<(Option, Option)> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { let project_team_member = - Self::get_from_user_id(project.team_id, user_id, executor).await?; + Self::get_from_user_id(project.team_id, user_id, executor) + .await + .wrap_err("fetching project team member for permissions")?; let organization = DBOrganization::get_associated_organization_project_id( project.id, executor, ) - .await?; + .await + .wrap_err("fetching project organization for permissions")?; let organization_team_member = if let Some(organization) = &organization { Self::get_from_user_id(organization.team_id, user_id, executor) - .await? + .await + .wrap_err("fetching organization team member for permissions")? } else { None }; diff --git a/apps/labrinth/src/database/models/thread_item.rs b/apps/labrinth/src/database/models/thread_item.rs index 9796044ddb..cb866afc53 100644 --- a/apps/labrinth/src/database/models/thread_item.rs +++ b/apps/labrinth/src/database/models/thread_item.rs @@ -1,8 +1,8 @@ use super::ids::*; use crate::database::PgTransaction; -use crate::database::models::DatabaseError; use crate::models::threads::{MessageBody, ThreadType}; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; pub struct ThreadBuilder { @@ -45,8 +45,10 @@ impl ThreadMessageBuilder { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { - let thread_message_id = generate_thread_message_id(transaction).await?; + ) -> Result { + let thread_message_id = generate_thread_message_id(transaction) + .await + .wrap_err("generating thread message id")?; sqlx::query!( " @@ -59,12 +61,14 @@ impl ThreadMessageBuilder { ", thread_message_id as DBThreadMessageId, self.author_id.map(|x| x.0), - serde_json::value::to_value(self.body.clone())?, + serde_json::value::to_value(self.body.clone()) + .wrap_err("serializing thread message body")?, self.thread_id as DBThreadId, self.hide_identity ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting thread message")?; Ok(thread_message_id) } @@ -74,8 +78,10 @@ impl ThreadBuilder { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result { - let thread_id = generate_thread_id(&mut *transaction).await?; + ) -> Result { + let thread_id = generate_thread_id(&mut *transaction) + .await + .wrap_err("generating thread id")?; sqlx::query!( " INSERT INTO threads ( @@ -91,7 +97,8 @@ impl ThreadBuilder { self.report_id.map(|x| x.0), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting thread")?; let (thread_ids, members): (Vec<_>, Vec<_>) = self.members.iter().map(|m| (thread_id.0, m.0)).unzip(); @@ -106,7 +113,8 @@ impl ThreadBuilder { &members[..], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting thread members")?; Ok(thread_id) } diff --git a/apps/labrinth/src/database/models/user_item.rs b/apps/labrinth/src/database/models/user_item.rs index e20adf2236..06c6231927 100644 --- a/apps/labrinth/src/database/models/user_item.rs +++ b/apps/labrinth/src/database/models/user_item.rs @@ -1,18 +1,18 @@ use super::ids::{DBProjectId, DBUserId}; use super::{DBCollectionId, DBReportId, DBThreadId}; +use crate::database::models::DBOrganizationId; use crate::database::models::charge_item::DBCharge; use crate::database::models::thread_item::ThreadMessageBuilder; use crate::database::models::user_subscription_item::DBUserSubscription; -use crate::database::models::{DBOrganizationId, DatabaseError}; use crate::database::{PgTransaction, models}; use crate::models::billing::ChargeStatus; use crate::models::projects::ProjectStatus; use crate::models::threads::MessageBody; use crate::models::users::Badges; -use crate::util::error::Context; use ariadne::ids::base62_impl::{parse_base62, to_base62}; use chrono::{DateTime, Utc}; use dashmap::DashMap; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; @@ -81,7 +81,7 @@ impl DBUser { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), sqlx::error::Error> { + ) -> std::result::Result<(), sqlx::error::Error> { sqlx::query!( " INSERT INTO users ( @@ -134,33 +134,35 @@ impl DBUser { string: &str, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { DBUser::get_many(&[string], executor, redis) .await .map(|x| x.into_iter().next()) + .wrap_err("getting user") } pub async fn get_id<'a, 'b, E>( id: DBUserId, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { DBUser::get_many(&[ariadne::ids::UserId::from(id)], executor, redis) .await .map(|x| x.into_iter().next()) + .wrap_err("getting user by id") } pub async fn get_many_ids<'a, E>( user_ids: &[DBUserId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -168,7 +170,9 @@ impl DBUser { .iter() .map(|x| ariadne::ids::UserId::from(*x)) .collect::>(); - DBUser::get_many(&ids, exec, redis).await + DBUser::get_many(&ids, exec, redis) + .await + .wrap_err("getting users by id") } pub async fn get_many< @@ -179,7 +183,7 @@ impl DBUser { users_strings: &[T], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -201,7 +205,7 @@ impl DBUser { .map(|x| x.to_string().to_lowercase()) .collect::>(); - let users = sqlx::query!( + sqlx::query!( " SELECT id, email, avatar_url, raw_avatar_url, username, bio, @@ -275,17 +279,16 @@ impl DBUser { acc.insert(u.id, (Some(u.username), user)); async move { Ok(acc) } }) - .await?; - - Ok::<_, DatabaseError>(users) - }).await?; + .await + }).await + .wrap_err("getting users from cache or database")?; Ok(val) } pub async fn search<'a, E>( query: &str, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -323,7 +326,7 @@ impl DBUser { pub async fn get_by_discord_id<'a, E>( discord_id: u64, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -343,7 +346,7 @@ impl DBUser { pub async fn get_by_email<'a, E>( email: &str, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -364,7 +367,7 @@ impl DBUser { pub async fn get_by_case_insensitive_email<'a, E>( email: &str, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -386,7 +389,7 @@ impl DBUser { pub async fn exists_many<'a, E>( user_ids: &[DBUserId], exec: E, - ) -> Result + ) -> std::result::Result where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -405,18 +408,23 @@ impl DBUser { user_id: DBUserId, exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { use futures::stream::TryStreamExt; { - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis for user projects")?; let key = redis.key().entity(USERS_PROJECTS_NAMESPACE, user_id.0); - let cached_projects = - redis.get_deserialized::>(&key).await?; + let cached_projects = redis + .get_deserialized::>(&key) + .await + .wrap_err("getting cached user projects")?; if let Some(projects) = cached_projects { return Ok(projects); @@ -435,12 +443,19 @@ impl DBUser { .fetch(exec) .map_ok(|m| DBProjectId(m.id)) .try_collect::>() - .await?; + .await + .wrap_err("fetching user projects from database")?; - let mut redis = redis.connect().await?; + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to cache user projects")?; let key = redis.key().entity(USERS_PROJECTS_NAMESPACE, user_id.0); - redis.set_serialized(&key, &db_projects, None).await?; + redis + .set_serialized(&key, &db_projects, None) + .await + .wrap_err("caching user projects")?; Ok(db_projects) } @@ -448,7 +463,7 @@ impl DBUser { pub async fn get_organizations<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -473,7 +488,7 @@ impl DBUser { pub async fn get_collections<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -497,7 +512,7 @@ impl DBUser { pub async fn get_follows<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -521,7 +536,7 @@ impl DBUser { pub async fn get_reports<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -545,7 +560,7 @@ impl DBUser { pub async fn get_backup_codes<'a, E>( user_id: DBUserId, exec: E, - ) -> Result, sqlx::Error> + ) -> std::result::Result, sqlx::Error> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -569,8 +584,11 @@ impl DBUser { pub async fn clear_caches( user_ids: &[(DBUserId, Option)], redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear user caches")?; let keys = user_ids .iter() .flat_map(|(id, username)| { @@ -588,21 +606,30 @@ impl DBUser { }) .collect::>(); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing user caches")?; Ok(()) } pub async fn clear_project_cache( user_ids: &[DBUserId], redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear user project caches")?; let keys = user_ids .iter() .map(|id| redis.key().entity(USERS_PROJECTS_NAMESPACE, id.0)) .collect::>(); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing user project caches")?; Ok(()) } @@ -611,7 +638,7 @@ impl DBUser { id: DBUserId, transaction: &mut PgTransaction<'_>, redis: &RedisPool, - ) -> Result, eyre::Report> { + ) -> Result> { let user = Self::get_id(id, &mut *transaction, redis) .await .wrap_err("failed to get user by ID")?; diff --git a/apps/labrinth/src/database/models/user_subscription_item.rs b/apps/labrinth/src/database/models/user_subscription_item.rs index e441663cf6..addefd14c9 100644 --- a/apps/labrinth/src/database/models/user_subscription_item.rs +++ b/apps/labrinth/src/database/models/user_subscription_item.rs @@ -1,10 +1,11 @@ use crate::database::models::{ - DBProductPriceId, DBUserId, DBUserSubscriptionId, DatabaseError, + DBProductPriceId, DBUserId, DBUserSubscriptionId, }; use crate::models::billing::{ PriceDuration, ProductMetadata, SubscriptionMetadata, SubscriptionStatus, }; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use itertools::Itertools; use std::convert::{TryFrom, TryInto}; @@ -46,7 +47,9 @@ macro_rules! select_user_subscriptions_with_predicate { impl TryFrom for DBUserSubscription { type Error = serde_json::Error; - fn try_from(r: UserSubscriptionQueryResult) -> Result { + fn try_from( + r: UserSubscriptionQueryResult, + ) -> std::result::Result { Ok(DBUserSubscription { id: DBUserSubscriptionId(r.id), user_id: DBUserId(r.user_id), @@ -63,14 +66,18 @@ impl DBUserSubscription { pub async fn get( id: DBUserSubscriptionId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - Ok(Self::get_many(&[id], exec).await?.into_iter().next()) + ) -> Result> { + Ok(Self::get_many(&[id], exec) + .await + .wrap_err("fetching user subscription")? + .into_iter() + .next()) } pub async fn get_many( ids: &[DBUserSubscriptionId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let ids = ids.iter().map(|id| id.0).collect_vec(); let ids_ref: &[i64] = &ids; let results = select_user_subscriptions_with_predicate!( @@ -78,36 +85,40 @@ impl DBUserSubscription { ids_ref ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching user subscriptions")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing user subscription metadata") } pub async fn get_all_user( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let user_id = user_id.0; let results = select_user_subscriptions_with_predicate!( "WHERE us.user_id = $1", user_id ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching user subscriptions for user")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing user subscription metadata") } pub async fn get_all_servers( status: Option, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let status = status.map(|x| x.as_str()); let results = select_user_subscriptions_with_predicate!( @@ -120,18 +131,20 @@ impl DBUserSubscription { status ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching server subscriptions")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing user subscription metadata") } pub async fn upsert( &self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { sqlx::query!( " INSERT INTO users_subscriptions ( @@ -153,10 +166,12 @@ impl DBUserSubscription { self.interval.as_str(), self.created, self.status.as_str(), - serde_json::to_value(&self.metadata)?, + serde_json::to_value(&self.metadata) + .wrap_err("serializing user subscription metadata")?, ) .execute(exec) - .await?; + .await + .wrap_err("upserting user subscription")?; Ok(()) } @@ -164,7 +179,7 @@ impl DBUserSubscription { pub async fn get_many_by_server_ids( server_ids: &[String], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { if server_ids.is_empty() { return Ok(vec![]); } @@ -179,12 +194,14 @@ impl DBUserSubscription { server_ids ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching user subscriptions by server ID")?; - Ok(results + results .into_iter() - .map(|r| r.try_into()) - .collect::, serde_json::Error>>()?) + .map(TryInto::try_into) + .collect::, _>>() + .wrap_err("deserializing user subscription metadata") } } diff --git a/apps/labrinth/src/database/models/users_notifications_preferences_item.rs b/apps/labrinth/src/database/models/users_notifications_preferences_item.rs index 1ba5e1ce01..d5b128aa48 100644 --- a/apps/labrinth/src/database/models/users_notifications_preferences_item.rs +++ b/apps/labrinth/src/database/models/users_notifications_preferences_item.rs @@ -1,6 +1,6 @@ use super::ids::*; -use crate::database::models::DatabaseError; use crate::models::v3::notifications::{NotificationChannel, NotificationType}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] @@ -40,14 +40,16 @@ impl UserNotificationPreference { pub async fn get_user_or_default( user_id: DBUserId, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { - Self::get_many_users_or_default(&[user_id], exec).await + ) -> Result> { + Self::get_many_users_or_default(&[user_id], exec) + .await + .wrap_err("fetching user notification preferences") } pub async fn get_many_users_or_default( user_ids: &[DBUserId], exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result, DatabaseError> { + ) -> Result> { let results = sqlx::query!( r#" SELECT @@ -65,7 +67,8 @@ impl UserNotificationPreference { &user_ids.iter().map(|x| x.0).collect::>(), ) .fetch_all(exec) - .await?; + .await + .wrap_err("fetching user notification preferences")?; let preferences = results .into_iter() @@ -87,7 +90,7 @@ impl UserNotificationPreference { pub async fn insert( &mut self, exec: impl crate::database::Executor<'_, Database = sqlx::Postgres>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let id = sqlx::query_scalar!( " INSERT INTO users_notifications_preferences ( @@ -102,7 +105,8 @@ impl UserNotificationPreference { self.enabled, ) .fetch_one(exec) - .await?; + .await + .wrap_err("inserting user notification preference")?; self.id = id; diff --git a/apps/labrinth/src/database/models/version_item.rs b/apps/labrinth/src/database/models/version_item.rs index 4a918a3231..92c83fc356 100644 --- a/apps/labrinth/src/database/models/version_item.rs +++ b/apps/labrinth/src/database/models/version_item.rs @@ -1,7 +1,7 @@ -use super::DatabaseError; use super::ids::*; use super::loader_fields::VersionField; use crate::database::PgTransaction; + use crate::database::models::loader_fields::{ QueryLoaderField, QueryLoaderFieldEnumValue, QueryVersionField, }; @@ -14,6 +14,7 @@ use crate::queue::{delphi_scan, file_scan::scan_file}; use crate::util::kafka::KafkaClientState; use chrono::{DateTime, Utc}; use dashmap::{DashMap, DashSet}; +use eyre::{Result, WrapErr}; use futures::TryStreamExt; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -27,7 +28,7 @@ const VERSION_FILES_NAMESPACE: &str = "versions_files:v4"; pub async fn cleanup_unused_attribution_files_and_groups( transaction: &mut PgTransaction<'_>, -) -> Result<(), DatabaseError> { +) -> Result<()> { sqlx::query!( " DELETE FROM project_attribution_files paf @@ -44,7 +45,8 @@ pub async fn cleanup_unused_attribution_files_and_groups( ", ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting unused attribution files")?; sqlx::query!( " @@ -57,7 +59,8 @@ pub async fn cleanup_unused_attribution_files_and_groups( ", ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting unused attribution groups")?; Ok(()) } @@ -95,13 +98,14 @@ impl DependencyBuilder { builders: Vec, version_id: DBVersionId, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let mut project_ids = Vec::new(); for dependency in &builders { project_ids.push( dependency .try_get_project_id(transaction) - .await? + .await + .wrap_err("resolving dependency project")? .map(|id| id.0), ); } @@ -134,7 +138,8 @@ impl DependencyBuilder { &filenames[..] as &[Option], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting dependencies")?; Ok(()) } @@ -142,7 +147,7 @@ impl DependencyBuilder { async fn try_get_project_id( &self, transaction: &mut PgTransaction<'_>, - ) -> Result, DatabaseError> { + ) -> Result> { Ok(if let Some(project_id) = self.project_id { Some(project_id) } else if let Some(version_id) = self.version_id { @@ -153,7 +158,8 @@ impl DependencyBuilder { version_id as DBVersionId, ) .fetch_optional(&mut *transaction) - .await? + .await + .wrap_err("fetching dependency project")? .map(|x| DBProjectId(x.mod_id)) } else { None @@ -180,8 +186,10 @@ impl VersionFileBuilder { redis: &RedisPool, file_host: &dyn FileHost, kafka_client: &KafkaClientState, - ) -> Result { - let file_id = generate_file_id(&mut *transaction).await?; + ) -> Result { + let file_id = generate_file_id(&mut *transaction) + .await + .wrap_err("generating file id")?; sqlx::query!( " @@ -197,7 +205,8 @@ impl VersionFileBuilder { self.file_type.map(|x| x.as_str()), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting version file")?; for hash in self.hashes { sqlx::query!( @@ -210,7 +219,8 @@ impl VersionFileBuilder { hash.hash, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting file hash")?; } let attribution_scan = sqlx::query!( @@ -227,7 +237,8 @@ impl VersionFileBuilder { version_id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("creating attribution file scan")?; if attribution_scan.rows_affected() > 0 && let Err(err) = scan_file( @@ -245,7 +256,7 @@ impl VersionFileBuilder { delphi_scan::enqueue_file(transaction, kafka_client, file_id) .await - .map_err(DatabaseError::Internal)?; + .wrap_err("enqueueing file for delphi scan")?; Ok(file_id) } @@ -264,7 +275,7 @@ impl VersionBuilder { redis: &RedisPool, file_host: &dyn FileHost, kafka_client: &KafkaClientState, - ) -> Result { + ) -> Result { let version = DBVersion { id: self.version_id, project_id: self.project_id, @@ -282,7 +293,10 @@ impl VersionBuilder { components: self.components, }; - version.insert(transaction).await?; + version + .insert(transaction) + .await + .wrap_err("inserting version")?; sqlx::query!( " @@ -293,7 +307,8 @@ impl VersionBuilder { self.project_id as DBProjectId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating project timestamp")?; let VersionBuilder { dependencies, @@ -312,7 +327,8 @@ impl VersionBuilder { file_host, kafka_client, ) - .await?; + .await + .wrap_err("inserting version file")?; } DependencyBuilder::insert_many( @@ -320,7 +336,8 @@ impl VersionBuilder { self.version_id, transaction, ) - .await?; + .await + .wrap_err("inserting version dependencies")?; let loader_versions = loaders .iter() @@ -329,9 +346,13 @@ impl VersionBuilder { version_id, }) .collect_vec(); - DBLoaderVersion::insert_many(loader_versions, transaction).await?; + DBLoaderVersion::insert_many(loader_versions, transaction) + .await + .wrap_err("inserting version loaders")?; - VersionField::insert_many(self.version_fields, transaction).await?; + VersionField::insert_many(self.version_fields, transaction) + .await + .wrap_err("inserting version fields")?; Ok(self.version_id) } @@ -347,7 +368,7 @@ impl DBLoaderVersion { pub async fn insert_many( items: Vec, transaction: &mut PgTransaction<'_>, - ) -> Result<(), DatabaseError> { + ) -> Result<()> { let (loader_ids, version_ids): (Vec<_>, Vec<_>) = items .iter() .map(|l| (l.loader_id.0, l.version_id.0)) @@ -361,7 +382,8 @@ impl DBLoaderVersion { &version_ids[..], ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting version loaders")?; Ok(()) } @@ -397,7 +419,7 @@ impl DBVersion { pub async fn insert( &self, transaction: &mut PgTransaction<'_>, - ) -> Result<(), sqlx::error::Error> { + ) -> Result<()> { sqlx::query!( " INSERT INTO versions ( @@ -429,7 +451,8 @@ impl DBVersion { .expect("serialization shouldn't fail"), ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("inserting version")?; Ok(()) } @@ -438,14 +461,18 @@ impl DBVersion { id: DBVersionId, redis: &RedisPool, transaction: &mut PgTransaction<'_>, - ) -> Result, DatabaseError> { - let result = Self::get(id, &mut *transaction, redis).await?; + ) -> Result> { + let result = Self::get(id, &mut *transaction, redis) + .await + .wrap_err("fetching version")?; let Some(result) = result else { return Ok(None); }; - DBVersion::clear_cache(&result, redis).await?; + DBVersion::clear_cache(&result, redis) + .await + .wrap_err("clearing version cache")?; sqlx::query!( " @@ -456,7 +483,8 @@ impl DBVersion { id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("unlinking version reports")?; sqlx::query!( " @@ -466,7 +494,8 @@ impl DBVersion { id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting version fields")?; sqlx::query!( " @@ -476,7 +505,8 @@ impl DBVersion { id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting version loaders")?; sqlx::query!( " @@ -490,7 +520,8 @@ impl DBVersion { id as DBVersionId ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting version file hashes")?; sqlx::query!( " @@ -500,9 +531,12 @@ impl DBVersion { id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting version files")?; - cleanup_unused_attribution_files_and_groups(transaction).await?; + cleanup_unused_attribution_files_and_groups(transaction) + .await + .wrap_err("cleaning up unused attribution files and groups")?; // Sync dependencies @@ -513,7 +547,8 @@ impl DBVersion { id as DBVersionId, ) .fetch_one(&mut *transaction) - .await?; + .await + .wrap_err("fetching version project")?; sqlx::query!( " @@ -525,7 +560,8 @@ impl DBVersion { project_id.mod_id, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("unlinking version dependencies")?; sqlx::query!( " @@ -533,7 +569,8 @@ impl DBVersion { ", ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting orphaned dependencies")?; sqlx::query!( " @@ -542,7 +579,8 @@ impl DBVersion { id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting version dependencies")?; // delete version @@ -553,7 +591,8 @@ impl DBVersion { id as DBVersionId, ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("deleting version")?; crate::database::models::DBProject::clear_cache( DBProjectId(project_id.mod_id), @@ -561,7 +600,8 @@ impl DBVersion { None, redis, ) - .await?; + .await + .wrap_err("clearing project cache")?; Ok(Some(())) } @@ -570,12 +610,13 @@ impl DBVersion { id: DBVersionId, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { Self::get_many(&[id], executor, redis) .await + .wrap_err("fetching version") .map(|x| x.into_iter().next()) } @@ -583,22 +624,26 @@ impl DBVersion { version_ids: &[DBVersionId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { - Self::get_many_inner(version_ids, exec, redis, true).await + Self::get_many_inner(version_ids, exec, redis, true) + .await + .wrap_err("fetching versions") } pub async fn get_many_uncached<'a, E>( version_ids: &[DBVersionId], exec: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { - Self::get_many_inner(version_ids, exec, redis, false).await + Self::get_many_inner(version_ids, exec, redis, false) + .await + .wrap_err("fetching uncached versions") } async fn get_many_inner<'a, E>( @@ -606,7 +651,7 @@ impl DBVersion { exec: E, redis: &RedisPool, use_cache: bool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Acquire<'a, Database = sqlx::Postgres>, { @@ -615,7 +660,10 @@ impl DBVersion { use_cache, &version_ids.iter().map(|x| x.0).collect::>(), |version_ids| async move { - let mut exec = exec.acquire().await?; + let mut exec = exec + .acquire() + .await + .wrap_err("acquiring database connection")?; let loader_field_enum_value_ids = DashSet::new(); let version_fields: DashMap> = sqlx::query!( @@ -627,6 +675,7 @@ impl DBVersion { &version_ids ) .fetch(&mut exec) + .map_err(eyre::Report::from) .try_fold( DashMap::new(), |acc: DashMap>, m| { @@ -643,10 +692,11 @@ impl DBVersion { } acc.entry(DBVersionId(m.version_id)).or_default().push(qvf); - async move { Ok(acc) } + async move { eyre::Ok(acc) } }, ) - .await?; + .await + .wrap_err("fetching version fields")?; #[derive(Default)] struct VersionLoaderData { @@ -696,7 +746,10 @@ impl DBVersion { (version_id,version_loader_data) } - ).try_collect().await?; + ) + .try_collect() + .await + .wrap_err("fetching version loader data")?; // Fetch all loader fields from any version let loader_fields: Vec = sqlx::query!( @@ -718,7 +771,8 @@ impl DBVersion { optional: m.optional, }) .try_collect() - .await?; + .await + .wrap_err("fetching loader fields")?; let loader_field_enum_values: Vec = sqlx::query!( r#" @@ -745,7 +799,8 @@ impl DBVersion { major: m.major, }) .try_collect() - .await?; + .await + .wrap_err("fetching loader field enum values")?; #[derive(Deserialize)] struct Hash { @@ -774,6 +829,7 @@ impl DBVersion { ", &version_ids ).fetch(&mut exec) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc : DashMap>, m| { let file = File { id: DBFileId(m.id), @@ -790,9 +846,11 @@ impl DBVersion { acc.entry(DBVersionId(m.version_id)) .or_default() .push(file); - async move { Ok(acc) } + async move { eyre::Ok(acc) } } - ).await?; + ) + .await + .wrap_err("fetching version files")?; let hashes: DashMap> = sqlx::query!( " @@ -803,6 +861,7 @@ impl DBVersion { &file_ids.iter().map(|x| x.0).collect::>() ) .fetch(&mut exec) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc: DashMap>, m| { if let Some(found_hash) = m.hash { let hash = Hash { @@ -815,9 +874,10 @@ impl DBVersion { acc.entry(*version_id).or_default().push(hash); } } - async move { Ok(acc) } + async move { eyre::Ok(acc) } }) - .await?; + .await + .wrap_err("fetching version file hashes")?; let dependencies : DashMap> = sqlx::query!( " @@ -827,6 +887,7 @@ impl DBVersion { ", &version_ids ).fetch(&mut exec) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc : DashMap<_,Vec>, m| { let dependency = DependencyQueryResult { id: m.dependency_id, @@ -840,9 +901,11 @@ impl DBVersion { acc.entry(DBVersionId(m.version_id)) .or_default() .push(dependency); - async move { Ok(acc) } + async move { eyre::Ok(acc) } } - ).await?; + ) + .await + .wrap_err("fetching version dependencies")?; let dependency_attributions = crate::queue::file_scan::get_dependency_attributions( @@ -868,6 +931,7 @@ impl DBVersion { &version_ids ) .fetch(&mut exec) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc, v| { let version_id = DBVersionId(v.id); let VersionLoaderData { @@ -963,13 +1027,16 @@ impl DBVersion { }; acc.insert(v.id, query_version); - async move { Ok(acc) } + async move { eyre::Ok(acc) } }) - .await?; + .await + .wrap_err("fetching versions")?; - Ok::<_, DatabaseError>(res) + eyre::Ok(res) }, - ).await?; + ) + .await + .wrap_err("fetching cached versions")?; val.sort(); @@ -982,12 +1049,13 @@ impl DBVersion { version_id: Option, executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres> + Copy, { Self::get_files_from_hash(algo, &[hash], executor, redis) .await + .wrap_err("fetching version file from hash") .map(|x| { x.into_iter() .find_or_first(|x| Some(x.version_id) == version_id) @@ -999,7 +1067,7 @@ impl DBVersion { hashes: &[String], executor: E, redis: &RedisPool, - ) -> Result, DatabaseError> + ) -> Result> where E: crate::database::Executor<'a, Database = sqlx::Postgres>, { @@ -1022,6 +1090,7 @@ impl DBVersion { &file_ids.into_iter().filter_map(|x| x.split('_').last().map(|x| x.as_bytes().to_vec())).collect::>(), ) .fetch(executor) + .map_err(eyre::Report::from) .try_fold(DashMap::new(), |acc, f| { #[derive(Deserialize)] struct Hash { @@ -1054,13 +1123,16 @@ impl DBVersion { acc.insert(key, file); } - async move { Ok(acc) } + async move { eyre::Ok(acc) } }) - .await?; + .await + .wrap_err("fetching version files from hashes")?; - Ok::<_, DatabaseError>(files) + eyre::Ok(files) } - ).await?; + ) + .await + .wrap_err("fetching cached version files")?; Ok(val) } @@ -1068,8 +1140,11 @@ impl DBVersion { pub async fn clear_cache( version: &VersionQueryResult, redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear version cache")?; let mut keys = vec![redis.key().entity(VERSIONS_NAMESPACE, version.inner.id.0)]; keys.extend(version.files.iter().flat_map(|file| { @@ -1081,21 +1156,30 @@ impl DBVersion { }) })); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing version cache")?; Ok(()) } pub async fn clear_cache_ids( version_ids: &[DBVersionId], redis: &RedisPool, - ) -> Result<(), DatabaseError> { - let mut redis = redis.connect().await?; + ) -> Result<()> { + let mut redis = redis + .connect() + .await + .wrap_err("connecting to redis to clear version caches")?; let keys = version_ids .iter() .map(|id| redis.key().entity(VERSIONS_NAMESPACE, id.0)) .collect::>(); - redis.delete_many(&keys).await?; + redis + .delete_many(&keys) + .await + .wrap_err("clearing version caches")?; Ok(()) } } diff --git a/apps/labrinth/src/models/v2/projects.rs b/apps/labrinth/src/models/v2/projects.rs index c4501b7a8e..a65eae6a40 100644 --- a/apps/labrinth/src/models/v2/projects.rs +++ b/apps/labrinth/src/models/v2/projects.rs @@ -3,9 +3,7 @@ use std::convert::TryFrom; use std::collections::HashMap; use super::super::ids::OrganizationId; -use crate::database::models::{ - DBProjectDisclosure, DBProjectId, DatabaseError, version_item, -}; +use crate::database::models::{DBProjectDisclosure, DBProjectId, version_item}; use crate::models::disclosures::ProjectDisclosureType; use crate::models::ids::{ProjectId, TeamId, ThreadId, VersionId}; use crate::models::projects::{ @@ -15,6 +13,7 @@ use crate::models::projects::{ use crate::routes::v2_reroute::{self, capitalize_first}; use ariadne::ids::UserId; use chrono::{DateTime, Utc}; +use eyre::{Result, WrapErr}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use validator::Validate; @@ -245,7 +244,7 @@ impl LegacyProject { data: Vec, pool: &crate::database::PgPool, redis: &RedisPool, - ) -> Result, DatabaseError> { + ) -> Result> { let version_ids: Vec<_> = data .iter() .filter_map(|p| p.versions.first().map(|i| (*i).into())) @@ -255,13 +254,15 @@ impl LegacyProject { let example_versions = version_item::DBVersion::get_many(&version_ids, pool, redis) - .await?; + .await + .wrap_err("fetching example versions for legacy projects")?; let archived_disclosure_ids = DBProjectDisclosure::projects_with_type( ProjectDisclosureType::Archived, &project_ids, pool, ) - .await?; + .await + .wrap_err("fetching archived disclosures for legacy projects")?; let mut legacy_projects = Vec::new(); for project in data { diff --git a/apps/labrinth/src/queue/billing.rs b/apps/labrinth/src/queue/billing.rs index 504720adb4..11a6578c8a 100644 --- a/apps/labrinth/src/queue/billing.rs +++ b/apps/labrinth/src/queue/billing.rs @@ -1,4 +1,5 @@ use crate::database::models::charge_item::DBCharge; +use crate::database::models::ids::*; use crate::database::models::notification_item::NotificationBuilder; use crate::database::models::product_item::DBProduct; use crate::database::models::products_tax_identifier_item::DBProductsTaxIdentifier; @@ -6,7 +7,6 @@ use crate::database::models::user_item::DBUser; use crate::database::models::user_subscription_item::DBUserSubscription; use crate::database::models::users_redeemals::UserRedeemal; use crate::database::models::users_subscriptions_affiliations::DBUsersSubscriptionsAffiliations; -use crate::database::models::{DatabaseError, ids::*}; use crate::database::models::{ product_item, user_subscription_item, users_redeemals, }; @@ -90,19 +90,15 @@ async fn update_tax_amounts( ) .await .wrap_api_err("fetching price")? - .ok_or_else(|| { - DatabaseError::Database(sqlx::Error::RowNotFound) - }) - .wrap_internal_err("querying database for `update_tax_amounts`")?; + .wrap_internal_err( + "finding product tax identifier for price", + )?; let product = DBProduct::get_price(charge.price_id, &pg) .await .wrap_internal_err( "fetching product price from database", )? - .ok_or_else(|| { - DatabaseError::Database(sqlx::Error::RowNotFound) - }) .wrap_internal_err( "finding product price in database", )?; @@ -402,10 +398,7 @@ async fn update_anrok_transactions( let tax_id = DBProductsTaxIdentifier::get_price(c.price_id, &mut *txn) .await .wrap_api_err("fetching price")? - .ok_or_else(|| DatabaseError::Database(sqlx::Error::RowNotFound)) - .wrap_internal_err( - "fetching products tax identifier from database", - )?; + .wrap_internal_err("finding product tax identifier for price")?; // Note: if the tax amount that was charged to the customer is *different* than // what it *should* be NOW, we will take on a loss here. diff --git a/apps/labrinth/src/queue/email/templates.rs b/apps/labrinth/src/queue/email/templates.rs index 1ba595cdae..7ac3cd5ee6 100644 --- a/apps/labrinth/src/queue/email/templates.rs +++ b/apps/labrinth/src/queue/email/templates.rs @@ -5,9 +5,7 @@ use crate::database::models::notifications_template_item::{ NotificationTemplate, get_or_set_cached_dynamic_html, }; use crate::database::models::report_item::DBReport; -use crate::database::models::{ - DBOrganization, DBProject, DBUser, DatabaseError, -}; +use crate::database::models::{DBOrganization, DBProject, DBUser}; use crate::env::ENV; use crate::models::v3::notifications::NotificationBody; use crate::routes::ApiError; @@ -177,8 +175,7 @@ pub async fn build_email( let db_user = DBUser::get_id(user_id, &mut *exec, redis) .await .wrap_internal_err("fetching user from database")? - .ok_or(DatabaseError::Database(sqlx::Error::RowNotFound)) - .wrap_internal_err("fetching user from database")?; + .wrap_internal_err("finding email recipient in database")?; let map = [ (USER_NAME, db_user.username), @@ -424,9 +421,8 @@ async fn collect_template_variables( redis, ) .await - .wrap_api_err("fetching email project")? - .ok_or_else(|| DatabaseError::Database(sqlx::Error::RowNotFound)) - .wrap_internal_err("fetching project from database")? + .wrap_internal_err("fetching email project")? + .wrap_internal_err("finding email project in database")? .inner; map.insert(PROJECT_ID, to_base62(project_id.0)); @@ -527,9 +523,8 @@ async fn collect_template_variables( redis, ) .await - .wrap_api_err("fetching email project")? - .ok_or_else(|| DatabaseError::Database(sqlx::Error::RowNotFound)) - .wrap_internal_err("fetching project from database")? + .wrap_internal_err("fetching email project")? + .wrap_internal_err("finding email project in database")? .inner; map.insert(PROJECT_ID, to_base62(project_id.0)); @@ -551,9 +546,8 @@ async fn collect_template_variables( redis, ) .await - .wrap_api_err("fetching email project")? - .ok_or_else(|| DatabaseError::Database(sqlx::Error::RowNotFound)) - .wrap_internal_err("fetching project from database")? + .wrap_internal_err("fetching email project")? + .wrap_internal_err("finding email project in database")? .inner; map.insert(PROJECT_ID, to_base62(project_id.0)); @@ -568,12 +562,7 @@ async fn collect_template_variables( ) .await .wrap_internal_err("fetching user from database")? - .ok_or_else(|| { - DatabaseError::Database(sqlx::Error::RowNotFound) - }) - .wrap_internal_err( - "querying database for `collect_template_variables`", - )?; + .wrap_internal_err("finding new owner user in database")?; map.insert(NEWOWNER_TYPE, "user".to_string()); map.insert(NEWOWNER_TYPE_CAPITALIZED, "User".to_string()); @@ -588,11 +577,8 @@ async fn collect_template_variables( ) .await .wrap_internal_err("fetching organization from database")? - .ok_or_else(|| { - DatabaseError::Database(sqlx::Error::RowNotFound) - }) .wrap_internal_err( - "querying database for `collect_template_variables`", + "finding new owner organization in database", )?; map.insert(NEWOWNER_TYPE, "organization".to_string()); @@ -901,8 +887,7 @@ async fn collect_template_variables( ) .await .wrap_internal_err("fetching user from database")? - .ok_or_else(|| DatabaseError::Database(sqlx::Error::RowNotFound)) - .wrap_internal_err("fetching user from database")?; + .wrap_internal_err("finding server invite sender in database")?; map.insert(SERVERINVITE_INVITER_NAME, inviter.username); map.insert(SERVERINVITE_SERVER_NAME, server_name.clone()); diff --git a/apps/labrinth/src/queue/session.rs b/apps/labrinth/src/queue/session.rs index 7e746412d1..fe598a6e34 100644 --- a/apps/labrinth/src/queue/session.rs +++ b/apps/labrinth/src/queue/session.rs @@ -1,11 +1,12 @@ use crate::database::models::pat_item::DBPersonalAccessToken; use crate::database::models::session_item::DBSession; use crate::database::models::{ - DBOAuthAccessTokenId, DBPatId, DBSessionId, DBUserId, DatabaseError, + DBOAuthAccessTokenId, DBPatId, DBSessionId, DBUserId, }; use crate::database::{PgPool, PgTransaction}; use crate::routes::internal::session::SessionMetadata; use chrono::Utc; +use eyre::{Result, WrapErr as _}; use itertools::Itertools; use std::collections::{HashMap, HashSet}; use tokio::sync::Mutex; @@ -65,11 +66,7 @@ impl AuthQueue { std::mem::replace(&mut *queue, HashSet::with_capacity(len)) } - pub async fn index( - &self, - pool: &PgPool, - redis: &RedisPool, - ) -> Result<(), DatabaseError> { + pub async fn index(&self, pool: &PgPool, redis: &RedisPool) -> Result<()> { let session_queue = self.take_sessions().await; let pat_queue = Self::take_hashset(&self.pat_queue).await; let oauth_access_token_queue = @@ -79,7 +76,10 @@ impl AuthQueue { || !pat_queue.is_empty() || !oauth_access_token_queue.is_empty() { - let mut transaction = pool.begin().await?; + let mut transaction = pool + .begin() + .await + .wrap_err("starting database transaction")?; let mut clear_cache_sessions = Vec::new(); for (id, metadata) in session_queue { @@ -101,7 +101,8 @@ impl AuthQueue { metadata.user_agent, ) .execute(&mut transaction) - .await?; + .await + .wrap_err("updating session last login in database")?; } use futures::TryStreamExt; @@ -115,7 +116,8 @@ impl AuthQueue { .fetch(&mut transaction) .map_ok(|x| (DBSessionId(x.id), x.session, DBUserId(x.user_id))) .try_collect::>() - .await?; + .await + .wrap_err("fetching expired sessions from database")?; for (id, session, user_id) in expired_ids { clear_cache_sessions.push(( @@ -123,10 +125,14 @@ impl AuthQueue { Some(session), Some(user_id), )); - DBSession::remove(id, &mut transaction).await?; + DBSession::remove(id, &mut transaction) + .await + .wrap_err("removing expired session from database")?; } - DBSession::clear_cache(clear_cache_sessions, redis).await?; + DBSession::clear_cache(clear_cache_sessions, redis) + .await + .wrap_err("clearing expired session cache")?; let ids = pat_queue.iter().map(|id| id.0).collect_vec(); let clear_cache_pats = pat_queue @@ -144,16 +150,23 @@ impl AuthQueue { Utc::now(), ) .execute(&mut transaction) - .await?; + .await + .wrap_err("updating personal access token last used in database")?; update_oauth_access_token_last_used( oauth_access_token_queue, &mut transaction, ) - .await?; - - transaction.commit().await?; - DBPersonalAccessToken::clear_cache(clear_cache_pats, redis).await?; + .await + .wrap_err("updating oauth access token last used in database")?; + + transaction + .commit() + .await + .wrap_err("committing database transaction")?; + DBPersonalAccessToken::clear_cache(clear_cache_pats, redis) + .await + .wrap_err("clearing personal access token cache")?; } Ok(()) @@ -163,7 +176,7 @@ impl AuthQueue { async fn update_oauth_access_token_last_used( oauth_access_token_queue: HashSet, transaction: &mut PgTransaction<'_>, -) -> Result<(), DatabaseError> { +) -> Result<()> { let ids = oauth_access_token_queue.iter().map(|id| id.0).collect_vec(); sqlx::query!( " @@ -176,6 +189,7 @@ async fn update_oauth_access_token_last_used( Utc::now() ) .execute(&mut *transaction) - .await?; + .await + .wrap_err("updating oauth access token last used in database")?; Ok(()) } diff --git a/apps/labrinth/src/routes/analytics.rs b/apps/labrinth/src/routes/analytics.rs index 7330f80d59..14800d3e6c 100644 --- a/apps/labrinth/src/routes/analytics.rs +++ b/apps/labrinth/src/routes/analytics.rs @@ -9,7 +9,7 @@ use crate::queue::analytics::AnalyticsQueue; use crate::queue::session::AuthQueue; use crate::routes::ApiError; use crate::util::date::get_current_tenths_of_ms; -use crate::util::error::ApiContext as _; + use crate::util::error::Context; use crate::util::http::HttpClient; use actix_web::{HttpRequest, HttpResponse}; @@ -160,7 +160,7 @@ pub async fn page_view_ingest( &redis, ) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; if let Some(project) = project { view.project_id = project.inner.id.0 as u64; @@ -305,7 +305,7 @@ pub async fn minecraft_server_play_ingest( let project = DBProject::get(&project_id.to_string(), &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_not_found_err("resource not found")?; if project.components.minecraft_server.is_none() { diff --git a/apps/labrinth/src/routes/internal/attribution.rs b/apps/labrinth/src/routes/internal/attribution.rs index 444065345c..3817a573e2 100644 --- a/apps/labrinth/src/routes/internal/attribution.rs +++ b/apps/labrinth/src/routes/internal/attribution.rs @@ -331,7 +331,7 @@ pub async fn list( let project = DBProject::get_id(project_id, pool.as_ref(), redis.as_ref()) .await - .wrap_api_err("fetching attribution project")? + .wrap_internal_err("fetching attribution project")? .wrap_not_found_err("resource not found")?; let (team_member, organization_team_member) = DBTeamMember::get_for_project_permissions( diff --git a/apps/labrinth/src/routes/internal/moderation/mod.rs b/apps/labrinth/src/routes/internal/moderation/mod.rs index 29795cdf2f..5a4336a837 100644 --- a/apps/labrinth/src/routes/internal/moderation/mod.rs +++ b/apps/labrinth/src/routes/internal/moderation/mod.rs @@ -1136,7 +1136,7 @@ pub async fn get_project_meta( let project = database::models::DBProject::get(&project_id, &**pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; if let Some(project) = project { let rows = sqlx::query!( @@ -1405,7 +1405,7 @@ pub async fn acquire_lock( let project = database::models::DBProject::get(&project_id_str, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_not_found_err("resource not found")?; let db_project_id = project.inner.id; @@ -1469,7 +1469,7 @@ pub async fn override_lock( let project = database::models::DBProject::get(&project_id_str, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_not_found_err("resource not found")?; let db_project_id = project.inner.id; @@ -1520,7 +1520,7 @@ pub async fn get_lock_status( let project = database::models::DBProject::get(&project_id_str, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_not_found_err("resource not found")?; let db_project_id = project.inner.id; @@ -1587,7 +1587,7 @@ pub async fn release_lock( let project = database::models::DBProject::get(&project_id_str, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_not_found_err("resource not found")?; let db_project_id = project.inner.id; @@ -1663,7 +1663,7 @@ pub async fn release_lock_beacon( let project = database::models::DBProject::get(&project_id_str, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_not_found_err("resource not found")?; let db_project_id = project.inner.id; diff --git a/apps/labrinth/src/routes/internal/statuses.rs b/apps/labrinth/src/routes/internal/statuses.rs index 03bd1a9e1c..8fa64865b2 100644 --- a/apps/labrinth/src/routes/internal/statuses.rs +++ b/apps/labrinth/src/routes/internal/statuses.rs @@ -26,6 +26,7 @@ use ariadne::networking::message::{ use ariadne::users::UserStatus; use chrono::Utc; use either::Either; +use eyre::Result; use futures_util::future::select; use futures_util::{StreamExt, TryStreamExt}; use serde::Deserialize; @@ -45,7 +46,7 @@ struct LauncherHeartbeatInit { } // TODO: Move launcher-specific tunnel traffic to a proper launcher websocket endpoint. -/// Start launcher socket. +/// Start launcher socket. #[utoipa::path( tag = "statuses", responses((status = 101)) @@ -404,11 +405,13 @@ pub async fn ws_init( pub async fn broadcast_friends_message( redis: &RedisPool, message: RedisFriendsMessage, -) -> Result<(), crate::database::models::DatabaseError> { +) -> Result<()> { redis .publish(FRIENDS_CHANNEL_NAME, message) .await - .map_err(Into::into) + .wrap_err("publishing friends message to redis")?; + + Ok(()) } pub async fn broadcast_to_local_friends( @@ -416,15 +419,15 @@ pub async fn broadcast_to_local_friends( message: ServerToClientMessage, ro_pool: &ReadOnlyPgPool, sockets: &ActiveSockets, -) -> Result<(), crate::database::models::DatabaseError> { - broadcast_to_known_local_friends( - user_id, - message, - sockets, +) -> Result<()> { + let friends = DBFriend::get_user_friends(user_id.into(), Some(true), &**ro_pool) - .await?, - ) - .await + .await + .wrap_err("fetching user friends from database")?; + + broadcast_to_known_local_friends(user_id, message, sockets, friends) + .await + .wrap_err("broadcasting message to local friends") } async fn broadcast_to_known_local_friends( @@ -432,9 +435,7 @@ async fn broadcast_to_known_local_friends( message: ServerToClientMessage, sockets: &ActiveSockets, friends: Vec, -) -> Result<(), crate::database::models::DatabaseError> { - // FIXME Probably shouldn't be using database errors for this. Maybe ApiError? - +) -> Result<()> { for friend in friends { let friend_id = if friend.user_id == user_id.into() { friend.friend_id @@ -460,7 +461,7 @@ async fn broadcast_to_known_local_friends( pub async fn send_message( socket: &ActiveSocket, message: &ServerToClientMessage, -) -> Result<(), crate::database::models::DatabaseError> { +) -> Result<()> { let mut socket = socket.socket.clone(); // FIXME Probably shouldn't swallow sending errors @@ -477,11 +478,13 @@ pub async fn send_message_to_user( db: &ActiveSockets, user: UserId, message: &ServerToClientMessage, -) -> Result<(), crate::database::models::DatabaseError> { +) -> Result<()> { if let Some(socket_ids) = db.sockets_by_user_id.get(&user) { for socket_id in socket_ids.iter() { if let Some(socket) = db.sockets.get(&socket_id) { - send_message(&socket, message).await?; + send_message(&socket, message) + .await + .wrap_err("sending websocket message to user")?; } } } @@ -493,8 +496,9 @@ pub async fn send_notification_to_user( db: &ActiveSockets, user: UserId, notification: &Notification, -) -> Result<(), crate::database::models::DatabaseError> { - let message = serde_json::to_string(notification)?; +) -> Result<()> { + let message = serde_json::to_string(notification) + .wrap_err("serializing websocket notification")?; if let Some(socket_ids) = db.sockets_by_user_id.get(&user) { for socket_id in socket_ids.iter() { @@ -513,7 +517,7 @@ pub async fn close_socket( ro_pool: &ReadOnlyPgPool, db: &ActiveSockets, redis: &RedisPool, -) -> Result<(), crate::database::models::DatabaseError> { +) -> Result<()> { if let Some((_, socket)) = db.sockets.remove(&id) { let user_id = socket.status.user_id; db.sockets_by_user_id.remove_if(&user_id, |_, sockets| { @@ -523,12 +527,15 @@ pub async fn close_socket( let _ = socket.socket.close(None).await; - replace_user_status(Some(&socket.status), None, redis).await?; + replace_user_status(Some(&socket.status), None, redis) + .await + .wrap_err("removing user status from redis")?; broadcast_friends_message( redis, RedisFriendsMessage::UserOffline { user: user_id }, ) - .await?; + .await + .wrap_err("broadcasting user offline status")?; for owned_socket in socket.owned_tunnel_sockets { let Some((_, tunnel_socket)) = diff --git a/apps/labrinth/src/routes/maven.rs b/apps/labrinth/src/routes/maven.rs index 369eaf6d1f..7ce9031f0d 100644 --- a/apps/labrinth/src/routes/maven.rs +++ b/apps/labrinth/src/routes/maven.rs @@ -89,7 +89,7 @@ pub async fn maven_metadata( let Some(project) = database::models::DBProject::get(&project_id, &**pool, &redis) .await - .wrap_api_err("fetching Maven project")? + .wrap_internal_err("fetching Maven project")? else { return Err(ApiError::NotFound(eyre::eyre!("resource not found"))); }; @@ -327,7 +327,7 @@ pub async fn version_file( let Some(project) = database::models::DBProject::get(&project_id, &**pool, &redis) .await - .wrap_api_err("fetching Maven project")? + .wrap_internal_err("fetching Maven project")? else { return Err(ApiError::NotFound(eyre::eyre!("resource not found"))); }; @@ -414,7 +414,7 @@ pub async fn version_file_sha1( let Some(project) = database::models::DBProject::get(&project_id, &**pool, &redis) .await - .wrap_api_err("fetching Maven project")? + .wrap_internal_err("fetching Maven project")? else { return Err(ApiError::NotFound(eyre::eyre!("resource not found"))); }; @@ -480,7 +480,7 @@ pub async fn version_file_sha512( let Some(project) = database::models::DBProject::get(&project_id, &**pool, &redis) .await - .wrap_api_err("fetching Maven project")? + .wrap_internal_err("fetching Maven project")? else { return Err(ApiError::NotFound(eyre::eyre!("resource not found"))); }; diff --git a/apps/labrinth/src/routes/updates.rs b/apps/labrinth/src/routes/updates.rs index 95db80199d..ee43da2a85 100644 --- a/apps/labrinth/src/routes/updates.rs +++ b/apps/labrinth/src/routes/updates.rs @@ -58,7 +58,7 @@ pub async fn forge_updates( let project = database::models::DBProject::get(&id, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| ERROR.to_string())?; let user_option = get_user_from_headers( diff --git a/apps/labrinth/src/routes/v2/projects.rs b/apps/labrinth/src/routes/v2/projects.rs index 23691141bd..26d30cfc46 100644 --- a/apps/labrinth/src/routes/v2/projects.rs +++ b/apps/labrinth/src/routes/v2/projects.rs @@ -628,7 +628,7 @@ pub async fn project_edit( let fetched_example_project = project_item::DBProject::get(&info.0, &**pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; let donation_links = fetched_example_project .map(|x| { x.urls @@ -704,7 +704,7 @@ pub async fn project_edit( &redis, ) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; let version_ids = project_item.map(|x| x.versions).unwrap_or_default(); let versions = version_item::DBVersion::get_many(&version_ids, &**pool, &redis) diff --git a/apps/labrinth/src/routes/v3/analytics_get/mod.rs b/apps/labrinth/src/routes/v3/analytics_get/mod.rs index 71679c1e54..72576c58e8 100644 --- a/apps/labrinth/src/routes/v3/analytics_get/mod.rs +++ b/apps/labrinth/src/routes/v3/analytics_get/mod.rs @@ -554,7 +554,7 @@ async fn fetch_response_projects( let project_ids = project_ids.into_iter().collect::>(); let projects = DBProject::get_many_ids(&project_ids, pool, redis) .await - .wrap_api_err("fetching analytics projects")?; + .wrap_internal_err("fetching analytics projects")?; let visible_project_ids = filter_visible_project_ids( projects.iter().map(|project| &project.inner).collect(), &Some(user.clone()), @@ -876,7 +876,7 @@ async fn filter_allowed_project_ids( ) -> Result, ApiError> { let projects = DBProject::get_many_ids(project_ids, pool, redis) .await - .wrap_api_err("fetching projects for analytics authorization")?; + .wrap_internal_err("fetching projects for analytics authorization")?; let team_ids = projects .iter() diff --git a/apps/labrinth/src/routes/v3/collections.rs b/apps/labrinth/src/routes/v3/collections.rs index b5e5d266a8..c2426866eb 100644 --- a/apps/labrinth/src/routes/v3/collections.rs +++ b/apps/labrinth/src/routes/v3/collections.rs @@ -374,7 +374,7 @@ pub async fn collection_edit( project_id, &**pool, &redis, ) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { eyre!("the specified project `{project_id}` does not exist") })?; diff --git a/apps/labrinth/src/routes/v3/images.rs b/apps/labrinth/src/routes/v3/images.rs index 7e45da50c1..5c6533cd28 100644 --- a/apps/labrinth/src/routes/v3/images.rs +++ b/apps/labrinth/src/routes/v3/images.rs @@ -82,7 +82,7 @@ pub async fn images_add( let project = project_item::DBProject::get(&id, &**pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; if let Some(project) = project { if is_team_member_project( &project.inner, diff --git a/apps/labrinth/src/routes/v3/oauth_clients.rs b/apps/labrinth/src/routes/v3/oauth_clients.rs index 039045f28d..02c38fcc1b 100644 --- a/apps/labrinth/src/routes/v3/oauth_clients.rs +++ b/apps/labrinth/src/routes/v3/oauth_clients.rs @@ -1,5 +1,6 @@ use crate::util::error::ApiContext as _; use crate::util::error::Context as _; +use eyre::Result; use std::{collections::HashSet, fmt::Display}; use xredis::RedisPool; @@ -11,7 +12,7 @@ use crate::util::img::{delete_old_images, upload_image_optimized}; use crate::{ auth::{checks::ValidateAuthorized, get_user_from_headers}, database::models::{ - DBOAuthClientId, DBUser, DatabaseError, generate_oauth_client_id, + DBOAuthClientId, DBUser, generate_oauth_client_id, generate_oauth_redirect_id, oauth_client_authorization_item::DBOAuthClientAuthorization, oauth_client_item::{DBOAuthClient, DBOAuthRedirectUri}, @@ -106,7 +107,7 @@ pub async fn get_user_clients( } } -/// Get an OAuth client. +/// Get an OAuth client. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -128,7 +129,7 @@ pub async fn get_client( } } -/// List OAuth clients. +/// List OAuth clients. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -179,7 +180,7 @@ pub struct NewOAuthApp { pub description: Option, } -/// Create an OAuth client. +/// Create an OAuth client. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -246,7 +247,7 @@ pub async fn oauth_client_create( })) } -/// Delete an OAuth client. +/// Delete an OAuth client. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -315,7 +316,7 @@ pub struct OAuthClientEdit { pub description: Option>, } -/// Update an OAuth client. +/// Update an OAuth client. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -413,7 +414,7 @@ pub struct Extension { pub ext: String, } -/// Update an OAuth client icon. +/// Update an OAuth client icon. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -510,7 +511,7 @@ pub async fn oauth_client_icon_edit( Ok(HttpResponse::NoContent().body("")) } -/// Delete an OAuth client icon. +/// Delete an OAuth client icon. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -579,7 +580,7 @@ pub async fn oauth_client_icon_delete( Ok(HttpResponse::NoContent().body("")) } -/// List OAuth authorizations. +/// List OAuth authorizations. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -616,7 +617,7 @@ pub async fn get_user_oauth_authorizations( Ok(HttpResponse::Ok().json(mapped)) } -/// Revoke OAuth authorization. +/// Revoke OAuth authorization. #[utoipa::path( context_path = "/oauth", tag = "oauth clients", @@ -665,10 +666,12 @@ async fn create_redirect_uris( uri_strings: impl IntoIterator, client_id: DBOAuthClientId, transaction: &mut PgTransaction<'_>, -) -> Result, DatabaseError> { +) -> Result> { let mut redirect_uris = vec![]; for uri in uri_strings.into_iter() { - let id = generate_oauth_redirect_id(transaction).await?; + let id = generate_oauth_redirect_id(transaction) + .await + .wrap_err("generating OAuth redirect URI ID")?; redirect_uris.push(DBOAuthRedirectUri { id, client_id, @@ -683,7 +686,7 @@ async fn edit_redirects( redirects: Vec, existing_client: &DBOAuthClient, transaction: &mut PgTransaction<'_>, -) -> Result<(), DatabaseError> { +) -> Result<()> { let updated_redirects: HashSet = redirects.into_iter().collect(); let original_redirects: HashSet = existing_client .redirect_uris @@ -696,9 +699,11 @@ async fn edit_redirects( existing_client.id, &mut *transaction, ) - .await?; + .await + .wrap_err("creating OAuth redirect URIs")?; DBOAuthClient::insert_redirect_uris(&redirects_to_add, &mut *transaction) - .await?; + .await + .wrap_err("inserting OAuth redirect URIs")?; let mut redirects_to_remove = existing_client.redirect_uris.clone(); redirects_to_remove.retain(|r| !updated_redirects.contains(&r.uri)); @@ -706,7 +711,8 @@ async fn edit_redirects( redirects_to_remove.iter().map(|r| r.id), &mut *transaction, ) - .await?; + .await + .wrap_err("removing OAuth redirect URIs")?; Ok(()) } diff --git a/apps/labrinth/src/routes/v3/organizations.rs b/apps/labrinth/src/routes/v3/organizations.rs index 3ff46b06e3..2b5ddd4662 100644 --- a/apps/labrinth/src/routes/v3/organizations.rs +++ b/apps/labrinth/src/routes/v3/organizations.rs @@ -95,7 +95,7 @@ pub async fn organization_projects_get( &redis, ) .await - .wrap_api_err("fetching organization projects")?; + .wrap_internal_err("fetching organization projects")?; let projects = filter_visible_projects(projects_data, ¤t_user, &pool, true) @@ -950,7 +950,7 @@ pub async fn organization_projects_add( &redis, ) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -1132,7 +1132,7 @@ pub async fn organization_projects_remove( let project_item = database::models::DBProject::get(&project_id, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; diff --git a/apps/labrinth/src/routes/v3/project_creation.rs b/apps/labrinth/src/routes/v3/project_creation.rs index 749415e2ec..b22e6f0c64 100644 --- a/apps/labrinth/src/routes/v3/project_creation.rs +++ b/apps/labrinth/src/routes/v3/project_creation.rs @@ -56,10 +56,10 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) { #[derive(Error, Debug)] pub enum CreateError { + #[error(transparent)] + InternalError(#[from] eyre::Report), #[error("An unknown database error occurred")] SqlxDatabaseError(#[from] sqlx::Error), - #[error("Database Error: {0}")] - DatabaseError(#[from] models::DatabaseError), #[error("Error while parsing multipart payload: {0}")] MultipartError(#[from] actix_multipart::MultipartError), #[error("Error while parsing JSON: {0}")] @@ -105,9 +105,7 @@ impl From for CreateError { crate::routes::ApiError::Request(err) => { Self::InvalidInput(format!("{err:#}")) } - err => Self::DatabaseError(models::DatabaseError::SchemaError( - format!("{err:#}"), - )), + err => Self::InternalError(eyre::eyre!("{err:#}")), } } } @@ -115,10 +113,10 @@ impl From for CreateError { impl actix_web::ResponseError for CreateError { fn status_code(&self) -> StatusCode { match self { + CreateError::InternalError(..) => StatusCode::INTERNAL_SERVER_ERROR, CreateError::SqlxDatabaseError(..) => { StatusCode::INTERNAL_SERVER_ERROR } - CreateError::DatabaseError(..) => StatusCode::INTERNAL_SERVER_ERROR, CreateError::FileHostingError(..) => { StatusCode::INTERNAL_SERVER_ERROR } @@ -146,8 +144,8 @@ impl actix_web::ResponseError for CreateError { fn error_response(&self) -> HttpResponse { HttpResponse::build(self.status_code()).json(ApiError { error: match self { + CreateError::InternalError(..) => "database_error", CreateError::SqlxDatabaseError(..) => "database_error", - CreateError::DatabaseError(..) => "database_error", CreateError::FileHostingError(..) => "file_hosting_error", CreateError::SerDeError(..) => "invalid_input", CreateError::MultipartError(..) => "invalid_input", @@ -550,7 +548,7 @@ async fn project_create_inner( ) .fetch_one(&mut *transaction) .await - .map_err(|e| CreateError::DatabaseError(e.into()))?; + .map_err(CreateError::SqlxDatabaseError)?; if results.exists.unwrap_or(false) { return Err(CreateError::SlugCollision); @@ -571,7 +569,7 @@ async fn project_create_inner( ) .fetch_one(&mut *transaction) .await - .map_err(|e| CreateError::DatabaseError(e.into()))?; + .map_err(CreateError::SqlxDatabaseError)?; if results.exists.unwrap_or(false) { return Err(CreateError::SlugCollision); diff --git a/apps/labrinth/src/routes/v3/projects/mod.rs b/apps/labrinth/src/routes/v3/projects/mod.rs index 5acae44de2..03e8235d13 100644 --- a/apps/labrinth/src/routes/v3/projects/mod.rs +++ b/apps/labrinth/src/routes/v3/projects/mod.rs @@ -212,7 +212,7 @@ pub async fn random_projects_get( let projects_data = db_models::DBProject::get_many_ids(&project_ids, &**pool, &redis) .await - .wrap_api_err("fetching projects by ID")? + .wrap_internal_err("fetching projects by ID")? .into_iter() .map(Project::from) .collect::>(); @@ -257,7 +257,7 @@ pub async fn projects_get( .wrap_request_err("deserializing JSON data")?; let projects_data = db_models::DBProject::get_many(&ids, &**pool, &redis) .await - .wrap_api_err("fetching requested projects")?; + .wrap_internal_err("fetching requested projects")?; let user_option = get_user_from_headers( &req, @@ -472,7 +472,7 @@ pub async fn project_edit_internal( let Some(mut project_item) = db_models::DBProject::get(&info.into_inner().0, &**pool, &redis) .await - .wrap_api_err("fetching project")? + .wrap_internal_err("fetching project")? else { return Err(ApiError::NotFound(eyre::eyre!("resource not found"))); }; @@ -944,7 +944,7 @@ pub async fn project_edit_internal( &redis, ) .await - .wrap_api_err("checking project slug availability")?; + .wrap_internal_err("checking project slug availability")?; if existing.is_some() { return Err(ApiError::Request(eyre::eyre!( "Slug collides with other project's id!", @@ -1695,7 +1695,7 @@ pub async fn project_get_check_internal( let project_data = db_models::DBProject::get(&slug, &**pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; if let Some(project) = project_data { Ok(HttpResponse::Ok().json(ProjectCheckResponse { @@ -1742,7 +1742,7 @@ pub async fn dependency_list_internal( let result = db_models::DBProject::get(&string, &***ro_pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; let user_option = get_user_from_headers( &req, @@ -1804,11 +1804,11 @@ pub async fn dependency_list_internal( &redis, ) .await - .wrap_internal_err("failed to fetch dependency versions") + .wrap_err("fetching dependency versions") }, ) .await - .wrap_api_err("fetching project dependencies")?; + .wrap_internal_err("fetching project dependencies")?; let mut projects = filter_visible_projects( projects_result, @@ -1928,7 +1928,7 @@ pub async fn projects_edit( let projects_data = db_models::DBProject::get_many_ids(&project_ids, &**pool, &redis) .await - .wrap_api_err("fetching projects to edit")?; + .wrap_internal_err("fetching projects to edit")?; if let Some(id) = project_ids .iter() @@ -2293,7 +2293,7 @@ pub async fn project_icon_edit_internal( let project_item = db_models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -2446,7 +2446,7 @@ pub async fn delete_project_icon_internal( let project_item = db_models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -2609,7 +2609,7 @@ pub async fn add_gallery_item_internal( let project_item = db_models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -2864,7 +2864,7 @@ pub async fn edit_gallery_item_internal( &redis, ) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -3091,7 +3091,7 @@ pub async fn delete_gallery_item_internal( &redis, ) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -3532,7 +3532,7 @@ pub async fn project_follow_internal( let project = db_models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -3637,7 +3637,7 @@ pub async fn project_unfollow_internal( let project = db_models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; @@ -3725,7 +3725,7 @@ pub async fn project_get_organization( let string = info.into_inner().0; let result = db_models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")? + .wrap_internal_err("fetching project from database")? .wrap_request_err_with(|| { "the specified project does not exist!".to_string() })?; diff --git a/apps/labrinth/src/routes/v3/teams.rs b/apps/labrinth/src/routes/v3/teams.rs index 5cb8e173bb..fe371e404a 100644 --- a/apps/labrinth/src/routes/v3/teams.rs +++ b/apps/labrinth/src/routes/v3/teams.rs @@ -63,7 +63,7 @@ pub async fn team_members_get_project_internal( let project_data = crate::database::models::DBProject::get(&string, &**pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; if let Some(project) = project_data { let current_user = get_user_from_headers( @@ -1049,7 +1049,7 @@ pub async fn transfer_ownership( if let Some(TeamAssociationId::Project(pid)) = team_association_id { let result = DBProject::get_id(pid, &**pool, &redis) .await - .wrap_api_err("fetching project for ownership transfer")?; + .wrap_internal_err("fetching project for ownership transfer")?; if let Some(project_item) = result && project_item.inner.organization_id.is_some() { diff --git a/apps/labrinth/src/routes/v3/threads.rs b/apps/labrinth/src/routes/v3/threads.rs index 4d7d82167d..cebe4a3a1e 100644 --- a/apps/labrinth/src/routes/v3/threads.rs +++ b/apps/labrinth/src/routes/v3/threads.rs @@ -535,7 +535,7 @@ pub async fn thread_send_message_internal( let project = database::models::DBProject::get_id(project_id, pool, redis) .await - .wrap_api_err("fetching thread project")?; + .wrap_internal_err("fetching thread project")?; if let Some(project) = project && project.inner.status != ProjectStatus::Processing diff --git a/apps/labrinth/src/routes/v3/users.rs b/apps/labrinth/src/routes/v3/users.rs index b9016c66a8..47b9af6dc9 100644 --- a/apps/labrinth/src/routes/v3/users.rs +++ b/apps/labrinth/src/routes/v3/users.rs @@ -208,7 +208,7 @@ pub async fn all_projects( let projects_data = crate::database::DBProject::get_many_ids(&project_ids, &**pool, &redis) .await - .wrap_api_err("fetching user and organization projects")?; + .wrap_internal_err("fetching user and organization projects")?; let projects = filter_visible_projects(projects_data, &user, &pool, true) .await .wrap_api_err("filtering visible projects")?; @@ -369,7 +369,7 @@ pub async fn projects_list( &redis, ) .await - .wrap_api_err("fetching organization projects")?; + .wrap_internal_err("fetching organization projects")?; let projects = filter_visible_projects(projects, &user, &pool, true) .await .wrap_api_err("filtering visible projects")?; @@ -1447,7 +1447,7 @@ pub async fn user_follows( &redis, ) .await - .wrap_api_err("fetching followed projects")? + .wrap_internal_err("fetching followed projects")? .into_iter() .map(Project::from) .collect(); diff --git a/apps/labrinth/src/routes/v3/version_file.rs b/apps/labrinth/src/routes/v3/version_file.rs index a773184e91..1cc33557b4 100644 --- a/apps/labrinth/src/routes/v3/version_file.rs +++ b/apps/labrinth/src/routes/v3/version_file.rs @@ -232,7 +232,7 @@ pub async fn get_update_from_hash( &redis, ) .await - .wrap_api_err("fetching project for version file")? + .wrap_internal_err("fetching project for version file")? { let mut versions = database::models::DBVersion::get_many( &project.versions, @@ -443,7 +443,7 @@ pub async fn get_projects_from_hashes( &redis, ) .await - .wrap_api_err("fetching projects for visibility filtering")?, + .wrap_internal_err("fetching projects for visibility filtering")?, &user_option, &pool, false, @@ -730,7 +730,7 @@ pub async fn update_individual_files( &redis, ) .await - .wrap_api_err("fetching projects for version files")?; + .wrap_internal_err("fetching projects for version files")?; let all_versions = database::models::DBVersion::get_many( &projects .iter() diff --git a/apps/labrinth/src/routes/v3/versions.rs b/apps/labrinth/src/routes/v3/versions.rs index 971be40694..1d4cb09af8 100644 --- a/apps/labrinth/src/routes/v3/versions.rs +++ b/apps/labrinth/src/routes/v3/versions.rs @@ -82,7 +82,7 @@ pub async fn version_project_get_helper( ) -> Result { let result = database::models::DBProject::get(&id.0, &***ro_pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; let user_option = get_user_from_headers( &req, @@ -1025,7 +1025,7 @@ pub async fn version_list_internal( let result = database::models::DBProject::get(&string, &***ro_pool, &redis) .await - .wrap_api_err("fetching project from database")?; + .wrap_internal_err("fetching project from database")?; let user_option = get_user_from_headers( &req, diff --git a/apps/labrinth/src/util/webhook.rs b/apps/labrinth/src/util/webhook.rs index 46a110f571..59044dbead 100644 --- a/apps/labrinth/src/util/webhook.rs +++ b/apps/labrinth/src/util/webhook.rs @@ -54,7 +54,7 @@ async fn get_webhook_metadata( redis, ) .await - .wrap_api_err("fetching webhook project")?; + .wrap_internal_err("fetching webhook project")?; if let Some(mut project) = project { let mut owner = None; diff --git a/apps/labrinth/src/validate/mod.rs b/apps/labrinth/src/validate/mod.rs index fed2a8d077..1bb9815e02 100644 --- a/apps/labrinth/src/validate/mod.rs +++ b/apps/labrinth/src/validate/mod.rs @@ -1,5 +1,4 @@ use crate::database::PgTransaction; -use crate::database::models::DatabaseError; use crate::database::models::legacy_loader_fields::MinecraftGameVersion; use crate::database::models::loader_fields::VersionField; use crate::models::pack::PackFormat; @@ -52,8 +51,8 @@ pub enum ValidationError { InvalidInput(std::borrow::Cow<'static, str>), #[error("Error while managing threads")] Blocking(#[from] actix_web::error::BlockingError), - #[error("Error while querying database")] - Database(#[from] DatabaseError), + #[error("internal error while validating uploaded file")] + Internal(#[from] eyre::Report), } #[derive(Eq, PartialEq, Debug)] diff --git a/apps/labrinth/tests/redis.rs b/apps/labrinth/tests/redis.rs index bbd38fea94..607a3836a0 100644 --- a/apps/labrinth/tests/redis.rs +++ b/apps/labrinth/tests/redis.rs @@ -11,7 +11,7 @@ use common::api_common::{ApiProject, ApiVersion}; use common::database::{ENEMY_USER_PAT, USER_USER_PAT}; use common::environment::{TestEnvironment, with_test_environment}; use dashmap::DashMap; -use labrinth::database::models::DatabaseError; +use eyre::Result; use labrinth::database::models::project_item::{ PROJECTS_NAMESPACE, PROJECTS_SLUGS_NAMESPACE, }; @@ -259,7 +259,7 @@ async fn cache_lock_coalesces_concurrent_misses_for_one_key() { for key in keys { values.insert(key.clone(), format!("value-{key}")); } - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ) .await @@ -304,7 +304,7 @@ async fn cache_lock_coalesces_only_overlapping_keys() { .or_insert(1); values.insert(key.clone(), format!("value-{key}")); } - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ) .await @@ -340,7 +340,7 @@ async fn cache_lock_does_not_block_independent_keys() { slow_release.notified().await; let values = DashMap::new(); values.insert(keys[0].clone(), "slow-value".to_string()); - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ) .await @@ -355,7 +355,7 @@ async fn cache_lock_does_not_block_independent_keys() { |keys| async move { let values = DashMap::new(); values.insert(keys[0].clone(), "fast-value".to_string()); - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ), ) @@ -382,8 +382,8 @@ async fn cache_lock_is_released_after_error_and_cancellation() { "error_recovery:v4", &["key".to_string()], |_| async { - Err::, _>(DatabaseError::Internal( - eyre::eyre!("intentional cache fill failure"), + Err::, _>(eyre::eyre!( + "intentional cache fill failure" )) }, ) @@ -398,7 +398,7 @@ async fn cache_lock_is_released_after_error_and_cancellation() { |keys| async move { let values = DashMap::new(); values.insert(keys[0].clone(), "recovered".to_string()); - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ), ) @@ -417,10 +417,8 @@ async fn cache_lock_is_released_after_error_and_cancellation() { &["key".to_string()], move |_| async move { cancelled_started.notify_one(); - std::future::pending::< - Result, DatabaseError>, - >() - .await + std::future::pending::>>() + .await }, ) .await @@ -437,7 +435,7 @@ async fn cache_lock_is_released_after_error_and_cancellation() { |keys| async move { let values = DashMap::new(); values.insert(keys[0].clone(), "recovered".to_string()); - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ), ) @@ -485,7 +483,7 @@ async fn expired_cache_value_serves_waiter_while_writer_refreshes() { writer_release.notified().await; let values = DashMap::new(); values.insert(keys[0].clone(), "fresh".to_string()); - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ) .await @@ -495,8 +493,8 @@ async fn expired_cache_value_serves_waiter_while_writer_refreshes() { let stale = timeout( Duration::from_secs(1), pool.get_cached_keys_raw(namespace, &["key".to_string()], |_| async { - Err::, _>(DatabaseError::Internal( - eyre::eyre!("stale waiter unexpectedly became writer"), + Err::, _>(eyre::eyre!( + "stale waiter unexpectedly became writer" )) }), ) @@ -512,8 +510,8 @@ async fn expired_cache_value_serves_waiter_while_writer_refreshes() { ); let fresh = pool .get_cached_keys_raw(namespace, &["key".to_string()], |_| async { - Err::, _>(DatabaseError::Internal( - eyre::eyre!("fresh value unexpectedly missed cache"), + Err::, _>(eyre::eyre!( + "fresh value unexpectedly missed cache" )) }) .await @@ -551,7 +549,7 @@ async fn case_insensitive_slug_requests_share_one_cache_lock() { canonical_id.to_string(), (Some("MiXeD-Slug".to_string()), "value".to_string()), ); - Ok::<_, DatabaseError>(values) + eyre::Ok(values) }, ) .await diff --git a/packages/xredis/src/cache.rs b/packages/xredis/src/cache.rs index 99e085e873..13a4950081 100644 --- a/packages/xredis/src/cache.rs +++ b/packages/xredis/src/cache.rs @@ -210,7 +210,7 @@ impl CacheManager { P: ConnectionProvider, F: FnOnce(Vec) -> Fut, Fut: Future, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, K: Display + Hash @@ -241,7 +241,7 @@ impl CacheManager { P: ConnectionProvider, F: FnOnce(Vec) -> Fut, Fut: Future, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, K: Display + Hash @@ -288,7 +288,7 @@ impl CacheManager { P: ConnectionProvider, F: FnOnce(Vec) -> Fut, Fut: Future, T)>, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, I: Display + Hash + Eq + PartialEq + Clone + Debug, K: Display @@ -329,7 +329,7 @@ impl CacheManager { P: ConnectionProvider, F: FnOnce(Vec) -> Fut, Fut: Future, T)>, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, I: Display + Hash + Eq + PartialEq + Clone + Debug, K: Display @@ -527,8 +527,9 @@ impl CacheManager { .await .map_err(|_| lock_timeout_error(0, waiters.len())) .wrap_err("waiting to fill Redis cache")?; - let values = - values.wrap_err("fetching values to fill Redis cache")?; + let values = values + .map_err(Into::::into) + .wrap_err("fetching values to fill Redis cache")?; let mut return_values = HashMap::new(); let mut encoded_values = Vec::with_capacity(values.len()); diff --git a/packages/xredis/src/lib.rs b/packages/xredis/src/lib.rs index f1d08ba5ab..55f066f28b 100644 --- a/packages/xredis/src/lib.rs +++ b/packages/xredis/src/lib.rs @@ -123,7 +123,7 @@ impl RedisPool { where F: FnOnce(Vec) -> Fut, Fut: Future, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, K: Display + Hash @@ -139,6 +139,7 @@ impl RedisPool { } else { Ok(closure(keys.to_vec()) .await + .map_err(Into::::into) .wrap_err("fetching uncached values")? .into_iter() .map(|(_, value)| value) @@ -155,7 +156,7 @@ impl RedisPool { where F: FnOnce(Vec) -> Fut, Fut: Future, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, K: Display + Hash @@ -180,7 +181,7 @@ impl RedisPool { where F: FnOnce(Vec) -> Fut, Fut: Future, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, K: Display + Hash @@ -208,7 +209,7 @@ impl RedisPool { where F: FnOnce(Vec) -> Fut, Fut: Future, T)>, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, I: Display + Hash + Eq + PartialEq + Clone + Debug, K: Display @@ -232,6 +233,7 @@ impl RedisPool { } else { Ok(closure(keys.to_vec()) .await + .map_err(Into::::into) .wrap_err("fetching uncached values by slug")? .into_iter() .map(|(_, (_, value))| value) @@ -250,7 +252,7 @@ impl RedisPool { where F: FnOnce(Vec) -> Fut, Fut: Future, T)>, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, I: Display + Hash + Eq + PartialEq + Clone + Debug, K: Display @@ -285,7 +287,7 @@ impl RedisPool { where F: FnOnce(Vec) -> Fut, Fut: Future, T)>, E>>, - E: std::error::Error + Send + Sync + 'static, + E: Into, T: Serialize + DeserializeOwned, I: Display + Hash + Eq + PartialEq + Clone + Debug, K: Display