feat: implement user follow/unfollow functionality and thought retrieval by user

- Added follow and unfollow endpoints for users.
- Implemented logic to retrieve thoughts by a specific user.
- Updated user error handling to include cases for already following and not following.
- Created persistence layer for follow relationships.
- Enhanced user and thought schemas to support new features.
- Added tests for follow/unfollow endpoints and thought retrieval.
- Updated frontend to display thoughts and allow posting new thoughts.
This commit is contained in:
2025-09-05 19:08:37 +02:00
parent 912259ef54
commit decf81e535
31 changed files with 872 additions and 155 deletions

View File

@@ -4,8 +4,10 @@ use validator::Validate;
#[derive(Deserialize, Validate, ToSchema)]
pub struct CreateThoughtParams {
pub author_id: i32,
#[validate(length(min = 1, max = 128))]
#[validate(length(
min = 1,
max = 128,
message = "Content must be between 1 and 128 characters"
))]
pub content: String,
}

View File

@@ -1,23 +1,26 @@
use crate::domains::thought;
use crate::domains::{thought, user};
use common::DateTimeWithTimeZoneWrapper;
use sea_orm::FromQueryResult;
use serde::Serialize;
use utoipa::ToSchema;
#[derive(Serialize, ToSchema)]
#[derive(Serialize, ToSchema, FromQueryResult)]
pub struct ThoughtSchema {
pub id: i32,
pub author_id: i32,
#[schema(example = "frutiger")]
pub author_username: String,
#[schema(example = "This is my first thought! #welcome")]
pub content: String,
pub created_at: DateTimeWithTimeZoneWrapper,
}
impl From<thought::Model> for ThoughtSchema {
fn from(model: thought::Model) -> Self {
impl ThoughtSchema {
pub fn from_models(thought: &thought::Model, author: &user::Model) -> Self {
Self {
id: model.id,
author_id: model.author_id,
content: model.content,
created_at: model.created_at.into(),
id: thought.id,
author_username: author.username.clone(),
content: thought.content.clone(),
created_at: thought.created_at.into(),
}
}
}
@@ -27,10 +30,28 @@ pub struct ThoughtListSchema {
pub thoughts: Vec<ThoughtSchema>,
}
impl From<Vec<thought::Model>> for ThoughtListSchema {
fn from(models: Vec<thought::Model>) -> Self {
impl From<Vec<ThoughtSchema>> for ThoughtListSchema {
fn from(thoughts: Vec<ThoughtSchema>) -> Self {
Self { thoughts }
}
}
#[derive(Debug, FromQueryResult)]
pub struct ThoughtWithAuthor {
pub id: i32,
pub content: String,
pub created_at: sea_orm::prelude::DateTimeWithTimeZone,
pub author_id: i32,
pub author_username: String,
}
impl From<ThoughtWithAuthor> for ThoughtSchema {
fn from(model: ThoughtWithAuthor) -> Self {
Self {
thoughts: models.into_iter().map(ThoughtSchema::from).collect(),
id: model.id,
author_username: model.author_username,
content: model.content,
created_at: model.created_at.into(),
}
}
}

View File

@@ -5,14 +5,14 @@ use crate::domains::user;
#[derive(Serialize, ToSchema)]
pub struct UserSchema {
pub id: u32,
pub id: i32,
pub username: String,
}
impl From<user::Model> for UserSchema {
fn from(user: user::Model) -> Self {
Self {
id: user.id as u32,
id: user.id,
username: user.username,
}
}