mirror of https://github.com/buster-so/buster.git
add the update chat rest and handler
This commit is contained in:
parent
869eecc80c
commit
365e8429d2
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod sharing;
|
||||
pub mod get_chat_test;
|
||||
pub mod get_chat_test;
|
||||
pub mod update_chat_test;
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue