feat(presentation/thoughts): use enrichment use cases — real engagement stats, no hardcoded zeros
This commit is contained in:
@@ -1,15 +1,16 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
deps_struct,
|
||||||
errors::ApiError,
|
errors::ApiError,
|
||||||
extractors::{AuthUser, Deps, FromAppState, OptionalAuthUser},
|
extractors::{AuthUser, Deps, OptionalAuthUser},
|
||||||
handlers::auth::to_user_response,
|
handlers::feed::to_thought_response,
|
||||||
state::AppState,
|
|
||||||
};
|
};
|
||||||
use api_types::{
|
use api_types::{
|
||||||
requests::{CreateThoughtRequest, EditThoughtRequest},
|
requests::{CreateThoughtRequest, EditThoughtRequest},
|
||||||
responses::ErrorResponse,
|
responses::ErrorResponse,
|
||||||
};
|
};
|
||||||
use application::use_cases::thoughts::{
|
use application::use_cases::thoughts::{
|
||||||
create_thought, delete_thought, edit_thought, get_thought, get_thread, CreateThoughtInput,
|
create_thought, delete_thought, edit_thought, get_thread_views, get_thought_view,
|
||||||
|
CreateThoughtInput,
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::Path,
|
extract::Path,
|
||||||
@@ -18,56 +19,20 @@ use axum::{
|
|||||||
Json,
|
Json,
|
||||||
};
|
};
|
||||||
use domain::{
|
use domain::{
|
||||||
ports::{EventPublisher, OutboxWriter, TagRepository, ThoughtRepository, UserRepository},
|
models::feed::{EngagementStats, FeedEntry, ViewerContext},
|
||||||
|
ports::{EngagementRepository, EventPublisher, OutboxWriter, TagRepository, ThoughtRepository, UserRepository},
|
||||||
value_objects::ThoughtId,
|
value_objects::ThoughtId,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub struct ThoughtsDeps {
|
deps_struct!(ThoughtsDeps {
|
||||||
pub thoughts: Arc<dyn ThoughtRepository>,
|
thoughts: ThoughtRepository,
|
||||||
pub users: Arc<dyn UserRepository>,
|
users: UserRepository,
|
||||||
pub tags: Arc<dyn TagRepository>,
|
tags: TagRepository,
|
||||||
pub events: Arc<dyn EventPublisher>,
|
events: EventPublisher,
|
||||||
pub outbox: Arc<dyn OutboxWriter>,
|
outbox: OutboxWriter,
|
||||||
}
|
engagement: EngagementRepository,
|
||||||
|
});
|
||||||
impl FromAppState for ThoughtsDeps {
|
|
||||||
fn from_state(s: &AppState) -> Self {
|
|
||||||
Self {
|
|
||||||
thoughts: s.thoughts.clone(),
|
|
||||||
users: s.users.clone(),
|
|
||||||
tags: s.tags.clone(),
|
|
||||||
events: s.events.clone(),
|
|
||||||
outbox: s.outbox.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn thought_to_json(
|
|
||||||
t: &domain::models::thought::Thought,
|
|
||||||
author: &domain::models::user::User,
|
|
||||||
like_count: i64,
|
|
||||||
boost_count: i64,
|
|
||||||
reply_count: i64,
|
|
||||||
) -> serde_json::Value {
|
|
||||||
serde_json::json!({
|
|
||||||
"id": t.id.as_uuid(),
|
|
||||||
"content": t.content.as_str(),
|
|
||||||
"author": to_user_response(author),
|
|
||||||
"replyToId": t.in_reply_to_id.as_ref().map(|x| x.as_uuid()),
|
|
||||||
"visibility": t.visibility.as_str(),
|
|
||||||
"contentWarning": t.content_warning,
|
|
||||||
"sensitive": t.sensitive,
|
|
||||||
"likeCount": like_count,
|
|
||||||
"boostCount": boost_count,
|
|
||||||
"replyCount": reply_count,
|
|
||||||
"likedByViewer": false,
|
|
||||||
"boostedByViewer": false,
|
|
||||||
"createdAt": t.created_at,
|
|
||||||
"updatedAt": t.updated_at,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
post, path = "/thoughts",
|
post, path = "/thoughts",
|
||||||
@@ -106,10 +71,13 @@ pub async fn post_thought(
|
|||||||
.find_by_id(&uid)
|
.find_by_id(&uid)
|
||||||
.await?
|
.await?
|
||||||
.ok_or(domain::errors::DomainError::NotFound)?;
|
.ok_or(domain::errors::DomainError::NotFound)?;
|
||||||
Ok((
|
let entry = FeedEntry {
|
||||||
StatusCode::CREATED,
|
thought: out.thought,
|
||||||
Json(thought_to_json(&out.thought, &author, 0, 0, 0)),
|
author,
|
||||||
))
|
stats: EngagementStats { like_count: 0, boost_count: 0, reply_count: 0 },
|
||||||
|
viewer: Some(ViewerContext { liked: false, boosted: false }),
|
||||||
|
};
|
||||||
|
Ok((StatusCode::CREATED, Json(to_thought_response(&entry))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -123,15 +91,17 @@ pub async fn post_thought(
|
|||||||
pub async fn get_thought_handler(
|
pub async fn get_thought_handler(
|
||||||
Deps(d): Deps<ThoughtsDeps>,
|
Deps(d): Deps<ThoughtsDeps>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
OptionalAuthUser(_viewer): OptionalAuthUser,
|
OptionalAuthUser(viewer): OptionalAuthUser,
|
||||||
) -> Result<Json<serde_json::Value>, ApiError> {
|
) -> Result<Json<serde_json::Value>, ApiError> {
|
||||||
let thought = get_thought(&*d.thoughts, &ThoughtId::from_uuid(id)).await?;
|
let entry = get_thought_view(
|
||||||
let author = d
|
&*d.thoughts,
|
||||||
.users
|
&*d.users,
|
||||||
.find_by_id(&thought.user_id)
|
&*d.engagement,
|
||||||
.await?
|
&ThoughtId::from_uuid(id),
|
||||||
.ok_or(domain::errors::DomainError::NotFound)?;
|
viewer.as_ref(),
|
||||||
Ok(Json(thought_to_json(&thought, &author, 0, 0, 0)))
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(Json(serde_json::to_value(to_thought_response(&entry)).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
@@ -191,13 +161,19 @@ pub async fn patch_thought(
|
|||||||
pub async fn get_thread_handler(
|
pub async fn get_thread_handler(
|
||||||
Deps(d): Deps<ThoughtsDeps>,
|
Deps(d): Deps<ThoughtsDeps>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
|
OptionalAuthUser(viewer): OptionalAuthUser,
|
||||||
) -> Result<Json<Vec<serde_json::Value>>, ApiError> {
|
) -> Result<Json<Vec<serde_json::Value>>, ApiError> {
|
||||||
let thoughts = get_thread(&*d.thoughts, &ThoughtId::from_uuid(id)).await?;
|
let entries = get_thread_views(
|
||||||
let mut items = Vec::new();
|
&*d.thoughts,
|
||||||
for t in &thoughts {
|
&*d.users,
|
||||||
if let Ok(Some(author)) = d.users.find_by_id(&t.user_id).await {
|
&*d.engagement,
|
||||||
items.push(thought_to_json(t, &author, 0, 0, 0));
|
&ThoughtId::from_uuid(id),
|
||||||
}
|
viewer.as_ref(),
|
||||||
}
|
)
|
||||||
|
.await?;
|
||||||
|
let items: Vec<_> = entries
|
||||||
|
.iter()
|
||||||
|
.map(|e| serde_json::to_value(to_thought_response(e)).unwrap())
|
||||||
|
.collect();
|
||||||
Ok(Json(items))
|
Ok(Json(items))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user