Files
movies-diary/crates/application/src/users/get_federated_profile_stats.rs

90 lines
3.0 KiB
Rust

//! The federated half of the former `get_profile` (ADR-0004's closed wart).
//! `build_federated_profile_response` (`handlers/users.rs`) calls this with a
//! synthetic `user_id` that has no row in the local `users` table — a federated
//! actor's identity is built entirely from the resolved `FederatedProfile`, so
//! this type carries no `identity` field at all: the old `get_profile` computed
//! one for this path anyway, filled with empty-string sentinels for `username`/
//! `handle`, and the handler never read it (see the closed wart paragraph in
//! `docs/adr/0004-instance-identity.md`). No type here can represent that
//! sentinel, because there is nothing to fill in.
//!
//! A federated actor also has no local pending-follow-request concept, so unlike
//! `get_local_profile` this never computes `pending_followers` — it would always
//! be empty here regardless of `is_own_profile`.
use crate::users::{
deps::GetFederatedProfileStatsDeps,
diary_filter::{feed_sort_to_direction, paged_user_filter},
queries::{GetUserProfileQuery, ProfileView},
};
use domain::{
errors::DomainError,
models::{DiaryEntry, UserStats, UserTrends, collections::Paginated},
value_objects::UserId,
};
pub struct FederatedProfileStats {
pub stats: UserStats,
pub entries: Option<Paginated<DiaryEntry>>,
pub history: Option<Vec<DiaryEntry>>,
pub trends: Option<UserTrends>,
pub following_count: usize,
pub followers_count: usize,
}
pub async fn execute(
deps: &GetFederatedProfileStatsDeps,
query: GetUserProfileQuery,
) -> Result<FederatedProfileStats, DomainError> {
let user_id = UserId::from_uuid(query.user_id);
let stats = deps.stats.get_user_stats(&user_id).await?;
let following_count = deps
.social_query
.count_following(&user_id)
.await
.unwrap_or(0);
let followers_count = deps
.social_query
.count_followers(&user_id)
.await
.unwrap_or(0);
let base = |entries, history, trends| FederatedProfileStats {
stats,
entries,
history,
trends,
following_count,
followers_count,
};
match query.view {
ProfileView::History => {
let all_entries = deps.diary.get_user_history(&user_id).await?;
Ok(base(None, Some(all_entries), None))
}
ProfileView::Trends => {
let trends = deps.stats.get_user_trends(&user_id).await?;
Ok(base(None, None, Some(trends)))
}
ProfileView::Ratings | ProfileView::Recent => {
let sort_direction = feed_sort_to_direction(query.sort_by);
let filter = paged_user_filter(
user_id,
sort_direction,
query.limit,
query.offset,
query.search.clone(),
query.include_remote,
)?;
let entries = deps.diary.query_diary(&filter).await?;
Ok(base(Some(entries), None, None))
}
}
}
#[cfg(test)]
#[path = "tests/get_federated_profile_stats.rs"]
mod tests;