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