add the update chat rest and handler

This commit is contained in:
dal 2025-03-20 15:56:33 -06:00
parent 869eecc80c
commit 365e8429d2
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
4 changed files with 233 additions and 1 deletions

View File

@ -9,6 +9,7 @@ mod get_chat_raw_llm_messages;
mod list_chats;
mod post_chat;
mod sharing;
mod update_chat;
mod update_chats;
pub use delete_chats::delete_chats_route;
@ -16,6 +17,7 @@ pub use get_chat::get_chat_route;
pub use get_chat_raw_llm_messages::get_chat_raw_llm_messages;
pub use list_chats::list_chats_route;
pub use post_chat::post_chat_route;
pub use update_chat::update_chat_route;
pub use update_chats::update_chats_route;
pub fn router() -> Router {
@ -25,6 +27,7 @@ pub fn router() -> Router {
.route("/", put(update_chats_route))
.route("/", delete(delete_chats_route))
.route("/:id", get(get_chat_route))
.route("/:id", put(update_chat_route))
.route("/:id/raw_llm_messages", get(get_chat_raw_llm_messages))
.nest("/:id/sharing", sharing::router())
}

View File

@ -0,0 +1,89 @@
use anyhow::Result;
use axum::extract::Path;
use axum::http::StatusCode;
use axum::Extension;
use axum::Json;
use handlers::chats::update_chats_handler::{ChatUpdate, ChatUpdateResult};
use handlers::chats::update_chats_handler;
use middleware::AuthenticatedUser;
use uuid::Uuid;
use crate::routes::rest::ApiResponse;
/// Update a single chat by ID
///
/// PUT /api/v1/chats/:id
pub async fn update_chat_route(
Path(chat_id): Path<Uuid>,
Extension(user): Extension<AuthenticatedUser>,
Json(update): Json<ChatUpdateRequest>,
) -> Result<ApiResponse<ChatUpdateResult>, (StatusCode, &'static str)> {
let chat_update = ChatUpdate {
id: chat_id,
title: update.title,
};
match update_chats_handler(vec![chat_update], &user.id).await {
Ok(results) => {
if results.is_empty() {
Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to update chat"))
} else if !results[0].success {
match &results[0].error {
Some(error) if error.contains("not found") => {
Err((StatusCode::NOT_FOUND, "Chat not found"))
}
Some(error) if error.contains("permission") => {
Err((StatusCode::FORBIDDEN, "You don't have permission to update this chat"))
}
_ => Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to update chat")),
}
} else {
let result = ChatUpdateResult {
id: results[0].id,
success: results[0].success,
error: results[0].error.clone(),
};
Ok(ApiResponse::JsonData(result))
}
}
Err(e) => {
tracing::error!("Error updating chat {}: {}", chat_id, e);
Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to update chat"))
}
}
}
#[derive(Debug, serde::Deserialize)]
pub struct ChatUpdateRequest {
pub title: String,
}
#[cfg(test)]
mod tests {
use super::*;
use axum::{
body::Body,
http::{Request, StatusCode},
routing::put,
Router,
};
use std::str::FromStr;
use tower::ServiceExt;
#[tokio::test]
async fn test_update_chat_route_success() {
// This test is a simplified unit test example
// More comprehensive integration tests are in the tests directory
let chat_id = Uuid::from_str("00000000-0000-0000-0000-000000000001").unwrap();
let user_id = Uuid::from_str("00000000-0000-0000-0000-000000000002").unwrap();
// Since we can't mock the handler function easily in this context,
// this test would normally use a test database in integration tests
// Here we're just checking the basic structure
let update_request = ChatUpdateRequest {
title: "Updated Title".to_string(),
};
// In a real test implementation, we would set up a proper testing environment
}
}

View File

@ -1,2 +1,3 @@
pub mod sharing;
pub mod get_chat_test;
pub mod get_chat_test;
pub mod update_chat_test;

View File

@ -0,0 +1,139 @@
use anyhow::Result;
use chrono::Utc;
use database::models::Chat;
use diesel::prelude::*;
use std::str::FromStr;
use uuid::Uuid;
use crate::common::{
db::run_test_with_db,
fixtures::{builder::TestFixtureBuilder, chats::CreateChatParams},
http::test_app::{TestApp, TestAppBuilder},
http::client::ApiClient,
};
#[tokio::test]
async fn test_update_chat() -> Result<()> {
run_test_with_db(|db| async move {
// Create test user
let user_id = Uuid::new_v4();
let mut fixture_builder = TestFixtureBuilder::new(db.clone());
let user = fixture_builder.create_user(user_id).await?;
// Create test chat
let chat_id = Uuid::new_v4();
let chat = fixture_builder
.create_chat(CreateChatParams {
id: Some(chat_id),
title: Some("Original Title".to_string()),
created_by: Some(user_id),
..Default::default()
})
.await?;
// Setup test app
let app = TestAppBuilder::new().with_db(db.clone()).build().await?;
let client = ApiClient::new(app.client, user.clone());
// Test updating the chat
let response = client
.put(&format!("/api/v1/chats/{}", chat_id))
.json(&serde_json::json!({
"title": "Updated Title"
}))
.send()
.await?;
// Verify response
assert_eq!(response.status(), 200);
let json = response.json::<serde_json::Value>().await?;
assert_eq!(json["id"], chat_id.to_string());
assert_eq!(json["success"], true);
assert_eq!(json["error"], serde_json::Value::Null);
// Verify database update
let updated_chat = db.with_conn(|conn| async move {
use database::schema::chats;
chats::table
.find(chat_id)
.first::<Chat>(conn)
.await
.map_err(anyhow::Error::from)
}).await?;
assert_eq!(updated_chat.title, "Updated Title");
Ok(())
}).await
}
#[tokio::test]
async fn test_update_chat_not_found() -> Result<()> {
run_test_with_db(|db| async move {
// Create test user
let user_id = Uuid::new_v4();
let mut fixture_builder = TestFixtureBuilder::new(db.clone());
let user = fixture_builder.create_user(user_id).await?;
// Setup test app
let app = TestAppBuilder::new().with_db(db.clone()).build().await?;
let client = ApiClient::new(app.client, user.clone());
// Test updating a non-existent chat
let non_existent_id = Uuid::new_v4();
let response = client
.put(&format!("/api/v1/chats/{}", non_existent_id))
.json(&serde_json::json!({
"title": "Updated Title"
}))
.send()
.await?;
// Verify response
assert_eq!(response.status(), 404);
Ok(())
}).await
}
#[tokio::test]
async fn test_update_chat_unauthorized() -> Result<()> {
run_test_with_db(|db| async move {
// Create two test users
let user1_id = Uuid::new_v4();
let user2_id = Uuid::new_v4();
let mut fixture_builder = TestFixtureBuilder::new(db.clone());
let user1 = fixture_builder.create_user(user1_id).await?;
let user2 = fixture_builder.create_user(user2_id).await?;
// Create test chat owned by user1
let chat_id = Uuid::new_v4();
let chat = fixture_builder
.create_chat(CreateChatParams {
id: Some(chat_id),
title: Some("Original Title".to_string()),
created_by: Some(user1_id),
..Default::default()
})
.await?;
// Setup test app with user2 (who doesn't own the chat)
let app = TestAppBuilder::new().with_db(db.clone()).build().await?;
let client = ApiClient::new(app.client, user2.clone());
// Test user2 trying to update user1's chat
let response = client
.put(&format!("/api/v1/chats/{}", chat_id))
.json(&serde_json::json!({
"title": "Updated Title"
}))
.send()
.await?;
// Verify response (should be forbidden)
assert_eq!(response.status(), 403);
Ok(())
}).await
}