refactor(manager-core): share resolve_app helper across handlers
apps_api.rs and app_members_api.rs each grew a near-identical local `resolve_app` that parses an id-or-slug param and translates None into their own AppNotFound variant. Promote the lookup half to `app_repo::resolve_app` (returns `Result<Option<AppLookup>, ...>`) and let callers handle the None → not-found mapping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use picloud_shared::{AdminUserId, App, AppId};
|
use picloud_shared::{AdminUserId, App, AppId};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::repo::ScriptRepositoryError;
|
use crate::repo::ScriptRepositoryError;
|
||||||
|
|
||||||
@@ -20,6 +21,32 @@ pub struct AppLookup {
|
|||||||
pub redirected: bool,
|
pub redirected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a free-form path param (UUID *or* slug *or* historical slug)
|
||||||
|
/// to an `AppLookup`. UUID lookups never set `redirected`; slug lookups
|
||||||
|
/// fall through to `app_slug_history` and set `redirected: true` when
|
||||||
|
/// they hit it.
|
||||||
|
///
|
||||||
|
/// Returns `Ok(None)` when nothing matches — callers map that to their
|
||||||
|
/// own not-found error variant.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Propagates any underlying repository error.
|
||||||
|
pub async fn resolve_app(
|
||||||
|
apps: &dyn AppRepository,
|
||||||
|
ident: &str,
|
||||||
|
) -> Result<Option<AppLookup>, ScriptRepositoryError> {
|
||||||
|
if let Ok(uuid) = ident.parse::<Uuid>() {
|
||||||
|
return Ok(apps
|
||||||
|
.get_by_id(AppId::from(uuid))
|
||||||
|
.await?
|
||||||
|
.map(|app| AppLookup {
|
||||||
|
app,
|
||||||
|
redirected: false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
apps.get_by_slug_or_history(ident).await
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AppRepository: Send + Sync {
|
pub trait AppRepository: Send + Sync {
|
||||||
/// Every app on the instance. For owner/admin callers — `member`
|
/// Every app on the instance. For owner/admin callers — `member`
|
||||||
|
|||||||
@@ -454,16 +454,7 @@ async fn resolve_app(
|
|||||||
apps: &dyn AppRepository,
|
apps: &dyn AppRepository,
|
||||||
ident: &str,
|
ident: &str,
|
||||||
) -> Result<crate::app_repo::AppLookup, AppsApiError> {
|
) -> Result<crate::app_repo::AppLookup, AppsApiError> {
|
||||||
if let Ok(uuid) = ident.parse::<Uuid>() {
|
crate::app_repo::resolve_app(apps, ident)
|
||||||
if let Some(app) = apps.get_by_id(AppId::from(uuid)).await? {
|
|
||||||
return Ok(crate::app_repo::AppLookup {
|
|
||||||
app,
|
|
||||||
redirected: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Err(AppsApiError::AppNotFound(ident.to_string()));
|
|
||||||
}
|
|
||||||
apps.get_by_slug_or_history(ident)
|
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppsApiError::AppNotFound(ident.to_string()))
|
.ok_or_else(|| AppsApiError::AppNotFound(ident.to_string()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub use app_members_repo::{
|
|||||||
AppMembersRepository, AppMembersRepositoryError, AppMembershipDetail, AppMembershipRow,
|
AppMembersRepository, AppMembersRepositoryError, AppMembershipDetail, AppMembershipRow,
|
||||||
PostgresAppMembersRepository,
|
PostgresAppMembersRepository,
|
||||||
};
|
};
|
||||||
pub use app_repo::{AppLookup, AppRepository, PostgresAppRepository};
|
pub use app_repo::{resolve_app, AppLookup, AppRepository, PostgresAppRepository};
|
||||||
pub use apps_api::{apps_router, AppsState};
|
pub use apps_api::{apps_router, AppsState};
|
||||||
pub use auth_api::auth_router;
|
pub use auth_api::auth_router;
|
||||||
pub use auth_bootstrap::{
|
pub use auth_bootstrap::{
|
||||||
|
|||||||
Reference in New Issue
Block a user