mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-21 15:11:08 +08:00
feat(go-api): add chat update endpoints (#16378)
## Summary - Added Go API route `PUT /api/v1/chats/:chat_id` to align with Python `PUT /api/v1/chats/<chat_id>` chat update behavior. - Added Go API route `PATCH /api/v1/chats/:chat_id` to align with Python `PATCH /api/v1/chats/<chat_id>` partial chat update behavior. - Added matching handler and service logic for owner checks, tenant validation, persisted-field filtering, read-only field filtering, `dataset_ids` to `kb_ids` conversion, and PATCH shallow merge semantics for `prompt_config` and `llm_setting`.
This commit is contained in:
@@ -503,3 +503,57 @@ func (h *ChatHandler) GetChat(c *gin.Context) {
|
||||
"message": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateChat updates a chat by ID using REST PUT semantics.
|
||||
func (h *ChatHandler) UpdateChat(c *gin.Context) {
|
||||
h.updateChatByMethod(c, false)
|
||||
}
|
||||
|
||||
// PatchChat updates a chat by ID using REST PATCH semantics.
|
||||
func (h *ChatHandler) PatchChat(c *gin.Context) {
|
||||
h.updateChatByMethod(c, true)
|
||||
}
|
||||
|
||||
func (h *ChatHandler) updateChatByMethod(c *gin.Context, patch bool) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
chatID := c.Param("chat_id")
|
||||
if chatID == "" {
|
||||
jsonError(c, common.CodeBadRequest, "chat_id is required")
|
||||
return
|
||||
}
|
||||
|
||||
var req map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
jsonError(c, common.CodeDataError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
result map[string]interface{}
|
||||
err error
|
||||
)
|
||||
if patch {
|
||||
result, err = h.chatService.PatchChat(user.ID, chatID, req)
|
||||
} else {
|
||||
result, err = h.chatService.UpdateChat(user.ID, chatID, req)
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() == "no authorization" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeAuthenticationError,
|
||||
"data": false,
|
||||
"message": "No authorization.",
|
||||
})
|
||||
return
|
||||
}
|
||||
jsonError(c, common.CodeDataError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(c, common.CodeSuccess, result, "success")
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func setupChatHandlerTestDB(t *testing.T) *gorm.DB {
|
||||
t.Fatalf("failed to open sqlite: %v", err)
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(&entity.Chat{}); err != nil {
|
||||
if err := db.AutoMigrate(&entity.Chat{}, &entity.Tenant{}); err != nil {
|
||||
t.Fatalf("failed to migrate test schema: %v", err)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,20 @@ func setupChatHandlerTestDB(t *testing.T) *gorm.DB {
|
||||
dao.DB = db
|
||||
t.Cleanup(func() { dao.DB = origDB })
|
||||
|
||||
status := string(entity.StatusValid)
|
||||
if err := db.Create(&entity.Tenant{
|
||||
ID: "user-1",
|
||||
LLMID: "model-a",
|
||||
EmbdID: "embd-a",
|
||||
ASRID: "asr-a",
|
||||
Img2TxtID: "img2txt-a",
|
||||
RerankID: "rerank-a",
|
||||
ParserIDs: "naive",
|
||||
Status: &status,
|
||||
}).Error; err != nil {
|
||||
t.Fatalf("failed to create tenant: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -120,3 +134,68 @@ func TestBulkDeleteChatsHandlerPartialSuccess(t *testing.T) {
|
||||
t.Fatalf("unexpected message: %v", resp["message"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchChatHandlerSuccess(t *testing.T) {
|
||||
db := setupChatHandlerTestDB(t)
|
||||
createChatHandlerTestChat(t, db, "chat-1", "user-1")
|
||||
|
||||
h := NewChatHandler(service.NewChatService(), service.NewUserService())
|
||||
c, w := setupGinContextWithUser("PATCH", "/api/v1/chats/chat-1", `{"name":" updated chat "}`)
|
||||
c.Params = []gin.Param{{Key: "chat_id", Value: "chat-1"}}
|
||||
|
||||
h.PatchChat(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeSuccess) {
|
||||
t.Fatalf("expected success code, got %v", resp["code"])
|
||||
}
|
||||
data, ok := resp["data"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("expected object data, got %+v", resp["data"])
|
||||
}
|
||||
if data["name"] != "updated chat" {
|
||||
t.Fatalf("expected trimmed name in response, got %+v", data["name"])
|
||||
}
|
||||
if _, ok := data["kb_ids"]; ok {
|
||||
t.Fatalf("response must not expose kb_ids: %+v", data)
|
||||
}
|
||||
if _, ok := data["dataset_ids"]; !ok {
|
||||
t.Fatalf("response should expose dataset_ids: %+v", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChatHandlerRejectsNonOwner(t *testing.T) {
|
||||
db := setupChatHandlerTestDB(t)
|
||||
createChatHandlerTestChat(t, db, "chat-1", "tenant-2")
|
||||
|
||||
h := NewChatHandler(service.NewChatService(), service.NewUserService())
|
||||
c, w := setupGinContextWithUser("PUT", "/api/v1/chats/chat-1", `{"name":"updated"}`)
|
||||
c.Params = []gin.Param{{Key: "chat_id", Value: "chat-1"}}
|
||||
|
||||
h.UpdateChat(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeAuthenticationError) {
|
||||
t.Fatalf("expected auth error code, got %v", resp["code"])
|
||||
}
|
||||
if resp["data"] != false {
|
||||
t.Fatalf("expected data=false, got %v", resp["data"])
|
||||
}
|
||||
if resp["message"] != "No authorization." {
|
||||
t.Fatalf("unexpected message: %v", resp["message"])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user