mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-08-04 06:40:29 +08:00
feat[Go]: implement /api/v1/agents/<agent_id> and test_db_connection (#15771)
### What problem does this PR solve? Add two API in go ``` /api/v1/agents/test_db_connection POST /api/v1/agents/<agent_id>/sessions DELETE ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
This commit is contained in:
@@ -19,6 +19,7 @@ package handler
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -307,6 +308,89 @@ func (h *AgentHandler) DeleteAgentSessionItem(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
type deleteAgentSessionsRequest struct {
|
||||
IDs []string `json:"ids"`
|
||||
DeleteAll bool `json:"delete_all,omitempty"`
|
||||
}
|
||||
|
||||
func (h *AgentHandler) DeleteAgentSessions(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
agentID := strings.TrimSpace(c.Param("agent_id"))
|
||||
if agentID == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeOperatingError,
|
||||
"data": nil,
|
||||
"message": "agent_id is required",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req deleteAgentSessionsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeBadRequest,
|
||||
"data": false,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
result, code, err := h.agentService.DeleteAgentSessions(strings.TrimSpace(user.ID), agentID, req.IDs, req.DeleteAll)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
response := gin.H{"code": common.CodeSuccess}
|
||||
if result != nil && result.Data != nil {
|
||||
response["data"] = result.Data
|
||||
}
|
||||
if result != nil && result.Message != "" {
|
||||
response["message"] = result.Message
|
||||
}
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// TestDBConnection Test DB connection
|
||||
func (h *AgentHandler) TestDBConnection(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
var req service.TestDBConnectionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil && !errors.Is(err, io.EOF) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeBadRequest,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
code, err := h.agentService.TestDBConnection(user.ID, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeSuccess,
|
||||
"message": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// ListAgentVersions returns versions for a specific agent.
|
||||
// @Summary List Agent Versions
|
||||
// @Description Returns all versions for a specific agent, ordered by update_time DESC.
|
||||
|
||||
@@ -508,6 +508,215 @@ func TestDeleteAgentSessionItemHandlerIgnoresSessionFromAnotherAgent(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgentSessionsHandlerDeletesDuplicateIDsPartially(t *testing.T) {
|
||||
c, w, db := setupGinContextWithUserAndDB(t, http.MethodDelete, "/api/v1/agents/canvas-1/sessions")
|
||||
c.Request = httptest.NewRequest(http.MethodDelete, "/api/v1/agents/canvas-1/sessions", strings.NewReader(`{"ids":["session-1","session-1"]}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "agent_id", Value: "canvas-1"}}
|
||||
|
||||
db.Create(&entity.UserCanvas{
|
||||
ID: "canvas-1",
|
||||
UserID: "user-1",
|
||||
Title: sptr("Test Agent"),
|
||||
})
|
||||
db.Create(&entity.API4Conversation{
|
||||
ID: "session-1",
|
||||
DialogID: "canvas-1",
|
||||
UserID: "user-1",
|
||||
Message: json.RawMessage(`[]`),
|
||||
Reference: json.RawMessage(`[]`),
|
||||
})
|
||||
|
||||
h := NewAgentHandler(service.NewAgentService(), nil)
|
||||
h.DeleteAgentSessions(c)
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to parse response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeSuccess) {
|
||||
t.Fatalf("expected code %d, got %v: %v", common.CodeSuccess, resp["code"], resp["message"])
|
||||
}
|
||||
data, ok := resp["data"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("expected partial data object, got %T", resp["data"])
|
||||
}
|
||||
if data["success_count"] != float64(1) {
|
||||
t.Fatalf("expected success_count 1, got %v", data["success_count"])
|
||||
}
|
||||
errorsList, ok := data["errors"].([]interface{})
|
||||
if !ok || len(errorsList) != 1 {
|
||||
t.Fatalf("expected one duplicate error, got %v", data["errors"])
|
||||
}
|
||||
if errorsList[0] != "Duplicate session ids: session-1" {
|
||||
t.Fatalf("unexpected duplicate error: %v", errorsList[0])
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&entity.API4Conversation{}).Where("id = ?", "session-1").Count(&count).Error; err != nil {
|
||||
t.Fatalf("failed to count deleted session: %v", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatalf("expected session-1 to be deleted, count=%d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgentSessionsHandlerDeleteAll(t *testing.T) {
|
||||
c, w, db := setupGinContextWithUserAndDB(t, http.MethodDelete, "/api/v1/agents/canvas-1/sessions")
|
||||
c.Request = httptest.NewRequest(http.MethodDelete, "/api/v1/agents/canvas-1/sessions", strings.NewReader(`{"delete_all":true}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "agent_id", Value: "canvas-1"}}
|
||||
|
||||
db.Create(&entity.UserCanvas{
|
||||
ID: "canvas-1",
|
||||
UserID: "user-1",
|
||||
Title: sptr("Test Agent"),
|
||||
})
|
||||
db.Create(&entity.API4Conversation{
|
||||
ID: "session-1",
|
||||
DialogID: "canvas-1",
|
||||
UserID: "user-1",
|
||||
Message: json.RawMessage(`[]`),
|
||||
Reference: json.RawMessage(`[]`),
|
||||
})
|
||||
db.Create(&entity.API4Conversation{
|
||||
ID: "session-2",
|
||||
DialogID: "canvas-1",
|
||||
UserID: "user-1",
|
||||
Message: json.RawMessage(`[]`),
|
||||
Reference: json.RawMessage(`[]`),
|
||||
})
|
||||
db.Create(&entity.API4Conversation{
|
||||
ID: "session-other",
|
||||
DialogID: "canvas-other",
|
||||
UserID: "user-1",
|
||||
Message: json.RawMessage(`[]`),
|
||||
Reference: json.RawMessage(`[]`),
|
||||
})
|
||||
|
||||
h := NewAgentHandler(service.NewAgentService(), nil)
|
||||
h.DeleteAgentSessions(c)
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to parse response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeSuccess) {
|
||||
t.Fatalf("expected code %d, got %v: %v", common.CodeSuccess, resp["code"], resp["message"])
|
||||
}
|
||||
|
||||
var ownCount int64
|
||||
if err := db.Model(&entity.API4Conversation{}).Where("dialog_id = ?", "canvas-1").Count(&ownCount).Error; err != nil {
|
||||
t.Fatalf("failed to count own sessions: %v", err)
|
||||
}
|
||||
if ownCount != 0 {
|
||||
t.Fatalf("expected all canvas-1 sessions to be deleted, count=%d", ownCount)
|
||||
}
|
||||
|
||||
var otherCount int64
|
||||
if err := db.Model(&entity.API4Conversation{}).Where("id = ?", "session-other").Count(&otherCount).Error; err != nil {
|
||||
t.Fatalf("failed to count other session: %v", err)
|
||||
}
|
||||
if otherCount != 1 {
|
||||
t.Fatalf("expected other agent session to remain, count=%d", otherCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAgentSessionsHandlerRequiresOwner(t *testing.T) {
|
||||
c, w, db := setupGinContextWithUserAndDB(t, http.MethodDelete, "/api/v1/agents/canvas-1/sessions")
|
||||
c.Request = httptest.NewRequest(http.MethodDelete, "/api/v1/agents/canvas-1/sessions", strings.NewReader(`{"ids":["session-1"]}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "agent_id", Value: "canvas-1"}}
|
||||
|
||||
db.Create(&entity.UserCanvas{
|
||||
ID: "canvas-1",
|
||||
UserID: "user-2",
|
||||
Permission: "team",
|
||||
Title: sptr("Team Agent"),
|
||||
})
|
||||
db.Create(&entity.API4Conversation{
|
||||
ID: "session-1",
|
||||
DialogID: "canvas-1",
|
||||
UserID: "user-1",
|
||||
Message: json.RawMessage(`[]`),
|
||||
Reference: json.RawMessage(`[]`),
|
||||
})
|
||||
|
||||
h := NewAgentHandler(service.NewAgentService(), nil)
|
||||
h.DeleteAgentSessions(c)
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to parse response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeDataError) {
|
||||
t.Fatalf("expected code %d, got %v: %v", common.CodeDataError, resp["code"], resp["message"])
|
||||
}
|
||||
|
||||
var count int64
|
||||
if err := db.Model(&entity.API4Conversation{}).Where("id = ?", "session-1").Count(&count).Error; err != nil {
|
||||
t.Fatalf("failed to count session: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("expected session to remain, count=%d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestDBConnectionHandlerMissingFields(t *testing.T) {
|
||||
c, w, _ := setupGinContextWithUserAndDB(t, http.MethodPost, "/api/v1/agents/test_db_connection")
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/api/v1/agents/test_db_connection", strings.NewReader(`{"db_type":"mysql"}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
h := NewAgentHandler(service.NewAgentService(), nil)
|
||||
h.TestDBConnection(c)
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to parse response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeArgumentError) {
|
||||
t.Fatalf("expected code %d, got %v: %v", common.CodeArgumentError, resp["code"], resp["message"])
|
||||
}
|
||||
if resp["data"] != nil {
|
||||
t.Fatalf("expected nil data, got %v", resp["data"])
|
||||
}
|
||||
want := "required argument are missing: database,username,host,port,password; "
|
||||
if resp["message"] != want {
|
||||
t.Fatalf("expected message %q, got %v", want, resp["message"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestDBConnectionHandlerRejectsLocalhost(t *testing.T) {
|
||||
c, w, _ := setupGinContextWithUserAndDB(t, http.MethodPost, "/api/v1/agents/test_db_connection")
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/api/v1/agents/test_db_connection", strings.NewReader(`{
|
||||
"db_type":"mysql",
|
||||
"database":"rag_flow",
|
||||
"username":"root",
|
||||
"host":"localhost",
|
||||
"port":3306,
|
||||
"password":"infini_rag_flow"
|
||||
}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
h := NewAgentHandler(service.NewAgentService(), nil)
|
||||
h.TestDBConnection(c)
|
||||
|
||||
var resp map[string]interface{}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("failed to parse response: %v", err)
|
||||
}
|
||||
if resp["code"] != float64(common.CodeDataError) {
|
||||
t.Fatalf("expected code %d, got %v: %v", common.CodeDataError, resp["code"], resp["message"])
|
||||
}
|
||||
if resp["data"] != nil {
|
||||
t.Fatalf("expected nil data, got %v", resp["data"])
|
||||
}
|
||||
message, ok := resp["message"].(string)
|
||||
if !ok || !strings.Contains(message, "non-public address") {
|
||||
t.Fatalf("expected non-public host message, got %v", resp["message"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateAgentTagsHandlerSuccess(t *testing.T) {
|
||||
c, w, db := setupGinContextWithUserAndDB(t, http.MethodPut, "/api/v1/agents/canvas-1/tags")
|
||||
c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/agents/canvas-1/tags", strings.NewReader(`{"tags":["alpha","beta","alpha"]}`))
|
||||
|
||||
Reference in New Issue
Block a user