feat: enhance error handling and user follow functionality, update tests for user context

This commit is contained in:
2025-09-05 21:44:46 +02:00
parent decf81e535
commit 0e6c072387
13 changed files with 172 additions and 81 deletions

View File

@@ -18,6 +18,9 @@ impl HTTPError for DbErr {
fn to_status_code(&self) -> StatusCode {
match self {
DbErr::ConnectionAcquire(_) => StatusCode::INTERNAL_SERVER_ERROR,
DbErr::UnpackInsertId => StatusCode::CONFLICT,
DbErr::RecordNotFound(_) => StatusCode::NOT_FOUND,
DbErr::Custom(s) if s == "Users cannot follow themselves" => StatusCode::BAD_REQUEST,
_ => StatusCode::INTERNAL_SERVER_ERROR, // TODO:: more granularity
}
}
@@ -27,7 +30,7 @@ impl HTTPError for UserError {
fn to_status_code(&self) -> StatusCode {
match self {
UserError::NotFound => StatusCode::NOT_FOUND,
UserError::NotFollowing => StatusCode::BAD_REQUEST,
UserError::NotFollowing => StatusCode::NOT_FOUND,
UserError::Forbidden => StatusCode::FORBIDDEN,
UserError::UsernameTaken => StatusCode::BAD_REQUEST,
UserError::AlreadyFollowing => StatusCode::BAD_REQUEST,

View File

@@ -15,7 +15,7 @@ impl FromRequestParts<AppState> for AuthUser {
type Rejection = (StatusCode, &'static str);
async fn from_request_parts(
_parts: &mut Parts,
parts: &mut Parts,
_state: &AppState,
) -> Result<Self, Self::Rejection> {
// For now, we'll just return a hardcoded user.
@@ -24,6 +24,12 @@ impl FromRequestParts<AppState> for AuthUser {
// 2. Validate the JWT.
// 3. Extract the user ID from the token claims.
// 4. Return an error if the token is invalid or missing.
Ok(AuthUser { id: 1 }) // Assume user with ID 1 is always authenticated.
if let Some(user_id_header) = parts.headers.get("x-test-user-id") {
let user_id_str = user_id_header.to_str().unwrap_or("1");
let user_id = user_id_str.parse::<i32>().unwrap_or(1);
return Ok(AuthUser { id: user_id });
} else {
return Ok(AuthUser { id: 1 });
}
}
}

View File

@@ -38,12 +38,15 @@ async fn users_post(
state: State<AppState>,
Valid(Json(params)): Valid<Json<CreateUserParams>>,
) -> Result<impl IntoResponse, ApiError> {
let user = create_user(&state.conn, params)
.await
.map_err(ApiError::from)?;
let user = user.try_into_model().unwrap();
Ok((StatusCode::CREATED, Json(UserSchema::from(user))))
let result = create_user(&state.conn, params).await;
match result {
Ok(user) => {
let user = user.try_into_model().unwrap();
Ok((StatusCode::CREATED, Json(UserSchema::from(user))))
}
Err(DbErr::UnpackInsertId) => Err(UserError::UsernameTaken.into()),
Err(e) => Err(e.into()),
}
}
#[utoipa::path(
@@ -111,6 +114,7 @@ async fn user_thoughts_get(
.ok_or(UserError::NotFound)?;
let thoughts_with_authors = get_thoughts_by_user(&state.conn, user.id).await?;
let thoughts_schema: Vec<ThoughtSchema> = thoughts_with_authors
.into_iter()
.map(ThoughtSchema::from)
@@ -148,7 +152,14 @@ async fn user_follow_post(
match result {
Ok(_) => Ok(StatusCode::NO_CONTENT),
Err(DbErr::UnpackInsertId) => Err(UserError::AlreadyFollowing.into()),
Err(e)
if matches!(
e.sql_err(),
Some(sea_orm::SqlErr::UniqueConstraintViolation { .. })
) =>
{
Err(UserError::AlreadyFollowing.into())
}
Err(e) => Err(e.into()),
}
}