mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-11-10 21:32:43 +08:00
refa: derive service identity from request context (#21923)
This commit is contained in:
1 parent
d2353a8c36
commit
7dd0164fb3
53 files changed
+719
-668
No files matched your search
+16
-68
@@ -34,14 +34,14 @@ import (
|
||||
|
||||
// chunkService is the consumer-side interface for ChunkHandler's service dependency.
|
||||
type chunkService interface {
|
||||
Get(ctx context.Context, req *service.GetChunkRequest, userID string) (*service.GetChunkResponse, error)
|
||||
List(ctx context.Context, req *service.ListChunksRequest, userID string) (*service.ListChunksResponse, error)
|
||||
SwitchChunks(ctx context.Context, userID string, datasetID, documentID string, availableInt int, chunkIDs []string) error
|
||||
UpdateChunk(ctx context.Context, req *service.UpdateChunkRequest, userID string) error
|
||||
RemoveChunks(ctx context.Context, req *service.RemoveChunksRequest, userID string) (int64, error)
|
||||
Parse(ctx context.Context, userID, datasetID string, req *service.ParseFileRequest) (map[string]interface{}, common.ErrorCode, error)
|
||||
AddChunk(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error)
|
||||
StopParsing(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error)
|
||||
Get(ctx context.Context, req *service.GetChunkRequest) (*service.GetChunkResponse, error)
|
||||
List(ctx context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error)
|
||||
SwitchChunks(ctx context.Context, datasetID, documentID string, availableInt int, chunkIDs []string) error
|
||||
UpdateChunk(ctx context.Context, req *service.UpdateChunkRequest) error
|
||||
RemoveChunks(ctx context.Context, req *service.RemoveChunksRequest) (int64, error)
|
||||
Parse(ctx context.Context, datasetID string, req *service.ParseFileRequest) (map[string]interface{}, common.ErrorCode, error)
|
||||
AddChunk(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error)
|
||||
StopParsing(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error)
|
||||
}
|
||||
|
||||
// ChunkHandler chunk handler
|
||||
@@ -68,11 +68,6 @@ func NewChunkHandler(chunkService chunkService, userService *service.UserService
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/datasets/{dataset_id}/documents/{document_id}/chunks/{chunk_id} [get]
|
||||
func (h *ChunkHandler) Get(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
documentID := c.Param("document_id")
|
||||
@@ -89,7 +84,7 @@ func (h *ChunkHandler) Get(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
resp, err := h.chunkService.Get(ctx, req, user.ID)
|
||||
resp, err := h.chunkService.Get(ctx, req)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
@@ -103,17 +98,7 @@ func (h *ChunkHandler) Get(c *gin.Context) {
|
||||
|
||||
// Parse reparse the datasets' files
|
||||
func (h *ChunkHandler) Parse(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
if userID == "" {
|
||||
common.RespondError(c, common.CodeArgumentError, "user_id is required")
|
||||
return
|
||||
}
|
||||
datasetId := strings.TrimSpace(c.Param("dataset_id"))
|
||||
if datasetId == "" {
|
||||
common.Respond(c, http.StatusBadRequest, common.CodeBadRequest, nil, "dataset_id is required")
|
||||
@@ -127,7 +112,7 @@ func (h *ChunkHandler) Parse(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
data, code, err := h.chunkService.Parse(ctx, userID, datasetId, &req)
|
||||
data, code, err := h.chunkService.Parse(ctx, datasetId, &req)
|
||||
if code != common.CodeSuccess {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -138,11 +123,6 @@ func (h *ChunkHandler) Parse(c *gin.Context) {
|
||||
|
||||
// ListChunks retrieves chunks for a document from path/query parameters.
|
||||
func (h *ChunkHandler) ListChunks(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
documentID := c.Param("document_id")
|
||||
@@ -183,7 +163,7 @@ func (h *ChunkHandler) ListChunks(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
resp, err := h.chunkService.List(ctx, &req, user.ID)
|
||||
resp, err := h.chunkService.List(ctx, &req)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
@@ -241,11 +221,6 @@ func parseAvailableQuery(raw string) (int, bool, error) {
|
||||
}
|
||||
|
||||
func (h *ChunkHandler) StopParsing(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
if datasetID == "" {
|
||||
@@ -264,7 +239,7 @@ func (h *ChunkHandler) StopParsing(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
resp, code, err := h.chunkService.StopParsing(ctx, user.ID, datasetID, req)
|
||||
resp, code, err := h.chunkService.StopParsing(ctx, datasetID, req)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -284,17 +259,6 @@ func (h *ChunkHandler) StopParsing(c *gin.Context) {
|
||||
|
||||
// SwitchChunks enable or disable a chunk
|
||||
func (h *ChunkHandler) SwitchChunks(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
if userID == "" {
|
||||
common.Respond(c, http.StatusBadRequest, common.CodeAuthenticationError, nil, "user_id is required")
|
||||
return
|
||||
}
|
||||
|
||||
// Get required ID
|
||||
datasetID := strings.TrimSpace(c.Param("dataset_id"))
|
||||
@@ -333,7 +297,7 @@ func (h *ChunkHandler) SwitchChunks(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
if err = h.chunkService.SwitchChunks(ctx, userID, datasetID, documentID, availableInt, chunkIDs); err != nil {
|
||||
if err = h.chunkService.SwitchChunks(ctx, datasetID, documentID, availableInt, chunkIDs); err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
}
|
||||
@@ -418,11 +382,6 @@ func optionalBodyString(rawBody map[string]interface{}, field, typeMessage strin
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/datasets/{dataset_id}/documents/{document_id}/chunks/{chunk_id} [patch]
|
||||
func (h *ChunkHandler) UpdateChunk(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode the update fields.
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
@@ -535,7 +494,7 @@ func (h *ChunkHandler) UpdateChunk(c *gin.Context) {
|
||||
req.ChunkID = chunkID
|
||||
|
||||
ctx := c.Request.Context()
|
||||
err = h.chunkService.UpdateChunk(ctx, &req, user.ID)
|
||||
err = h.chunkService.UpdateChunk(ctx, &req)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
@@ -566,11 +525,6 @@ func (h *ChunkHandler) UpdateChunk(c *gin.Context) {
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/datasets/{dataset_id}/documents/{document_id}/chunks [delete]
|
||||
func (h *ChunkHandler) RemoveChunks(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
docID := c.Param("document_id")
|
||||
@@ -589,7 +543,7 @@ func (h *ChunkHandler) RemoveChunks(c *gin.Context) {
|
||||
req.DocID = docID
|
||||
|
||||
ctx := c.Request.Context()
|
||||
deletedCount, err := h.chunkService.RemoveChunks(ctx, &req, user.ID)
|
||||
deletedCount, err := h.chunkService.RemoveChunks(ctx, &req)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
@@ -658,13 +612,7 @@ func addChunkResponseMessage(code common.ErrorCode, err error) string {
|
||||
|
||||
func (h *ChunkHandler) AddChunk(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
userID := user.ID
|
||||
datasetID, documentID := strings.TrimSpace(c.Param("dataset_id")), strings.TrimSpace(c.Param("document_id"))
|
||||
|
||||
var rawBody map[string]json.RawMessage
|
||||
@@ -716,7 +664,7 @@ func (h *ChunkHandler) AddChunk(c *gin.Context) {
|
||||
ImageBase64: imageBase64,
|
||||
}
|
||||
|
||||
resp, err := h.chunkService.AddChunk(ctx, &req, userID)
|
||||
resp, err := h.chunkService.AddChunk(ctx, &req)
|
||||
if err != nil {
|
||||
var codedErr service.ErrorCoder
|
||||
if errors.As(err, &codedErr) {
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
func TestChunkUpdateRejectsMalformedSDKFields(t *testing.T) {
|
||||
for _, body := range []string{`{"content":null}`, `{"content":1}`, `{"important_keywords":[1]}`, `{"important_keywords":null}`, `{"questions":"abc"}`, `{"questions":[false]}`, `{"available":1}`, `{"available":"true"}`} {
|
||||
t.Run(body, func(t *testing.T) {
|
||||
mock := &mockChunkSvc{updateChunkFn: func(context.Context, *service.UpdateChunkRequest, string) error {
|
||||
mock := &mockChunkSvc{updateChunkFn: func(context.Context, *service.UpdateChunkRequest) error {
|
||||
t.Error("invalid update reached service")
|
||||
return nil
|
||||
}}
|
||||
@@ -50,7 +50,7 @@ func TestChunkUpdateRejectsMalformedSDKFields(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChunkListAcceptsSDKID(t *testing.T) {
|
||||
mock := &mockChunkSvc{listFn: func(_ context.Context, req *service.ListChunksRequest, _ string) (*service.ListChunksResponse, error) {
|
||||
mock := &mockChunkSvc{listFn: func(_ context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error) {
|
||||
if len(req.ChunkIDs) != 1 || req.ChunkIDs[0] != "sdk-id" {
|
||||
t.Errorf("filter=%v", req.ChunkIDs)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"ragflow/internal/common"
|
||||
"ragflow/internal/entity"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/service"
|
||||
|
||||
@@ -21,12 +20,12 @@ import (
|
||||
// mockChunkSvc implements chunkSvcIface for testing ChunkHandler.
|
||||
// Only the methods actually called by the test are set; others panic.
|
||||
type mockChunkSvc struct {
|
||||
addChunkFn func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error)
|
||||
listFn func(ctx context.Context, req *service.ListChunksRequest, userID string) (*service.ListChunksResponse, error)
|
||||
switchChunksFn func(ctx context.Context, userID, datasetID, documentID string, availableInt int, chunkIDs []string) error
|
||||
updateChunkFn func(ctx context.Context, req *service.UpdateChunkRequest, userID string) error
|
||||
stopParsingFn func(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error)
|
||||
removeChunksFn func(context.Context, *service.RemoveChunksRequest, string) (int64, error)
|
||||
addChunkFn func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error)
|
||||
listFn func(ctx context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error)
|
||||
switchChunksFn func(ctx context.Context, datasetID, documentID string, availableInt int, chunkIDs []string) error
|
||||
updateChunkFn func(ctx context.Context, req *service.UpdateChunkRequest) error
|
||||
stopParsingFn func(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error)
|
||||
removeChunksFn func(context.Context, *service.RemoveChunksRequest) (int64, error)
|
||||
}
|
||||
|
||||
type codedTestError struct {
|
||||
@@ -42,30 +41,30 @@ func (e codedTestError) Code() common.ErrorCode {
|
||||
return e.code
|
||||
}
|
||||
|
||||
func (m *mockChunkSvc) Get(context.Context, *service.GetChunkRequest, string) (*service.GetChunkResponse, error) {
|
||||
func (m *mockChunkSvc) Get(context.Context, *service.GetChunkRequest) (*service.GetChunkResponse, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (m *mockChunkSvc) List(ctx context.Context, req *service.ListChunksRequest, userID string) (*service.ListChunksResponse, error) {
|
||||
func (m *mockChunkSvc) List(ctx context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error) {
|
||||
if m.listFn != nil {
|
||||
return m.listFn(ctx, req, userID)
|
||||
return m.listFn(ctx, req)
|
||||
}
|
||||
panic("not implemented")
|
||||
}
|
||||
func (m *mockChunkSvc) SwitchChunks(ctx context.Context, userID, datasetID, documentID string, availableInt int, chunkIDs []string) error {
|
||||
func (m *mockChunkSvc) SwitchChunks(ctx context.Context, datasetID, documentID string, availableInt int, chunkIDs []string) error {
|
||||
if m.switchChunksFn != nil {
|
||||
return m.switchChunksFn(ctx, userID, datasetID, documentID, availableInt, chunkIDs)
|
||||
return m.switchChunksFn(ctx, datasetID, documentID, availableInt, chunkIDs)
|
||||
}
|
||||
panic("not implemented")
|
||||
}
|
||||
func (m *mockChunkSvc) UpdateChunk(ctx context.Context, req *service.UpdateChunkRequest, userID string) error {
|
||||
func (m *mockChunkSvc) UpdateChunk(ctx context.Context, req *service.UpdateChunkRequest) error {
|
||||
if m.updateChunkFn != nil {
|
||||
return m.updateChunkFn(ctx, req, userID)
|
||||
return m.updateChunkFn(ctx, req)
|
||||
}
|
||||
panic("not implemented")
|
||||
}
|
||||
func (m *mockChunkSvc) RemoveChunks(ctx context.Context, req *service.RemoveChunksRequest, userID string) (int64, error) {
|
||||
func (m *mockChunkSvc) RemoveChunks(ctx context.Context, req *service.RemoveChunksRequest) (int64, error) {
|
||||
if m.removeChunksFn != nil {
|
||||
return m.removeChunksFn(ctx, req, userID)
|
||||
return m.removeChunksFn(ctx, req)
|
||||
}
|
||||
panic("not implemented")
|
||||
}
|
||||
@@ -83,10 +82,10 @@ func TestChunkRemoveUsesRouteIDs(t *testing.T) {
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
called := false
|
||||
mock := &mockChunkSvc{removeChunksFn: func(_ context.Context, req *service.RemoveChunksRequest, userID string) (int64, error) {
|
||||
mock := &mockChunkSvc{removeChunksFn: func(ctx context.Context, req *service.RemoveChunksRequest) (int64, error) {
|
||||
called = true
|
||||
if req.DatasetID != "kb-1" || req.DocID != "doc-1" || userID != "user-1" {
|
||||
t.Fatalf("unexpected delete target: %#v, user %q", req, userID)
|
||||
if req.DatasetID != "kb-1" || req.DocID != "doc-1" || permission.UserIDFromContext(ctx) != "user-1" {
|
||||
t.Fatalf("unexpected delete target: %#v, user %q", req, permission.UserIDFromContext(ctx))
|
||||
}
|
||||
if !reflect.DeepEqual(req.ChunkIDs, []string{"chunk-1"}) {
|
||||
t.Fatalf("unexpected chunk IDs: %v", req.ChunkIDs)
|
||||
@@ -122,18 +121,18 @@ func TestChunkRemoveUsesRouteIDs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func (m *mockChunkSvc) StopParsing(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
func (m *mockChunkSvc) StopParsing(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
if m.stopParsingFn != nil {
|
||||
return m.stopParsingFn(ctx, userID, datasetID, req)
|
||||
return m.stopParsingFn(ctx, datasetID, req)
|
||||
}
|
||||
panic("not implemented")
|
||||
}
|
||||
func (m *mockChunkSvc) Parse(context.Context, string, string, *service.ParseFileRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (m *mockChunkSvc) Parse(context.Context, string, *service.ParseFileRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (m *mockChunkSvc) AddChunk(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
func (m *mockChunkSvc) AddChunk(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
if m.addChunkFn != nil {
|
||||
return m.addChunkFn(ctx, req, userID)
|
||||
return m.addChunkFn(ctx, req)
|
||||
}
|
||||
return &service.AddChunkResponse{Chunk: map[string]interface{}{"id": "chunk-1"}}, nil
|
||||
}
|
||||
@@ -144,7 +143,7 @@ func setupChunkStopParsingTest(userID string) (*gin.Engine, *mockChunkSvc) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: userID})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), userID, ""))
|
||||
})
|
||||
r.DELETE("/api/v1/datasets/:dataset_id/chunks", h.StopParsing)
|
||||
return r, mock
|
||||
@@ -155,7 +154,7 @@ func setupChunkHandlerWithUser(userID string, mock *mockChunkSvc) (*gin.Engine,
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: userID})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), userID, ""))
|
||||
})
|
||||
return r, h
|
||||
}
|
||||
@@ -165,9 +164,9 @@ func TestChunkHandlerListChunksMapsPathAndQuery(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.GET("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.ListChunks)
|
||||
|
||||
mock.listFn = func(ctx context.Context, req *service.ListChunksRequest, userID string) (*service.ListChunksResponse, error) {
|
||||
if userID != "user-1" {
|
||||
t.Fatalf("userID = %q, want user-1", userID)
|
||||
mock.listFn = func(ctx context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error) {
|
||||
if permission.UserIDFromContext(ctx) != "user-1" {
|
||||
t.Fatalf("context userID = %q, want user-1", permission.UserIDFromContext(ctx))
|
||||
}
|
||||
if req.DatasetID != "kb-1" || req.DocID != "doc-1" {
|
||||
t.Fatalf("req ids = %q/%q, want kb-1/doc-1", req.DatasetID, req.DocID)
|
||||
@@ -217,9 +216,9 @@ func TestChunkHandlerListChunksMapsAvailableFalse(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.GET("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.ListChunks)
|
||||
|
||||
mock.listFn = func(ctx context.Context, req *service.ListChunksRequest, userID string) (*service.ListChunksResponse, error) {
|
||||
if userID != "user-1" {
|
||||
t.Fatalf("userID = %q, want user-1", userID)
|
||||
mock.listFn = func(ctx context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error) {
|
||||
if permission.UserIDFromContext(ctx) != "user-1" {
|
||||
t.Fatalf("context userID = %q, want user-1", permission.UserIDFromContext(ctx))
|
||||
}
|
||||
if req.AvailableInt == nil || *req.AvailableInt != 0 {
|
||||
t.Fatalf("available_int = %v, want 0", req.AvailableInt)
|
||||
@@ -245,9 +244,9 @@ func TestChunkHandlerSwitchChunksCallsService(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.SwitchChunks)
|
||||
|
||||
mock.switchChunksFn = func(ctx context.Context, userID, datasetID, documentID string, availableInt int, chunkIDs []string) error {
|
||||
if userID != "user-1" || datasetID != "kb-1" || documentID != "doc-1" {
|
||||
t.Fatalf("ids = %q/%q/%q, want user-1/kb-1/doc-1", userID, datasetID, documentID)
|
||||
mock.switchChunksFn = func(ctx context.Context, datasetID, documentID string, availableInt int, chunkIDs []string) error {
|
||||
if permission.UserIDFromContext(ctx) != "user-1" || datasetID != "kb-1" || documentID != "doc-1" {
|
||||
t.Fatalf("identity and ids = %q/%q/%q, want user-1/kb-1/doc-1", permission.UserIDFromContext(ctx), datasetID, documentID)
|
||||
}
|
||||
if availableInt != 0 {
|
||||
t.Fatalf("availableInt = %d, want 0", availableInt)
|
||||
@@ -299,9 +298,9 @@ func TestChunkHandlerUpdateChunkUsesPathIDs(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest, userID string) error {
|
||||
if userID != "user-1" {
|
||||
t.Fatalf("userID = %q, want user-1", userID)
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest) error {
|
||||
if permission.UserIDFromContext(ctx) != "user-1" {
|
||||
t.Fatalf("context userID = %q, want user-1", permission.UserIDFromContext(ctx))
|
||||
}
|
||||
if req.DatasetID != "kb-1" || req.DocumentID != "doc-1" || req.ChunkID != "chunk-1" {
|
||||
t.Fatalf("ids = %q/%q/%q, want kb-1/doc-1/chunk-1", req.DatasetID, req.DocumentID, req.ChunkID)
|
||||
@@ -327,7 +326,7 @@ func TestChunkHandlerUpdateChunkValidationErrorIsBadRequest(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest, userID string) error {
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest) error {
|
||||
return codedTestError{code: common.CodeArgumentError, msg: "`tag_feas` values must be finite numbers greater than 0"}
|
||||
}
|
||||
|
||||
@@ -346,7 +345,7 @@ func TestChunkHandlerUpdateChunkForwardsImageFields(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest, userID string) error {
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest) error {
|
||||
if req.ImageBase64 == nil || *req.ImageBase64 != "aGVsbG8=" {
|
||||
t.Fatalf("image_base64 = %v, want aGVsbG8=", req.ImageBase64)
|
||||
}
|
||||
@@ -383,7 +382,7 @@ func TestChunkHandlerUpdateChunkRejectsNonStringImageFields(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
mock.updateChunkFn = func(context.Context, *service.UpdateChunkRequest, string) error {
|
||||
mock.updateChunkFn = func(context.Context, *service.UpdateChunkRequest) error {
|
||||
t.Fatal("service must not be called for a malformed image field")
|
||||
return nil
|
||||
}
|
||||
@@ -429,7 +428,7 @@ func TestChunkHandlerUpdateChunkForwardsTagKwd(t *testing.T) {
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
called := false
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest, userID string) error {
|
||||
mock.updateChunkFn = func(ctx context.Context, req *service.UpdateChunkRequest) error {
|
||||
called = true
|
||||
if !reflect.DeepEqual(req.TagKwd, tc.want) {
|
||||
t.Fatalf("tag_kwd = %#v, want %#v", req.TagKwd, tc.want)
|
||||
@@ -468,7 +467,7 @@ func TestChunkHandlerUpdateChunkRejectsInvalidTagKwd(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
mock.updateChunkFn = func(context.Context, *service.UpdateChunkRequest, string) error {
|
||||
mock.updateChunkFn = func(context.Context, *service.UpdateChunkRequest) error {
|
||||
t.Fatal("service must not be called for a malformed tag_kwd")
|
||||
return nil
|
||||
}
|
||||
@@ -500,7 +499,7 @@ func TestChunkHandlerUpdateChunkStillRejectsUnknownFields(t *testing.T) {
|
||||
r, h := setupChunkHandlerWithUser("user-1", mock)
|
||||
r.PATCH("/api/v1/datasets/:dataset_id/documents/:document_id/chunks/:chunk_id", h.UpdateChunk)
|
||||
|
||||
mock.updateChunkFn = func(context.Context, *service.UpdateChunkRequest, string) error {
|
||||
mock.updateChunkFn = func(context.Context, *service.UpdateChunkRequest) error {
|
||||
t.Fatal("service must not be called for an unsupported field")
|
||||
return nil
|
||||
}
|
||||
@@ -527,9 +526,9 @@ func TestChunkHandlerUpdateChunkStillRejectsUnknownFields(t *testing.T) {
|
||||
|
||||
func TestChunkStopParsing_Success(t *testing.T) {
|
||||
r, mock := setupChunkStopParsingTest("user1")
|
||||
mock.stopParsingFn = func(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
if userID != "user1" {
|
||||
t.Fatalf("expected user1, got %q", userID)
|
||||
mock.stopParsingFn = func(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
if permission.UserIDFromContext(ctx) != "user1" {
|
||||
t.Fatalf("expected user1, got %q", permission.UserIDFromContext(ctx))
|
||||
}
|
||||
if datasetID != "kb1" {
|
||||
t.Fatalf("expected kb1, got %q", datasetID)
|
||||
@@ -562,7 +561,7 @@ func TestChunkStopParsing_Success(t *testing.T) {
|
||||
|
||||
func TestChunkStopParsingRouteRequiresDocumentIDs(t *testing.T) {
|
||||
r, mock := setupChunkStopParsingTest("user1")
|
||||
mock.stopParsingFn = func(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
mock.stopParsingFn = func(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
t.Fatal("service should not be called when document_ids is missing")
|
||||
return nil, common.CodeSuccess, nil
|
||||
}
|
||||
@@ -589,7 +588,7 @@ func TestChunkStopParsingRouteRequiresDocumentIDs(t *testing.T) {
|
||||
|
||||
func TestChunkStopParsing_InvalidStateOmitsData(t *testing.T) {
|
||||
r, mock := setupChunkStopParsingTest("user1")
|
||||
mock.stopParsingFn = func(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
mock.stopParsingFn = func(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
return &service.StopParsingResponse{
|
||||
Data: map[string]interface{}{"error_code": "DOC_STOP_PARSING_INVALID_STATE"},
|
||||
}, common.CodeDataError, errors.New("can't stop parsing document that has not started or already completed")
|
||||
@@ -628,9 +627,9 @@ func (e addChunkTestError) Code() common.ErrorCode { return e.code }
|
||||
|
||||
func TestChunkHandlerAddChunkSuccess(t *testing.T) {
|
||||
mock := &mockChunkSvc{
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
if userID != "user1" {
|
||||
t.Fatalf("userID = %q, want user1", userID)
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
if permission.UserIDFromContext(ctx) != "user1" {
|
||||
t.Fatalf("context userID = %q, want user1", permission.UserIDFromContext(ctx))
|
||||
}
|
||||
if req.DatasetID != "kb1" || req.DocumentID != "doc1" || req.Content != "chunk body" {
|
||||
t.Fatalf("unexpected request: %#v", req)
|
||||
@@ -642,7 +641,7 @@ func TestChunkHandlerAddChunkSuccess(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: "user1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user1", ""))
|
||||
})
|
||||
h := &ChunkHandler{chunkService: mock}
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.AddChunk)
|
||||
@@ -666,7 +665,7 @@ func TestChunkHandlerAddChunkSuccess(t *testing.T) {
|
||||
|
||||
func TestChunkHandlerAddChunkPathIDsOverrideBody(t *testing.T) {
|
||||
mock := &mockChunkSvc{
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
if req.DatasetID != "kb1" || req.DocumentID != "doc1" {
|
||||
t.Fatalf("path IDs were not preserved: %#v", req)
|
||||
}
|
||||
@@ -680,7 +679,7 @@ func TestChunkHandlerAddChunkPathIDsOverrideBody(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: "user1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user1", ""))
|
||||
})
|
||||
h := &ChunkHandler{chunkService: mock}
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.AddChunk)
|
||||
@@ -697,7 +696,7 @@ func TestChunkHandlerAddChunkPathIDsOverrideBody(t *testing.T) {
|
||||
|
||||
func TestChunkHandlerAddChunkCodedError(t *testing.T) {
|
||||
mock := &mockChunkSvc{
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
return nil, addChunkTestError{code: common.CodeDataError, msg: "`content` is required"}
|
||||
},
|
||||
}
|
||||
@@ -705,7 +704,7 @@ func TestChunkHandlerAddChunkCodedError(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: "user1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user1", ""))
|
||||
})
|
||||
h := &ChunkHandler{chunkService: mock}
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.AddChunk)
|
||||
@@ -758,7 +757,7 @@ func TestChunkHandlerAddChunkValidatesListFields(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mock := &mockChunkSvc{
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
t.Fatal("service should not be called for invalid request")
|
||||
return nil, nil
|
||||
},
|
||||
@@ -766,7 +765,7 @@ func TestChunkHandlerAddChunkValidatesListFields(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: "user1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user1", ""))
|
||||
})
|
||||
h := &ChunkHandler{chunkService: mock}
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.AddChunk)
|
||||
@@ -819,7 +818,7 @@ func TestChunkHandlerAddChunkForwardsTagKwd(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var got *service.AddChunkRequest
|
||||
mock := &mockChunkSvc{
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
got = req
|
||||
return &service.AddChunkResponse{Chunk: map[string]interface{}{"id": "chunk-1"}}, nil
|
||||
},
|
||||
@@ -827,7 +826,7 @@ func TestChunkHandlerAddChunkForwardsTagKwd(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: "user1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user1", ""))
|
||||
})
|
||||
h := &ChunkHandler{chunkService: mock}
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.AddChunk)
|
||||
@@ -858,7 +857,7 @@ func TestChunkHandlerAddChunkForwardsTagKwd(t *testing.T) {
|
||||
|
||||
func TestChunkHandlerAddChunkHidesServerErrorDetails(t *testing.T) {
|
||||
mock := &mockChunkSvc{
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
addChunkFn: func(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
return nil, addChunkTestError{code: common.CodeServerError, msg: "encode chunk embedding: provider secret"}
|
||||
},
|
||||
}
|
||||
@@ -866,7 +865,7 @@ func TestChunkHandlerAddChunkHidesServerErrorDetails(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("user", &entity.User{ID: "user1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user1", ""))
|
||||
})
|
||||
h := &ChunkHandler{chunkService: mock}
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/:document_id/chunks", h.AddChunk)
|
||||
|
||||
+36
-50
@@ -48,11 +48,11 @@ type DatasetsHandler struct {
|
||||
}
|
||||
|
||||
type searchDatasetsService interface {
|
||||
SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest, userID string) (*service.SearchDatasetsResponse, error)
|
||||
SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest) (*service.SearchDatasetsResponse, error)
|
||||
}
|
||||
|
||||
type searchDatasetService interface {
|
||||
SearchDataset(ctx context.Context, datasetID, userID string, req *service.SearchDatasetRequest) (*service.SearchDatasetsResponse, error)
|
||||
SearchDataset(ctx context.Context, datasetID string, req *service.SearchDatasetRequest) (*service.SearchDatasetsResponse, error)
|
||||
}
|
||||
|
||||
// NewDatasetsHandler creates a new datasets' handler.
|
||||
@@ -70,7 +70,7 @@ func NewDatasetsHandler(datasetsService *dataset.DatasetService, metadataService
|
||||
|
||||
// ListDatasets handles GET /api/v1/datasets.
|
||||
func (h *DatasetsHandler) ListDatasets(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -78,7 +78,7 @@ func (h *DatasetsHandler) ListDatasets(c *gin.Context) {
|
||||
|
||||
if c.Query("type") == "filter" {
|
||||
ctx := c.Request.Context()
|
||||
data, code, err := h.datasetsService.ListDatasetFilters(ctx, user.ID, c.Query("canvas_id"))
|
||||
data, code, err := h.datasetsService.ListDatasetFilters(ctx, c.Query("canvas_id"))
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -216,7 +216,6 @@ func (h *DatasetsHandler) ListDatasets(c *gin.Context) {
|
||||
keywords,
|
||||
ownerIDs,
|
||||
parserID,
|
||||
user.ID,
|
||||
ids,
|
||||
c.Query("canvas_id"),
|
||||
)
|
||||
@@ -341,7 +340,7 @@ func (h *DatasetsHandler) CreateDataset(c *gin.Context) {
|
||||
|
||||
// GetDataset handles GET /api/v1/datasets/:dataset_id.
|
||||
func (h *DatasetsHandler) GetDataset(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -350,7 +349,7 @@ func (h *DatasetsHandler) GetDataset(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
result, code, err := h.datasetsService.GetDataset(ctx, datasetID, user.ID)
|
||||
result, code, err := h.datasetsService.GetDataset(ctx, datasetID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -547,7 +546,7 @@ func (h *DatasetsHandler) UpdateDataset(c *gin.Context) {
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
result, code, err := h.datasetsService.UpdateDataset(ctx, datasetID, userID, req)
|
||||
result, code, err := h.datasetsService.UpdateDataset(ctx, datasetID, req)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
operationLog.Code = int(code)
|
||||
@@ -565,7 +564,7 @@ func (h *DatasetsHandler) UpdateDataset(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *DatasetsHandler) GetMetadataConfig(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -574,7 +573,7 @@ func (h *DatasetsHandler) GetMetadataConfig(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
result, code, err := h.datasetsService.GetMetadataConfig(ctx, datasetID, user.ID)
|
||||
result, code, err := h.datasetsService.GetMetadataConfig(ctx, datasetID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -584,7 +583,7 @@ func (h *DatasetsHandler) GetMetadataConfig(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *DatasetsHandler) UpdateMetadataConfig(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -599,7 +598,7 @@ func (h *DatasetsHandler) UpdateMetadataConfig(c *gin.Context) {
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
result, code, err := h.datasetsService.UpdateMetadataConfig(ctx, datasetID, user.ID, &req)
|
||||
result, code, err := h.datasetsService.UpdateMetadataConfig(ctx, datasetID, &req)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -610,14 +609,14 @@ func (h *DatasetsHandler) UpdateMetadataConfig(c *gin.Context) {
|
||||
|
||||
// GetIngestionSummary handles GET /api/v1/datasets/:dataset_id/ingestions/summary.
|
||||
func (h *DatasetsHandler) GetIngestionSummary(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
ctx := c.Request.Context()
|
||||
datasetID := c.Param("dataset_id")
|
||||
result, code, err := h.datasetsService.GetIngestionSummary(ctx, datasetID, user.ID)
|
||||
result, code, err := h.datasetsService.GetIngestionSummary(ctx, datasetID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -629,7 +628,7 @@ func (h *DatasetsHandler) GetIngestionSummary(c *gin.Context) {
|
||||
// ListIngestionLogs handles GET /api/v1/datasets/:dataset_id/ingestions.
|
||||
// The log_type=datasource query returns logs for connectors linked to the dataset.
|
||||
func (h *DatasetsHandler) ListIngestionLogs(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -659,7 +658,7 @@ func (h *DatasetsHandler) ListIngestionLogs(c *gin.Context) {
|
||||
|
||||
ctx := c.Request.Context()
|
||||
if strings.EqualFold(strings.TrimSpace(c.Query("log_type")), "datasource") {
|
||||
result, code, err := h.datasetsService.ListSyncLogs(ctx, datasetID, user.ID, page, pageSize)
|
||||
result, code, err := h.datasetsService.ListSyncLogs(ctx, datasetID, page, pageSize)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -681,7 +680,7 @@ func (h *DatasetsHandler) ListIngestionLogs(c *gin.Context) {
|
||||
// equivalent; the frontend only sends it on the Go backend.
|
||||
documentID := c.Query("document_id")
|
||||
|
||||
result, code, err := h.datasetsService.ListIngestionLogs(ctx, datasetID, user.ID, page, pageSize, terms, operationStatus, createDateFrom, createDateTo, logType, keywords, documentID)
|
||||
result, code, err := h.datasetsService.ListIngestionLogs(ctx, datasetID, page, pageSize, terms, operationStatus, createDateFrom, createDateTo, logType, keywords, documentID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -692,7 +691,7 @@ func (h *DatasetsHandler) ListIngestionLogs(c *gin.Context) {
|
||||
|
||||
// GetIngestionLog handles GET /api/v1/datasets/:dataset_id/ingestions/:log_id.
|
||||
func (h *DatasetsHandler) GetIngestionLog(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -702,7 +701,7 @@ func (h *DatasetsHandler) GetIngestionLog(c *gin.Context) {
|
||||
|
||||
datasetID := c.Param("dataset_id")
|
||||
logID := c.Param("log_id")
|
||||
result, code, err := h.datasetsService.GetIngestionLog(ctx, datasetID, user.ID, logID)
|
||||
result, code, err := h.datasetsService.GetIngestionLog(ctx, datasetID, logID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -714,7 +713,7 @@ func (h *DatasetsHandler) GetIngestionLog(c *gin.Context) {
|
||||
// ListIngestionMessages handles GET
|
||||
// /api/v1/datasets/:dataset_id/ingestions/:log_id/messages.
|
||||
func (h *DatasetsHandler) ListIngestionMessages(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -739,7 +738,7 @@ func (h *DatasetsHandler) ListIngestionMessages(c *gin.Context) {
|
||||
}
|
||||
|
||||
result, code, err := h.datasetsService.ListIngestionMessages(
|
||||
c.Request.Context(), c.Param("dataset_id"), user.ID, c.Param("log_id"), limit, afterID, beforeID,
|
||||
c.Request.Context(), c.Param("dataset_id"), c.Param("log_id"), limit, afterID, beforeID,
|
||||
)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
@@ -852,7 +851,7 @@ func (h *DatasetsHandler) DeleteDatasets(c *gin.Context) {
|
||||
|
||||
// GetKnowledgeGraph handles GET /api/v1/datasets/:dataset_id/graph.
|
||||
func (h *DatasetsHandler) GetKnowledgeGraph(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -865,7 +864,7 @@ func (h *DatasetsHandler) GetKnowledgeGraph(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
datasetInstance, code, err := h.datasetsService.GetDataset(ctx, datasetID, user.ID)
|
||||
datasetInstance, code, err := h.datasetsService.GetDataset(ctx, datasetID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -955,7 +954,7 @@ func (h *DatasetsHandler) GetKnowledgeGraph(c *gin.Context) {
|
||||
|
||||
// DeleteKnowledgeGraph handles DELETE /api/v1/datasets/:dataset_id/graph.
|
||||
func (h *DatasetsHandler) DeleteKnowledgeGraph(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -968,7 +967,7 @@ func (h *DatasetsHandler) DeleteKnowledgeGraph(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
datasetInstance, code, err := h.datasetsService.GetDataset(ctx, datasetID, user.ID)
|
||||
datasetInstance, code, err := h.datasetsService.GetDataset(ctx, datasetID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -1001,7 +1000,7 @@ func (h *DatasetsHandler) DeleteKnowledgeGraph(c *gin.Context) {
|
||||
// CheckEmbedding Check embedding model compatibility by sampling random chunks,
|
||||
// re-embedding them with the new model, and computing cosine similarity.
|
||||
func (h *DatasetsHandler) CheckEmbedding(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -1013,12 +1012,6 @@ func (h *DatasetsHandler) CheckEmbedding(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
if userID == "" {
|
||||
common.RespondError(c, common.CodeDataError, "user_id is required")
|
||||
return
|
||||
}
|
||||
|
||||
var req service.CheckEmbeddingRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.RespondError(c, common.CodeDataError, err.Error())
|
||||
@@ -1030,7 +1023,7 @@ func (h *DatasetsHandler) CheckEmbedding(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
data, code, err := h.datasetsService.CheckEmbedding(ctx, userID, datasetID, &req)
|
||||
data, code, err := h.datasetsService.CheckEmbedding(ctx, datasetID, &req)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -1048,7 +1041,7 @@ func (h *DatasetsHandler) CheckEmbedding(c *gin.Context) {
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/datasets/tags/aggregation [get]
|
||||
func (h *DatasetsHandler) AggregateTags(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -1070,7 +1063,7 @@ func (h *DatasetsHandler) AggregateTags(c *gin.Context) {
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
result, code, err := h.datasetsService.AggregateTags(ctx, datasetIDs, user.ID)
|
||||
result, code, err := h.datasetsService.AggregateTags(ctx, datasetIDs)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -1082,7 +1075,7 @@ func (h *DatasetsHandler) AggregateTags(c *gin.Context) {
|
||||
// state. The optional `kind` query parameter scopes the status to one compile
|
||||
// type.
|
||||
func (h *DatasetsHandler) GetCompilationStatus(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -1092,10 +1085,9 @@ func (h *DatasetsHandler) GetCompilationStatus(c *gin.Context) {
|
||||
common.RespondError(c, common.CodeDataError, "dataset_id is required")
|
||||
return
|
||||
}
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
ctx := c.Request.Context()
|
||||
kind := strings.TrimSpace(c.Query("kind"))
|
||||
st, code, err := h.datasetsService.GetDatasetCompilationStatus(ctx, userID, datasetID, kind)
|
||||
st, code, err := h.datasetsService.GetDatasetCompilationStatus(ctx, datasetID, kind)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -1152,7 +1144,7 @@ func (h *DatasetsHandler) ListMetadataFlattened(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *DatasetsHandler) UpdateDocumentMetadataConfig(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -1168,12 +1160,6 @@ func (h *DatasetsHandler) UpdateDocumentMetadataConfig(c *gin.Context) {
|
||||
common.RespondError(c, common.CodeArgumentError, "document_id is required")
|
||||
return
|
||||
}
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
if userID == "" {
|
||||
common.RespondError(c, common.CodeArgumentError, "user_id is required")
|
||||
return
|
||||
}
|
||||
|
||||
var req map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
common.RespondError(c, common.CodeDataError, err.Error())
|
||||
@@ -1181,7 +1167,7 @@ func (h *DatasetsHandler) UpdateDocumentMetadataConfig(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
data, code, err := h.datasetsService.UpdateDocumentMetadataConfig(ctx, userID, datasetID, documentID, req)
|
||||
data, code, err := h.datasetsService.UpdateDocumentMetadataConfig(ctx, datasetID, documentID, req)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
@@ -1198,7 +1184,7 @@ func (h *DatasetsHandler) UpdateDocumentMetadataConfig(c *gin.Context) {
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/datasets/search [post]
|
||||
func (h *DatasetsHandler) SearchDatasets(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -1261,7 +1247,7 @@ func (h *DatasetsHandler) SearchDatasets(c *gin.Context) {
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
resp, err := searchService.SearchDatasets(ctx, &req, user.ID)
|
||||
resp, err := searchService.SearchDatasets(ctx, &req)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
@@ -1282,7 +1268,7 @@ func (h *DatasetsHandler) SearchDatasets(c *gin.Context) {
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/datasets/{dataset_id}/search [post]
|
||||
func (h *DatasetsHandler) SearchDataset(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -1320,7 +1306,7 @@ func (h *DatasetsHandler) SearchDataset(c *gin.Context) {
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
resp, err := searchService.SearchDataset(ctx, datasetID, user.ID, &req)
|
||||
resp, err := searchService.SearchDataset(ctx, datasetID, &req)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
return
|
||||
|
||||
@@ -27,9 +27,9 @@ type fakeSearchDatasetService struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeSearchDatasetService) SearchDataset(ctx context.Context, datasetID, userID string, req *service.SearchDatasetRequest) (*service.SearchDatasetsResponse, error) {
|
||||
func (f *fakeSearchDatasetService) SearchDataset(ctx context.Context, datasetID string, req *service.SearchDatasetRequest) (*service.SearchDatasetsResponse, error) {
|
||||
f.datasetID = datasetID
|
||||
f.userID = userID
|
||||
f.userID = permission.UserIDFromContext(ctx)
|
||||
f.req = req
|
||||
return f.resp, f.err
|
||||
}
|
||||
@@ -41,8 +41,8 @@ type fakeSearchDatasetsService struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeSearchDatasetsService) SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest, userID string) (*service.SearchDatasetsResponse, error) {
|
||||
f.userID = userID
|
||||
func (f *fakeSearchDatasetsService) SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest) (*service.SearchDatasetsResponse, error) {
|
||||
f.userID = permission.UserIDFromContext(ctx)
|
||||
f.req = req
|
||||
return f.resp, f.err
|
||||
}
|
||||
@@ -55,6 +55,7 @@ func TestDatasetsHandlerSearchDataset(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/datasets/ds-1/search", strings.NewReader(`{"question":"hello","document_ids":["doc-1"],"page_size":9,"metadata_condition":{"logic":"and","conditions":[{"name":"author","comparison_operator":"=","value":"Luo"}]},"knn_top_k":7,"knn_num_candidates":14,"keywords_similarity_weight":0.7}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req = req.WithContext(permission.WithIdentity(req.Context(), "user-1", ""))
|
||||
c, _ := gin.CreateTestContext(rec)
|
||||
c.Request = req
|
||||
c.Set("user", &entity.User{ID: "user-1"})
|
||||
@@ -171,6 +172,7 @@ func TestDatasetsHandlerSearchDatasetsSuccess(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/datasets/search", strings.NewReader(`{"question":" hello ","dataset_ids":["ds-1"],"document_ids":["doc-1"],"page_size":9,"metadata_condition":{"logic":"and","conditions":[]},"top_k":7,"include_knowledge_compilation":false}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req = req.WithContext(permission.WithIdentity(req.Context(), "user-1", ""))
|
||||
c, _ := gin.CreateTestContext(rec)
|
||||
c.Request = req
|
||||
c.Set("user", &entity.User{ID: "user-1"})
|
||||
|
||||
@@ -52,8 +52,8 @@ type documentServiceIface interface {
|
||||
GetDocumentByID(ctx context.Context, id string) (*document.DocumentResponse, error)
|
||||
UpdateDocument(ctx context.Context, id string, req *document.UpdateDocumentRequest) (common.ErrorCode, error)
|
||||
DeleteDocument(ctx context.Context, id string) error
|
||||
DeleteDocuments(ctx context.Context, ids []string, deleteAll bool, datasetID, userID string) (int, error)
|
||||
ParseDocuments(ctx context.Context, datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error)
|
||||
DeleteDocuments(ctx context.Context, ids []string, deleteAll bool, datasetID string) (int, error)
|
||||
ParseDocuments(ctx context.Context, datasetID string, docIDs []string) ([]*service.ParseDocumentResponse, error)
|
||||
StopParseDocuments(ctx context.Context, datasetID string, docIDs []string) (map[string]interface{}, error)
|
||||
StopParseDocumentsAll(ctx context.Context, datasetID string, runStatuses []string) (map[string]interface{}, error)
|
||||
ListDocuments(ctx context.Context, page, pageSize int) ([]*document.DocumentResponse, int64, error)
|
||||
@@ -63,27 +63,27 @@ type documentServiceIface interface {
|
||||
GetDocumentFiltersByDatasetID(ctx context.Context, opts dao.DocumentListOptions) (map[string]interface{}, int64, error)
|
||||
GetMetadataByKBs(ctx context.Context, kbIDs []string) (map[string]interface{}, error)
|
||||
GetDocumentsByAuthorID(ctx context.Context, authorID, page, pageSize int) ([]*document.DocumentResponse, int64, error)
|
||||
GetThumbnails(ctx context.Context, userID string, docIDs []string) (map[string]string, error)
|
||||
GetDocumentImage(ctx context.Context, userID, imageID string) ([]byte, error)
|
||||
GetDocumentImageForDocument(ctx context.Context, userID, docID, imageID string) ([]byte, error)
|
||||
GetThumbnails(ctx context.Context, docIDs []string) (map[string]string, error)
|
||||
GetDocumentImage(ctx context.Context, imageID string) ([]byte, error)
|
||||
GetDocumentImageForDocument(ctx context.Context, docID, imageID string) ([]byte, error)
|
||||
GetDocumentThumbnail(ctx context.Context, docID string) ([]byte, error)
|
||||
GetMetadataSummary(ctx context.Context, kbID string, docIDs []string) (map[string]interface{}, error)
|
||||
GetDocumentMetadataByID(ctx context.Context, docID string) (map[string]interface{}, error)
|
||||
GetDocumentArtifact(ctx context.Context, filename, userID string) (*document.ArtifactResponse, error)
|
||||
GetDocumentArtifact(ctx context.Context, filename string) (*document.ArtifactResponse, error)
|
||||
GetDocumentPreview(ctx context.Context, docID string) (*document.DocumentPreview, error)
|
||||
UploadLocalDocuments(ctx context.Context, kb *entity.Knowledgebase, tenantID string, files []*multipart.FileHeader, parentPath string, parserConfigOverride map[string]interface{}) ([]map[string]interface{}, []string)
|
||||
UploadWebDocument(ctx context.Context, kb *entity.Knowledgebase, tenantID, name, url string) (map[string]interface{}, common.ErrorCode, error)
|
||||
UploadEmptyDocument(ctx context.Context, kb *entity.Knowledgebase, tenantID, name string) (map[string]interface{}, common.ErrorCode, error)
|
||||
DownloadDocument(ctx context.Context, datasetID, docID string) (*document.DownloadDocumentResp, error)
|
||||
UpdateDatasetDocument(ctx context.Context, userID, datasetID, documentID string, req *document.UpdateDatasetDocumentRequest, present map[string]bool) (*document.UpdateDatasetDocumentResponse, common.ErrorCode, error)
|
||||
ApplyDatasetParserConfig(ctx context.Context, userID, datasetID string) (*document.ApplyDatasetParserConfigResponse, common.ErrorCode, error)
|
||||
UpdateDatasetDocument(ctx context.Context, datasetID, documentID string, req *document.UpdateDatasetDocumentRequest, present map[string]bool) (*document.UpdateDatasetDocumentResponse, common.ErrorCode, error)
|
||||
ApplyDatasetParserConfig(ctx context.Context, datasetID string) (*document.ApplyDatasetParserConfigResponse, common.ErrorCode, error)
|
||||
BatchUpdateDocumentMetadatas(ctx context.Context, datasetID string, selector *document.MetadataSelector, updates []document.MetadataUpdate, deletes []document.MetadataDelete) (*document.BatchUpdateMetadatasResponse, common.ErrorCode, error)
|
||||
ListIngestionTasks(ctx context.Context, userID string, datasetID *string, page, pageSize int) ([]*entity.IngestionTask, error)
|
||||
IngestDocuments(ctx context.Context, datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error)
|
||||
StopIngestionTasks(ctx context.Context, tasks []string, userID string) ([]*entity.IngestionTask, error)
|
||||
Ingest(ctx context.Context, userID string, req *document.IngestDocumentRequest) (common.ErrorCode, error)
|
||||
RemoveIngestionTasks(ctx context.Context, tasks []string, userID string) ([]map[string]string, error)
|
||||
BatchUpdateDocumentStatus(ctx context.Context, userID, datasetID, status string, DocumentIDs []string) (map[string]interface{}, common.ErrorCode, error)
|
||||
ListIngestionTasks(ctx context.Context, datasetID *string, page, pageSize int) ([]*entity.IngestionTask, error)
|
||||
IngestDocuments(ctx context.Context, datasetID string, docIDs []string) ([]*service.ParseDocumentResponse, error)
|
||||
StopIngestionTasks(ctx context.Context, tasks []string) ([]*entity.IngestionTask, error)
|
||||
Ingest(ctx context.Context, req *document.IngestDocumentRequest) (common.ErrorCode, error)
|
||||
RemoveIngestionTasks(ctx context.Context, tasks []string) ([]map[string]string, error)
|
||||
BatchUpdateDocumentStatus(ctx context.Context, datasetID, status string, DocumentIDs []string) (map[string]interface{}, common.ErrorCode, error)
|
||||
HasActiveIngestionTasks(ctx context.Context, datasetID string) (bool, error)
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func (h *DocumentHandler) GetDocumentByID(c *gin.Context) {
|
||||
|
||||
// GetThumbnail Get thumbnails for documents.
|
||||
func (h *DocumentHandler) GetThumbnail(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
@@ -158,7 +158,7 @@ func (h *DocumentHandler) GetThumbnail(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
result, err := h.documentService.GetThumbnails(ctx, user.ID, docIDs)
|
||||
result, err := h.documentService.GetThumbnails(ctx, docIDs)
|
||||
if err != nil {
|
||||
common.RespondError(c, common.CodeServerError, err.Error())
|
||||
return
|
||||
@@ -189,14 +189,14 @@ func parseThumbnailDocIDs(c *gin.Context) []string {
|
||||
|
||||
// GetDocumentImage returns a document image from object storage.
|
||||
func (h *DocumentHandler) GetDocumentImage(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
imageID := c.Param("image_id")
|
||||
ctx := c.Request.Context()
|
||||
data, err := h.documentService.GetDocumentImage(ctx, user.ID, imageID)
|
||||
data, err := h.documentService.GetDocumentImage(ctx, imageID)
|
||||
if err != nil {
|
||||
common.RespondError(c, common.CodeDataError, "Image not found.")
|
||||
return
|
||||
@@ -205,12 +205,12 @@ func (h *DocumentHandler) GetDocumentImage(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) GetDocumentImageForDocument(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
data, err := h.documentService.GetDocumentImageForDocument(c.Request.Context(), user.ID, c.Param("id"), c.Param("image_id"))
|
||||
data, err := h.documentService.GetDocumentImageForDocument(c.Request.Context(), c.Param("id"), c.Param("image_id"))
|
||||
if err != nil {
|
||||
common.RespondError(c, common.CodeDataError, "Image not found.")
|
||||
return
|
||||
@@ -255,14 +255,14 @@ func documentImageContentType(data []byte) string {
|
||||
}
|
||||
|
||||
func (h *DocumentHandler) GetDocumentArtifact(c *gin.Context) {
|
||||
user, code, msg := GetUser(c)
|
||||
_, code, msg := GetUser(c)
|
||||
if code != common.CodeSuccess {
|
||||
common.RespondError(c, code, msg)
|
||||
return
|
||||
}
|
||||
filename := c.Param("filename")
|
||||
ctx := c.Request.Context()
|
||||
artifact, err := h.documentService.GetDocumentArtifact(ctx, filename, user.ID)
|
||||
artifact, err := h.documentService.GetDocumentArtifact(ctx, filename)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, document.ErrArtifactInvalidFilename),
|
||||
@@ -516,9 +516,8 @@ func (h *DocumentHandler) DeleteDocuments(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
userID := c.GetString("user_id")
|
||||
ctx := c.Request.Context()
|
||||
deleted, err := h.documentService.DeleteDocuments(ctx, ids, req.DeleteAll, datasetID, userID)
|
||||
deleted, err := h.documentService.DeleteDocuments(ctx, ids, req.DeleteAll, datasetID)
|
||||
if err != nil {
|
||||
if respondPermissionErrorIf(c, err, false) {
|
||||
operationLog.Code = int(common.CodePermissionError)
|
||||
@@ -536,14 +535,14 @@ func (h *DocumentHandler) DeleteDocuments(c *gin.Context) {
|
||||
|
||||
// BatchUpdateDocumentStatus Batch update status of documents within a dataset.
|
||||
func (h *DocumentHandler) BatchUpdateDocumentStatus(c *gin.Context) {
|
||||
user, code, errorMessage := GetUser(c)
|
||||
_, code, errorMessage := GetUser(c)
|
||||
if code != common.CodeSuccess {
|
||||
common.RespondError(c, code, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
if userID == "" {
|
||||
ctx := c.Request.Context()
|
||||
if permission.UserIDFromContext(ctx) == "" {
|
||||
common.RespondError(c, common.CodeArgumentError, "invalid user id")
|
||||
return
|
||||
}
|
||||
@@ -586,8 +585,7 @@ func (h *DocumentHandler) BatchUpdateDocumentStatus(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
result, code, err := h.documentService.BatchUpdateDocumentStatus(ctx, userID, datasetID, status, documentIDs)
|
||||
result, code, err := h.documentService.BatchUpdateDocumentStatus(ctx, datasetID, status, documentIDs)
|
||||
if err != nil {
|
||||
message := err.Error()
|
||||
if code == common.CodeServerError {
|
||||
@@ -1344,14 +1342,14 @@ func stringValue(value *string) string {
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/documents/ingest [post]
|
||||
func (h *DocumentHandler) Ingest(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
_, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
common.RespondError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
userID := strings.TrimSpace(user.ID)
|
||||
if userID == "" {
|
||||
ctx := c.Request.Context()
|
||||
if permission.UserIDFromContext(ctx) == "" {
|
||||
common.RespondError(c, common.CodeAuthenticationError, "no authentication")
|
||||
return
|
||||
}
|
||||
@@ -1361,8 +1359,7 @@ func (h *DocumentHandler) Ingest(c *gin.Context) {
|
||||
common.RespondError(c, common.CodeBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
ctx := c.Request.Context()
|
||||
if code, err := h.documentService.Ingest(ctx, userID, &req); err != nil {
|
||||
if code, err := h.documentService.Ingest(ctx, &req); err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -1381,8 +1378,6 @@ func (h *DocumentHandler) ListIngestionTasks(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := c.GetString("user_id")
|
||||
|
||||
var parseResult []*entity.IngestionTask
|
||||
var err error
|
||||
ctx := c.Request.Context()
|
||||
@@ -1392,7 +1387,7 @@ func (h *DocumentHandler) ListIngestionTasks(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
parseResult, err = h.documentService.ListIngestionTasks(ctx, userID, req.DatasetID, 0, 0)
|
||||
parseResult, err = h.documentService.ListIngestionTasks(ctx, req.DatasetID, 0, 0)
|
||||
if err != nil {
|
||||
common.RespondError(c, IngestionTaskErrorCode(err), err.Error())
|
||||
return
|
||||
@@ -1450,7 +1445,7 @@ func (h *DocumentHandler) StartIngestionTask(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
parseResult, err := h.documentService.IngestDocuments(ctx, datasetID, userID, req.DocumentIDs)
|
||||
parseResult, err := h.documentService.IngestDocuments(ctx, datasetID, req.DocumentIDs)
|
||||
if err != nil {
|
||||
common.RespondError(c, common.CodeExceptionError, err.Error())
|
||||
return
|
||||
@@ -1476,9 +1471,8 @@ func (h *DocumentHandler) StopIngestionTasks(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := c.GetString("user_id")
|
||||
ctx := c.Request.Context()
|
||||
parseResult, err := h.documentService.StopIngestionTasks(ctx, req.Tasks, userID)
|
||||
parseResult, err := h.documentService.StopIngestionTasks(ctx, req.Tasks)
|
||||
if err != nil {
|
||||
common.RespondError(c, IngestionTaskErrorCode(err), err.Error())
|
||||
return
|
||||
@@ -1502,10 +1496,9 @@ func (h *DocumentHandler) RemoveIngestionTasks(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := c.GetString("user_id")
|
||||
ctx := c.Request.Context()
|
||||
|
||||
deletedTasks, err := h.documentService.RemoveIngestionTasks(ctx, req.Tasks, userID)
|
||||
deletedTasks, err := h.documentService.RemoveIngestionTasks(ctx, req.Tasks)
|
||||
if err != nil {
|
||||
common.RespondError(c, IngestionTaskErrorCode(err), err.Error())
|
||||
return
|
||||
@@ -1526,14 +1519,13 @@ func (h *DocumentHandler) ParseDocuments(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
userID := c.GetString("user_id")
|
||||
ctx := c.Request.Context()
|
||||
if err := permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindDataset, ID: datasetID}, permission.OperationRun); err != nil {
|
||||
respondPermissionError(c, err, false)
|
||||
return
|
||||
}
|
||||
|
||||
parseResult, err := h.documentService.ParseDocuments(ctx, datasetID, userID, req.Documents)
|
||||
parseResult, err := h.documentService.ParseDocuments(ctx, datasetID, req.Documents)
|
||||
if err != nil {
|
||||
common.RespondError(c, common.CodeExceptionError, err.Error())
|
||||
return
|
||||
@@ -1713,7 +1705,7 @@ func (h *DocumentHandler) UpdateDatasetDocument(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
ctx := c.Request.Context()
|
||||
data, code, err := h.documentService.UpdateDatasetDocument(ctx, user.ID, datasetID, documentID, &req, present)
|
||||
data, code, err := h.documentService.UpdateDatasetDocument(ctx, datasetID, documentID, &req, present)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// ApplyDatasetParserConfig handles POST /api/v1/datasets/:dataset_id/documents/apply-parser-config.
|
||||
func (h *DocumentHandler) ApplyDatasetParserConfig(c *gin.Context) {
|
||||
user, code, message := GetUser(c)
|
||||
_, code, message := GetUser(c)
|
||||
if code != common.CodeSuccess {
|
||||
common.RespondError(c, code, message)
|
||||
return
|
||||
@@ -20,7 +20,7 @@ func (h *DocumentHandler) ApplyDatasetParserConfig(c *gin.Context) {
|
||||
common.RespondError(c, common.CodeArgumentError, "dataset_id is required")
|
||||
return
|
||||
}
|
||||
result, code, err := h.documentService.ApplyDatasetParserConfig(c.Request.Context(), user.ID, datasetID)
|
||||
result, code, err := h.documentService.ApplyDatasetParserConfig(c.Request.Context(), datasetID)
|
||||
if err != nil {
|
||||
common.RespondError(c, code, err.Error())
|
||||
return
|
||||
|
||||
@@ -10,12 +10,13 @@ import (
|
||||
|
||||
"ragflow/internal/common"
|
||||
"ragflow/internal/entity"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/service/document"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (f *fakeDocumentService) ApplyDatasetParserConfig(ctx context.Context, userID, datasetID string) (*document.ApplyDatasetParserConfigResponse, common.ErrorCode, error) {
|
||||
func (f *fakeDocumentService) ApplyDatasetParserConfig(ctx context.Context, datasetID string) (*document.ApplyDatasetParserConfigResponse, common.ErrorCode, error) {
|
||||
return &document.ApplyDatasetParserConfigResponse{DatasetID: datasetID}, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
@@ -27,10 +28,10 @@ type applyParserConfigService struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (s *applyParserConfigService) ApplyDatasetParserConfig(ctx context.Context, userID, datasetID string) (*document.ApplyDatasetParserConfigResponse, common.ErrorCode, error) {
|
||||
func (s *applyParserConfigService) ApplyDatasetParserConfig(ctx context.Context, datasetID string) (*document.ApplyDatasetParserConfigResponse, common.ErrorCode, error) {
|
||||
s.called = true
|
||||
s.ctx = ctx
|
||||
if userID != "user-1" || datasetID != "kb-1" {
|
||||
if permission.UserIDFromContext(ctx) != "user-1" || datasetID != "kb-1" {
|
||||
return nil, common.CodeArgumentError, errors.New("wrong user or dataset")
|
||||
}
|
||||
return &document.ApplyDatasetParserConfigResponse{DatasetID: datasetID, TotalDocuments: 3, UpdatedDocuments: 2, UnchangedDocuments: 1}, s.code, s.err
|
||||
@@ -55,6 +56,7 @@ func TestApplyDatasetParserConfigHandler(t *testing.T) {
|
||||
r.POST("/api/v1/datasets/:dataset_id/documents/apply-parser-config", func(c *gin.Context) {
|
||||
if tc.auth {
|
||||
c.Set("user", &entity.User{ID: "user-1"})
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), "user-1", ""))
|
||||
}
|
||||
h.ApplyDatasetParserConfig(c)
|
||||
})
|
||||
@@ -77,8 +79,8 @@ func TestApplyDatasetParserConfigHandler(t *testing.T) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if body.Code != tc.code || svc.ctx != req.Context() {
|
||||
t.Fatalf("response=%s context propagated=%t", rec.Body.String(), svc.ctx == req.Context())
|
||||
if body.Code != tc.code || permission.UserIDFromContext(svc.ctx) != "user-1" {
|
||||
t.Fatalf("response=%s context identity=%q", rec.Body.String(), permission.UserIDFromContext(svc.ctx))
|
||||
}
|
||||
if tc.err == nil && (body.Data == nil || body.Data.UpdatedDocuments != 2 || body.Data.UnchangedDocuments != 1) {
|
||||
t.Fatalf("response=%s", rec.Body.String())
|
||||
|
||||
@@ -190,8 +190,8 @@ func TestMapDocumentListItemIncludesLatestIngestionEvent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeDocumentService) Ingest(ctx context.Context, userID string, req *document.IngestDocumentRequest) (common.ErrorCode, error) {
|
||||
f.ingestUserID = userID
|
||||
func (f *fakeDocumentService) Ingest(ctx context.Context, req *document.IngestDocumentRequest) (common.ErrorCode, error) {
|
||||
f.ingestUserID = permission.UserIDFromContext(ctx)
|
||||
f.ingestReq = req
|
||||
if f.ingestCode != 0 || f.ingestErr != nil {
|
||||
return f.ingestCode, f.ingestErr
|
||||
@@ -201,14 +201,14 @@ func (f *fakeDocumentService) Ingest(ctx context.Context, userID string, req *do
|
||||
|
||||
const uploadTestDatasetID = "123e4567-e89b-12d3-a456-426614174000"
|
||||
|
||||
func (f *fakeDocumentService) UpdateDatasetDocument(ctx context.Context, userID, datasetID, documentID string, req *document.UpdateDatasetDocumentRequest, present map[string]bool) (*document.UpdateDatasetDocumentResponse, common.ErrorCode, error) {
|
||||
func (f *fakeDocumentService) UpdateDatasetDocument(ctx context.Context, datasetID, documentID string, req *document.UpdateDatasetDocumentRequest, present map[string]bool) (*document.UpdateDatasetDocumentResponse, common.ErrorCode, error) {
|
||||
return nil, common.CodeSuccess, nil
|
||||
}
|
||||
func (f *fakeDocumentService) BatchUpdateDocumentMetadatas(ctx context.Context, datasetID string, selector *document.MetadataSelector, updates []document.MetadataUpdate, deletes []document.MetadataDelete) (*document.BatchUpdateMetadatasResponse, common.ErrorCode, error) {
|
||||
return nil, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (f *fakeDocumentService) GetDocumentArtifact(ctx context.Context, filename, _ string) (*document.ArtifactResponse, error) {
|
||||
func (f *fakeDocumentService) GetDocumentArtifact(ctx context.Context, filename string) (*document.ArtifactResponse, error) {
|
||||
if filename == "error.txt" {
|
||||
return nil, document.ErrArtifactNotFound
|
||||
}
|
||||
@@ -270,10 +270,10 @@ func (f *fakeDocumentService) DeleteDocument(ctx context.Context, id string) err
|
||||
f.deletedID = id
|
||||
return nil
|
||||
}
|
||||
func (f *fakeDocumentService) DeleteDocuments(ctx context.Context, ids []string, deleteAll bool, datasetID, userID string) (int, error) {
|
||||
func (f *fakeDocumentService) DeleteDocuments(ctx context.Context, ids []string, deleteAll bool, datasetID string) (int, error) {
|
||||
return f.deleted, f.err
|
||||
}
|
||||
func (f *fakeDocumentService) ParseDocuments(ctx context.Context, datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
func (f *fakeDocumentService) ParseDocuments(ctx context.Context, datasetID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeDocumentService) StopParseDocuments(ctx context.Context, datasetID string, docIDs []string) (map[string]interface{}, error) {
|
||||
@@ -319,21 +319,21 @@ func (f *fakeDocumentService) GetMetadataByKBs(ctx context.Context, kbIDs []stri
|
||||
}
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
func (f *fakeDocumentService) BatchUpdateDocumentStatus(ctx context.Context, userID, datasetID, status string, documentIDs []string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (f *fakeDocumentService) BatchUpdateDocumentStatus(ctx context.Context, datasetID, status string, documentIDs []string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
return map[string]interface{}{}, common.CodeSuccess, nil
|
||||
}
|
||||
func (f *fakeDocumentService) GetThumbnails(ctx context.Context, userID string, docIDs []string) (map[string]string, error) {
|
||||
f.thumbnailUserID = userID
|
||||
func (f *fakeDocumentService) GetThumbnails(ctx context.Context, docIDs []string) (map[string]string, error) {
|
||||
f.thumbnailUserID = permission.UserIDFromContext(ctx)
|
||||
f.thumbnailDocIDs = append([]string(nil), docIDs...)
|
||||
return f.thumbnails, f.thumbnailErr
|
||||
}
|
||||
func (f *fakeDocumentService) GetDocumentImage(ctx context.Context, userID, imageID string) ([]byte, error) {
|
||||
f.imageUserID = userID
|
||||
func (f *fakeDocumentService) GetDocumentImage(ctx context.Context, imageID string) ([]byte, error) {
|
||||
f.imageUserID = permission.UserIDFromContext(ctx)
|
||||
f.imageID = imageID
|
||||
return f.imageData, f.imageErr
|
||||
}
|
||||
func (f *fakeDocumentService) GetDocumentImageForDocument(ctx context.Context, userID, docID, imageID string) ([]byte, error) {
|
||||
f.imageUserID = userID
|
||||
func (f *fakeDocumentService) GetDocumentImageForDocument(ctx context.Context, docID, imageID string) ([]byte, error) {
|
||||
f.imageUserID = permission.UserIDFromContext(ctx)
|
||||
f.imageDocumentID = docID
|
||||
f.imageID = imageID
|
||||
return f.imageData, f.imageErr
|
||||
@@ -370,16 +370,16 @@ func (f *fakeDocumentService) UploadEmptyDocument(ctx context.Context, kb *entit
|
||||
return nil, common.CodeServerError, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (f *fakeDocumentService) ListIngestionTasks(ctx context.Context, userID string, datasetID *string, page, pageSize int) ([]*entity.IngestionTask, error) {
|
||||
func (f *fakeDocumentService) ListIngestionTasks(ctx context.Context, datasetID *string, page, pageSize int) ([]*entity.IngestionTask, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeDocumentService) IngestDocuments(ctx context.Context, datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
func (f *fakeDocumentService) IngestDocuments(ctx context.Context, datasetID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeDocumentService) StopIngestionTasks(ctx context.Context, tasks []string, userID string) ([]*entity.IngestionTask, error) {
|
||||
func (f *fakeDocumentService) StopIngestionTasks(ctx context.Context, tasks []string) ([]*entity.IngestionTask, error) {
|
||||
return f.stopIngestionTasks, f.stopIngestionTaskErr
|
||||
}
|
||||
func (f *fakeDocumentService) RemoveIngestionTasks(ctx context.Context, tasks []string, userID string) ([]map[string]string, error) {
|
||||
func (f *fakeDocumentService) RemoveIngestionTasks(ctx context.Context, tasks []string) ([]map[string]string, error) {
|
||||
return f.removeIngestionTasks, f.removeIngestionTaskErr
|
||||
}
|
||||
|
||||
@@ -830,6 +830,7 @@ func setupDocumentIngestRoute(userID string, svc *fakeDocumentService) *gin.Engi
|
||||
}
|
||||
r := gin.New()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Request = c.Request.WithContext(permission.WithIdentity(c.Request.Context(), userID, ""))
|
||||
c.Set("user", &entity.User{ID: userID})
|
||||
c.Set("user_id", userID)
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"ragflow/internal/common"
|
||||
"ragflow/internal/dao"
|
||||
"ragflow/internal/mcp"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/service"
|
||||
"ragflow/internal/service/document"
|
||||
)
|
||||
@@ -39,15 +40,22 @@ func validateRetrievalWindow(page, pageSize int) error {
|
||||
// the MCP retrieval entry point can be tested against a recording fake without
|
||||
// standing up the full DatasetService.
|
||||
type mcpDatasetService interface {
|
||||
ListDatasets(ctx context.Context, id, name string, page, pageSize int, terms []dao.OrderTerm, keywords string, ownerIDs []string, parserID, userID string, ids []string, canvasID string) ([]map[string]interface{}, int64, common.ErrorCode, error)
|
||||
SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest, userID string) (*service.SearchDatasetsResponse, error)
|
||||
GetDataset(ctx context.Context, datasetID, userID string) (map[string]interface{}, common.ErrorCode, error)
|
||||
ListDatasets(ctx context.Context, id, name string, page, pageSize int, terms []dao.OrderTerm, keywords string, ownerIDs []string, parserID string, ids []string, canvasID string) ([]map[string]interface{}, int64, common.ErrorCode, error)
|
||||
SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest) (*service.SearchDatasetsResponse, error)
|
||||
GetDataset(ctx context.Context, datasetID string) (map[string]interface{}, common.ErrorCode, error)
|
||||
}
|
||||
|
||||
// mcpRetrieval executes a retrieval request on behalf of the MCP tool handler.
|
||||
// It translates the mcp.RetrievalRequest into a service.SearchDatasetsRequest
|
||||
// and calls DatasetService.SearchDatasets. The result is serialized as JSON.
|
||||
func mcpRetrieval(ctx context.Context, ds mcpDatasetService, userID string, req mcp.RetrievalRequest) (string, error) {
|
||||
contextUserID := permission.UserIDFromContext(ctx)
|
||||
if contextUserID == "" {
|
||||
ctx = permission.WithIdentity(ctx, userID, permission.TenantIDFromContext(ctx))
|
||||
} else if contextUserID != userID {
|
||||
return "", permission.ErrIdentityMismatch
|
||||
}
|
||||
|
||||
if err := validateRetrievalWindow(req.Page, req.PageSize); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -59,7 +67,7 @@ func mcpRetrieval(ctx context.Context, ds mcpDatasetService, userID string, req
|
||||
ids, err := fetchAllDatasetIDs(func(page, pageSize int) ([]map[string]interface{}, int64, error) {
|
||||
data, total, _, err := ds.ListDatasets(ctx,
|
||||
"", "", page, pageSize, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", nil, "", userID, nil, "",
|
||||
"", nil, "", nil, "",
|
||||
)
|
||||
return data, total, err
|
||||
}, maxPageSize)
|
||||
@@ -111,7 +119,7 @@ func mcpRetrieval(ctx context.Context, ds mcpDatasetService, userID string, req
|
||||
searchReq.Keyword = &v
|
||||
}
|
||||
|
||||
resp, err := ds.SearchDatasets(ctx, searchReq, userID)
|
||||
resp, err := ds.SearchDatasets(ctx, searchReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -121,7 +129,7 @@ func mcpRetrieval(ctx context.Context, ds mcpDatasetService, userID string, req
|
||||
datasetNames := make(map[string]string)
|
||||
documentMetadata := make(map[string]map[string]any)
|
||||
for _, id := range datasetIDs {
|
||||
info, code, err := ds.GetDataset(ctx, id, userID)
|
||||
info, code, err := ds.GetDataset(ctx, id)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"ragflow/internal/dao"
|
||||
"ragflow/internal/ingestion/knowledge_compile"
|
||||
"ragflow/internal/mcp"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/service"
|
||||
)
|
||||
|
||||
@@ -35,17 +36,17 @@ type recordingDatasetService struct {
|
||||
searchErr error
|
||||
}
|
||||
|
||||
func (f *recordingDatasetService) ListDatasets(_ context.Context, _, _ string, page, pageSize int, _ []dao.OrderTerm, _ string, _ []string, _, _ string, _ []string, _ string) ([]map[string]interface{}, int64, common.ErrorCode, error) {
|
||||
func (f *recordingDatasetService) ListDatasets(_ context.Context, _, _ string, page, pageSize int, _ []dao.OrderTerm, _ string, _ []string, _ string, _ []string, _ string) ([]map[string]interface{}, int64, common.ErrorCode, error) {
|
||||
f.listCalls++
|
||||
f.listPages = append(f.listPages, page)
|
||||
f.listSizes = append(f.listSizes, pageSize)
|
||||
return f.listRows, int64(len(f.listRows)), common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (f *recordingDatasetService) SearchDatasets(_ context.Context, req *service.SearchDatasetsRequest, userID string) (*service.SearchDatasetsResponse, error) {
|
||||
func (f *recordingDatasetService) SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest) (*service.SearchDatasetsResponse, error) {
|
||||
f.searchCalls++
|
||||
f.request = req
|
||||
f.searchUser = userID
|
||||
f.searchUser = permission.UserIDFromContext(ctx)
|
||||
if f.searchErr != nil {
|
||||
return nil, f.searchErr
|
||||
}
|
||||
@@ -55,7 +56,7 @@ func (f *recordingDatasetService) SearchDatasets(_ context.Context, req *service
|
||||
return f.response, nil
|
||||
}
|
||||
|
||||
func (f *recordingDatasetService) GetDataset(_ context.Context, datasetID, _ string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (f *recordingDatasetService) GetDataset(_ context.Context, datasetID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
f.getCalls++
|
||||
f.getIDs = append(f.getIDs, datasetID)
|
||||
return map[string]interface{}{"name": "dataset-" + datasetID}, common.CodeSuccess, nil
|
||||
|
||||
@@ -118,9 +118,15 @@ func (h *MCPServerHandler) HandleMCP(c *gin.Context) {
|
||||
// mcpListDatasets wraps DatasetService.ListDatasets for the MCP tool handler,
|
||||
// filling in default values for parameters that the MCP tool does not expose.
|
||||
func mcpListDatasets(ctx context.Context, ds *dataset.DatasetService, userID string, page, pageSize int, orderby string, desc bool) ([]map[string]interface{}, int64, error) {
|
||||
contextUserID := permission.UserIDFromContext(ctx)
|
||||
if contextUserID == "" {
|
||||
ctx = permission.WithIdentity(ctx, userID, permission.TenantIDFromContext(ctx))
|
||||
} else if contextUserID != userID {
|
||||
return nil, 0, permission.ErrIdentityMismatch
|
||||
}
|
||||
data, total, _, err := ds.ListDatasets(ctx,
|
||||
"", "", page, pageSize, []dao.OrderTerm{{Column: orderby, Desc: desc}},
|
||||
"", nil, "", userID, nil, "",
|
||||
"", nil, "", nil, "",
|
||||
)
|
||||
return data, total, err
|
||||
}
|
||||
|
||||
@@ -86,13 +86,13 @@ type ChunkService struct {
|
||||
ingestionTaskDAO *dao.IngestionTaskDAO
|
||||
searchService *service.SearchService
|
||||
|
||||
checkDatasetAccessFunc func(string, string) error
|
||||
checkDatasetAccessFunc func(string) error
|
||||
getKnowledgebaseByIDFunc func(string) (*entity.Knowledgebase, error)
|
||||
getDocumentsByIDsFunc func([]string) ([]*entity.Document, error)
|
||||
// startParseDocumentsFunc overrides the DSL start-parse flow. Production
|
||||
// uses service.DocumentService.StartParseDocuments; tests inject a fake
|
||||
// to avoid the MQ publisher.
|
||||
startParseDocumentsFunc func(ctx context.Context, doc *entity.Document, kb *entity.Knowledgebase, userID string, opts document.StartParseOptions) error
|
||||
startParseDocumentsFunc func(ctx context.Context, doc *entity.Document, kb *entity.Knowledgebase, opts document.StartParseOptions) error
|
||||
// cancelIngestionTaskFunc overrides the document-parsing cancellation.
|
||||
// Production uses service.DocumentService.CancelDocParse; tests inject
|
||||
// a fake to avoid the MQ publisher.
|
||||
@@ -557,7 +557,7 @@ func hydrateChunkVectors(ctx context.Context, engine engine.DocEngine, chunks []
|
||||
}
|
||||
|
||||
// Get retrieves a chunk by ID
|
||||
func (s *ChunkService) Get(ctx context.Context, req *service.GetChunkRequest, userID string) (*service.GetChunkResponse, error) {
|
||||
func (s *ChunkService) Get(ctx context.Context, req *service.GetChunkRequest) (*service.GetChunkResponse, error) {
|
||||
if s.docEngine == nil {
|
||||
return nil, fmt.Errorf("doc engine not initialized")
|
||||
}
|
||||
@@ -566,7 +566,7 @@ func (s *ChunkService) Get(ctx context.Context, req *service.GetChunkRequest, us
|
||||
return nil, fmt.Errorf("chunk_id is required")
|
||||
}
|
||||
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, req.DatasetID, userID, permission.OperationRead)
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, req.DatasetID, permission.OperationRead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -634,7 +634,7 @@ func (s *ChunkService) cancelAllTasksOfDoc(ctx context.Context, doc *entity.Docu
|
||||
return cancel(ctx, doc)
|
||||
}
|
||||
|
||||
func (s *ChunkService) StopParsing(ctx context.Context, userID, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
func (s *ChunkService) StopParsing(ctx context.Context, datasetID string, req service.StopParsingRequest) (*service.StopParsingResponse, common.ErrorCode, error) {
|
||||
if err := permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindDataset, ID: datasetID}, permission.OperationRun); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
return nil, code, permissionErr
|
||||
@@ -706,9 +706,9 @@ func checkDuplicateIDs(documentIDs []string, idTypes string) ([]string, []string
|
||||
return uniqueDocIDs, duplicateMessages
|
||||
}
|
||||
|
||||
func (s *ChunkService) checkDatasetAccess(ctx context.Context, datasetID, userID string, operation permission.Operation) error {
|
||||
func (s *ChunkService) checkDatasetAccess(ctx context.Context, datasetID string, operation permission.Operation) error {
|
||||
if s.checkDatasetAccessFunc != nil {
|
||||
return s.checkDatasetAccessFunc(datasetID, userID)
|
||||
return s.checkDatasetAccessFunc(datasetID)
|
||||
}
|
||||
return permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindDataset, ID: datasetID}, operation)
|
||||
}
|
||||
@@ -720,8 +720,8 @@ func (s *ChunkService) getKnowledgebaseByID(ctx context.Context, datasetID strin
|
||||
return s.kbDAO.GetByID(ctx, dao.DB, datasetID)
|
||||
}
|
||||
|
||||
func (s *ChunkService) getAccessibleKnowledgebase(ctx context.Context, datasetID, userID string, operation permission.Operation) (*entity.Knowledgebase, error) {
|
||||
if err := s.checkDatasetAccess(ctx, datasetID, userID, operation); err != nil {
|
||||
func (s *ChunkService) getAccessibleKnowledgebase(ctx context.Context, datasetID string, operation permission.Operation) (*entity.Knowledgebase, error) {
|
||||
if err := s.checkDatasetAccess(ctx, datasetID, operation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kb, err := s.getKnowledgebaseByID(ctx, datasetID)
|
||||
@@ -738,8 +738,8 @@ func (s *ChunkService) getDocumentsByIDs(ctx context.Context, docIDs []string) (
|
||||
return s.documentDAO.GetByIDs(ctx, dao.DB, docIDs)
|
||||
}
|
||||
|
||||
func (s *ChunkService) Parse(ctx context.Context, userID, datasetID string, req *service.ParseFileRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
if err := s.checkDatasetAccess(ctx, datasetID, userID, permission.OperationRun); err != nil {
|
||||
func (s *ChunkService) Parse(ctx context.Context, datasetID string, req *service.ParseFileRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
if err := s.checkDatasetAccess(ctx, datasetID, permission.OperationRun); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
return nil, code, permissionErr
|
||||
}
|
||||
@@ -789,7 +789,7 @@ func (s *ChunkService) Parse(ctx context.Context, userID, datasetID string, req
|
||||
|
||||
for _, docID := range docIDs {
|
||||
doc := docByID[docID]
|
||||
if err = startParse(ctx, doc, kb, userID, document.StartParseOptions{RerunWithDelete: true}); err != nil {
|
||||
if err = startParse(ctx, doc, kb, document.StartParseOptions{RerunWithDelete: true}); err != nil {
|
||||
return nil, common.CodeServerError, err
|
||||
}
|
||||
successCount++
|
||||
@@ -808,7 +808,7 @@ func (s *ChunkService) Parse(ctx context.Context, userID, datasetID string, req
|
||||
}
|
||||
|
||||
// List retrieves chunks for a document
|
||||
func (s *ChunkService) List(ctx context.Context, req *service.ListChunksRequest, userID string) (*service.ListChunksResponse, error) {
|
||||
func (s *ChunkService) List(ctx context.Context, req *service.ListChunksRequest) (*service.ListChunksResponse, error) {
|
||||
if s.docEngine == nil {
|
||||
return nil, fmt.Errorf("doc engine not initialized")
|
||||
}
|
||||
@@ -827,7 +827,7 @@ func (s *ChunkService) List(ctx context.Context, req *service.ListChunksRequest,
|
||||
return nil, fmt.Errorf("document not found")
|
||||
}
|
||||
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, doc.KbID, userID, permission.OperationRead)
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, doc.KbID, permission.OperationRead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1049,7 +1049,7 @@ func (s *ChunkService) List(ctx context.Context, req *service.ListChunksRequest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ChunkService) SwitchChunks(ctx context.Context, userID, datasetID, documentID string, availableInt int, chunkIDs []string) error {
|
||||
func (s *ChunkService) SwitchChunks(ctx context.Context, datasetID, documentID string, availableInt int, chunkIDs []string) error {
|
||||
if s.docEngine == nil {
|
||||
return fmt.Errorf("doc engine not initialized")
|
||||
}
|
||||
@@ -1062,7 +1062,7 @@ func (s *ChunkService) SwitchChunks(ctx context.Context, userID, datasetID, docu
|
||||
return fmt.Errorf("req is null")
|
||||
}
|
||||
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, datasetID, userID, permission.OperationUpdate)
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, datasetID, permission.OperationUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1095,7 +1095,7 @@ func (s *ChunkService) SwitchChunks(ctx context.Context, userID, datasetID, docu
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ChunkService) UpdateChunk(ctx context.Context, req *service.UpdateChunkRequest, userID string) error {
|
||||
func (s *ChunkService) UpdateChunk(ctx context.Context, req *service.UpdateChunkRequest) error {
|
||||
if s.docEngine == nil {
|
||||
return fmt.Errorf("doc engine not initialized")
|
||||
}
|
||||
@@ -1104,7 +1104,7 @@ func (s *ChunkService) UpdateChunk(ctx context.Context, req *service.UpdateChunk
|
||||
return fmt.Errorf("chunk_id is required")
|
||||
}
|
||||
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, req.DatasetID, userID, permission.OperationUpdate)
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, req.DatasetID, permission.OperationUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1271,7 +1271,7 @@ func (s *ChunkService) UpdateChunk(ctx context.Context, req *service.UpdateChunk
|
||||
|
||||
return nil
|
||||
}
|
||||
func (s *ChunkService) RemoveChunks(ctx context.Context, req *service.RemoveChunksRequest, userID string) (int64, error) {
|
||||
func (s *ChunkService) RemoveChunks(ctx context.Context, req *service.RemoveChunksRequest) (int64, error) {
|
||||
if s.docEngine == nil {
|
||||
return 0, fmt.Errorf("doc engine not initialized")
|
||||
}
|
||||
@@ -1290,7 +1290,7 @@ func (s *ChunkService) RemoveChunks(ctx context.Context, req *service.RemoveChun
|
||||
return 0, permission.ErrResourceNotFound
|
||||
}
|
||||
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, doc.KbID, userID, permission.OperationUpdate)
|
||||
kb, err := s.getAccessibleKnowledgebase(ctx, doc.KbID, permission.OperationUpdate)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -1381,14 +1381,14 @@ func (s *ChunkService) nextSourceChunkOrder(ctx context.Context, indexName, data
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *ChunkService) AddChunk(ctx context.Context, req *service.AddChunkRequest, userID string) (*service.AddChunkResponse, error) {
|
||||
func (s *ChunkService) AddChunk(ctx context.Context, req *service.AddChunkRequest) (*service.AddChunkResponse, error) {
|
||||
if s.docEngine == nil {
|
||||
return nil, addChunkError{code: common.CodeServerError, message: "doc engine not initialized"}
|
||||
}
|
||||
if req == nil {
|
||||
return nil, addChunkError{code: common.CodeDataError, message: "invalid request payload"}
|
||||
}
|
||||
if err := s.checkDatasetAccess(ctx, req.DatasetID, userID, permission.OperationUpdate); err != nil {
|
||||
if err := s.checkDatasetAccess(ctx, req.DatasetID, permission.OperationUpdate); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
return nil, addChunkError{code: code, message: permissionErr.Error(), cause: permissionErr}
|
||||
}
|
||||
|
||||
@@ -68,17 +68,17 @@ func TestChunkManagementDatasetPermissions(t *testing.T) {
|
||||
}}
|
||||
_, err := s.Get(ctx, &service.GetChunkRequest{
|
||||
DatasetID: "kb-1", DocumentID: "doc-1", ChunkID: "chunk-1",
|
||||
}, tt.userID)
|
||||
})
|
||||
return err
|
||||
}},
|
||||
{"list", func(s *ChunkService) error {
|
||||
s.docEngine = &listChunksSearchEngine{}
|
||||
_, err := s.List(ctx, &service.ListChunksRequest{DatasetID: "kb-1", DocID: "doc-1"}, tt.userID)
|
||||
_, err := s.List(ctx, &service.ListChunksRequest{DatasetID: "kb-1", DocID: "doc-1"})
|
||||
return err
|
||||
}},
|
||||
{"switch", func(s *ChunkService) error {
|
||||
s.docEngine = &updateChunkTestEngine{}
|
||||
return s.SwitchChunks(ctx, tt.userID, "kb-1", "doc-1", 0, []string{"chunk-1"})
|
||||
return s.SwitchChunks(ctx, "kb-1", "doc-1", 0, []string{"chunk-1"})
|
||||
}},
|
||||
{"update", func(s *ChunkService) error {
|
||||
s.docEngine = &updateChunkTestEngine{existingChunk: map[string]interface{}{
|
||||
@@ -86,11 +86,11 @@ func TestChunkManagementDatasetPermissions(t *testing.T) {
|
||||
}}
|
||||
return s.UpdateChunk(ctx, &service.UpdateChunkRequest{
|
||||
DatasetID: "kb-1", DocumentID: "doc-1", ChunkID: "chunk-1",
|
||||
}, tt.userID)
|
||||
})
|
||||
}},
|
||||
{"remove", func(s *ChunkService) error {
|
||||
s.docEngine = &parseTestDocEngine{}
|
||||
_, err := s.RemoveChunks(ctx, &service.RemoveChunksRequest{DatasetID: "kb-1", DocID: "doc-1", DeleteAll: true}, tt.userID)
|
||||
_, err := s.RemoveChunks(ctx, &service.RemoveChunksRequest{DatasetID: "kb-1", DocID: "doc-1", DeleteAll: true})
|
||||
return err
|
||||
}},
|
||||
} {
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestSDKChunkListSchemaAndEmptyDeletion(t *testing.T) {
|
||||
engine.chunks = append(engine.chunks, map[string]interface{}{"id": "chunk-2", "content_with_weight": "plain"})
|
||||
svc := &ChunkService{docEngine: engine, kbDAO: dao.NewKnowledgebaseDAO()}
|
||||
ctx := chunkTestContext(t, "user-1", "tenant-1")
|
||||
resp, err := svc.List(ctx, &service.ListChunksRequest{DatasetID: "kb-1", DocID: "doc-1"}, "user-1")
|
||||
resp, err := svc.List(ctx, &service.ListChunksRequest{DatasetID: "kb-1", DocID: "doc-1"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func TestSDKChunkListSchemaAndEmptyDeletion(t *testing.T) {
|
||||
}
|
||||
}
|
||||
for _, ids := range [][]string{nil, {}} {
|
||||
n, err := svc.RemoveChunks(ctx, &service.RemoveChunksRequest{DatasetID: "kb-1", DocID: "doc-1", ChunkIDs: ids}, "user-1")
|
||||
n, err := svc.RemoveChunks(ctx, &service.RemoveChunksRequest{DatasetID: "kb-1", DocID: "doc-1", ChunkIDs: ids})
|
||||
if err != nil || n != 0 {
|
||||
t.Errorf("empty selection: count=%d err=%v", n, err)
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ func TestParsePrevalidatesDocumentsBeforeMutating(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := chunkTestContext(t, userID, userID)
|
||||
_, code, err := svc.Parse(ctx, userID, datasetID, &service.ParseFileRequest{
|
||||
_, code, err := svc.Parse(ctx, datasetID, &service.ParseFileRequest{
|
||||
DocumentIDs: []string{"doc-1", "missing-doc", "doc-2"},
|
||||
})
|
||||
if err == nil {
|
||||
@@ -213,9 +213,9 @@ func TestParsePrevalidatesDocumentsBeforeMutating(t *testing.T) {
|
||||
|
||||
func TestParseRejectsInaccessibleDataset(t *testing.T) {
|
||||
svc := newParseTestService(t)
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return permission.ErrPermissionDenied }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return permission.ErrPermissionDenied }
|
||||
ctx := t.Context()
|
||||
_, code, err := svc.Parse(ctx, "user-1", "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, code, err := svc.Parse(ctx, "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err == nil {
|
||||
t.Fatal("expected parse to fail")
|
||||
}
|
||||
@@ -229,10 +229,10 @@ func TestParseRejectsInaccessibleDataset(t *testing.T) {
|
||||
|
||||
func TestParseRequiresDocumentIDs(t *testing.T) {
|
||||
svc := newParseTestService(t)
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return nil }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return nil }
|
||||
ctx := t.Context()
|
||||
for _, req := range []*service.ParseFileRequest{nil, {DocumentIDs: nil}, {DocumentIDs: []string{}}} {
|
||||
_, code, err := svc.Parse(ctx, "user-1", "kb-1", req)
|
||||
_, code, err := svc.Parse(ctx, "kb-1", req)
|
||||
if err == nil {
|
||||
t.Fatal("expected parse to fail")
|
||||
}
|
||||
@@ -247,10 +247,10 @@ func TestParseRequiresDocumentIDs(t *testing.T) {
|
||||
|
||||
func TestParseReturnsDataErrorWhenDatasetMissing(t *testing.T) {
|
||||
svc := newParseTestService(t)
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return nil }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return nil }
|
||||
svc.getKnowledgebaseByIDFunc = func(string) (*entity.Knowledgebase, error) { return nil, nil }
|
||||
ctx := t.Context()
|
||||
_, code, err := svc.Parse(ctx, "user-1", "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, code, err := svc.Parse(ctx, "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err == nil {
|
||||
t.Fatal("expected parse to fail")
|
||||
}
|
||||
@@ -265,7 +265,7 @@ func TestParseReturnsDataErrorWhenDatasetMissing(t *testing.T) {
|
||||
func TestParseReturnsServerErrorWhenDocumentsQueryFails(t *testing.T) {
|
||||
svc := newParseTestService(t)
|
||||
queryErr := errors.New("documents query failed")
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return nil }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return nil }
|
||||
svc.getKnowledgebaseByIDFunc = func(string) (*entity.Knowledgebase, error) {
|
||||
return &entity.Knowledgebase{ID: "kb-1", TenantID: "user-1"}, nil
|
||||
}
|
||||
@@ -273,7 +273,7 @@ func TestParseReturnsServerErrorWhenDocumentsQueryFails(t *testing.T) {
|
||||
return nil, queryErr
|
||||
}
|
||||
ctx := t.Context()
|
||||
_, code, err := svc.Parse(ctx, "user-1", "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, code, err := svc.Parse(ctx, "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if !errors.Is(err, queryErr) {
|
||||
t.Fatalf("expected query error, got %v", err)
|
||||
}
|
||||
@@ -294,7 +294,7 @@ func TestParseRejectsRunningDocument(t *testing.T) {
|
||||
|
||||
svc := newParseTestService(t)
|
||||
ctx := chunkTestContext(t, userID, userID)
|
||||
_, code, err := svc.Parse(ctx, userID, datasetID, &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, code, err := svc.Parse(ctx, datasetID, &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err == nil {
|
||||
t.Fatal("expected parse to fail")
|
||||
}
|
||||
@@ -333,7 +333,7 @@ func TestListSortsChunksByDocumentPosition(t *testing.T) {
|
||||
DocID: documentID,
|
||||
Page: &page,
|
||||
Size: &size,
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("List() error = %v", err)
|
||||
}
|
||||
@@ -408,7 +408,7 @@ func TestListReportsChunkAvailability(t *testing.T) {
|
||||
DocID: documentID,
|
||||
Page: &page,
|
||||
Size: &size,
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("List() error = %v", err)
|
||||
}
|
||||
@@ -463,7 +463,7 @@ func TestListScopesSearchToDocumentDataset(t *testing.T) {
|
||||
DocID: documentID,
|
||||
Page: &page,
|
||||
Size: &size,
|
||||
}, userID); err != nil {
|
||||
}); err != nil {
|
||||
t.Fatalf("List() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ func TestGetScopesChunkLookupToDocumentDataset(t *testing.T) {
|
||||
DatasetID: datasetID,
|
||||
DocumentID: documentID,
|
||||
ChunkID: "chunk-1",
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Get() error = %v", err)
|
||||
}
|
||||
@@ -572,7 +572,7 @@ func TestGetRejectsChunkOfAnotherDocument(t *testing.T) {
|
||||
DatasetID: datasetID,
|
||||
DocumentID: documentID,
|
||||
ChunkID: "chunk-1",
|
||||
}, userID)
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "chunk not found") {
|
||||
t.Fatalf("Get() error = %v, want chunk not found", err)
|
||||
}
|
||||
@@ -607,7 +607,7 @@ func TestGetRequiresDocumentInRouteDataset(t *testing.T) {
|
||||
DatasetID: datasetID,
|
||||
DocumentID: "doc-missing",
|
||||
ChunkID: "chunk-1",
|
||||
}, userID)
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "document not found") {
|
||||
t.Fatalf("Get() error = %v, want document not found", err)
|
||||
}
|
||||
@@ -616,7 +616,7 @@ func TestGetRequiresDocumentInRouteDataset(t *testing.T) {
|
||||
DatasetID: datasetID,
|
||||
DocumentID: "doc-2",
|
||||
ChunkID: "chunk-1",
|
||||
}, userID)
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "document does not belong to this dataset") {
|
||||
t.Fatalf("Get() error = %v, want document does not belong to this dataset", err)
|
||||
}
|
||||
@@ -694,7 +694,7 @@ func TestListBuildsMatchTextExprForKeywords(t *testing.T) {
|
||||
Page: &page,
|
||||
Size: &size,
|
||||
Keywords: " invoice terms ",
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("List() error = %v", err)
|
||||
}
|
||||
@@ -773,7 +773,7 @@ func TestUpdateChunkRejectsChunkFromAnotherDocument(t *testing.T) {
|
||||
DatasetID: "kb-1",
|
||||
DocumentID: "doc-a",
|
||||
ChunkID: "chunk-1",
|
||||
}, "user-1")
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("expected UpdateChunk to reject a chunk from another document")
|
||||
}
|
||||
@@ -808,7 +808,7 @@ func TestUpdateChunkUpdatesSameDocumentWithDocumentCondition(t *testing.T) {
|
||||
DatasetID: "kb-1",
|
||||
DocumentID: "doc-a",
|
||||
ChunkID: "chunk-1",
|
||||
}, "user-1")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateChunk() error = %v", err)
|
||||
}
|
||||
@@ -863,7 +863,7 @@ func TestUpdateChunkStoresTagKwdOnlyWhenSent(t *testing.T) {
|
||||
DocumentID: "doc-a",
|
||||
ChunkID: "chunk-1",
|
||||
TagKwd: tt.tagKwd,
|
||||
}, "user-1")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateChunk() error = %v", err)
|
||||
}
|
||||
@@ -922,7 +922,7 @@ func TestUpdateChunkStoresImageAndFlagsImageChunk(t *testing.T) {
|
||||
ChunkID: "chunk-1",
|
||||
ImageBase64: &imageBase64,
|
||||
ImageUpdateMode: tc.mode,
|
||||
}, "user-1")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateChunk() error = %v", err)
|
||||
}
|
||||
@@ -987,7 +987,7 @@ func TestUpdateChunkRemoveModeClearsImageAfterIndexUpdate(t *testing.T) {
|
||||
DocumentID: "doc-a",
|
||||
ChunkID: "chunk-1",
|
||||
ImageUpdateMode: &removeMode,
|
||||
}, "user-1")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateChunk() error = %v", err)
|
||||
}
|
||||
@@ -1043,7 +1043,7 @@ func TestUpdateChunkMarksWikiDirtyWhenImageRemovalFails(t *testing.T) {
|
||||
ChunkID: "chunk-1",
|
||||
Content: &content,
|
||||
ImageUpdateMode: &removeMode,
|
||||
}, "user-1")
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "Failed to remove chunk image") {
|
||||
t.Fatalf("UpdateChunk() error = %v, want the removal failure", err)
|
||||
}
|
||||
@@ -1098,7 +1098,7 @@ func TestUpdateChunkRejectsInvalidImageInput(t *testing.T) {
|
||||
ChunkID: "chunk-1",
|
||||
ImageBase64: tc.imageB64,
|
||||
ImageUpdateMode: tc.mode,
|
||||
}, "user-1")
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), tc.wantError) {
|
||||
t.Fatalf("UpdateChunk() error = %v, want %q", err, tc.wantError)
|
||||
}
|
||||
@@ -1152,8 +1152,8 @@ func TestAddChunkSuccess(t *testing.T) {
|
||||
docEngine: engine,
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
documentDAO: dao.NewDocumentDAO(),
|
||||
checkDatasetAccessFunc: func(datasetIDArg, userIDArg string) error {
|
||||
if datasetIDArg == datasetID && userIDArg == userID {
|
||||
checkDatasetAccessFunc: func(datasetIDArg string) error {
|
||||
if datasetIDArg == datasetID {
|
||||
return nil
|
||||
}
|
||||
return permission.ErrPermissionDenied
|
||||
@@ -1197,7 +1197,7 @@ func TestAddChunkSuccess(t *testing.T) {
|
||||
ImportantKeywords: []string{"k1"},
|
||||
Questions: []string{" q1 ", ""},
|
||||
TagFeas: map[string]interface{}{"tag1": float64(0.5)},
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddChunk() error = %v", err)
|
||||
}
|
||||
@@ -1306,8 +1306,8 @@ func TestAddChunkPersistsTagKwdOnlyWhenProvided(t *testing.T) {
|
||||
docEngine: engine,
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
documentDAO: dao.NewDocumentDAO(),
|
||||
checkDatasetAccessFunc: func(datasetIDArg, userIDArg string) error {
|
||||
if datasetIDArg != datasetID || userIDArg != userID {
|
||||
checkDatasetAccessFunc: func(datasetIDArg string) error {
|
||||
if datasetIDArg != datasetID {
|
||||
return permission.ErrPermissionDenied
|
||||
}
|
||||
return nil
|
||||
@@ -1339,7 +1339,7 @@ func TestAddChunkPersistsTagKwdOnlyWhenProvided(t *testing.T) {
|
||||
DatasetID: datasetID,
|
||||
DocumentID: documentID,
|
||||
Content: "chunk body",
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddChunk() error = %v", err)
|
||||
}
|
||||
@@ -1361,7 +1361,7 @@ func TestAddChunkPersistsTagKwdOnlyWhenProvided(t *testing.T) {
|
||||
DocumentID: documentID,
|
||||
Content: "chunk body",
|
||||
TagKwd: []string{},
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddChunk() error = %v", err)
|
||||
}
|
||||
@@ -1385,7 +1385,7 @@ func TestAddChunkPersistsTagKwdOnlyWhenProvided(t *testing.T) {
|
||||
DocumentID: documentID,
|
||||
Content: "chunk body",
|
||||
TagKwd: want,
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddChunk() error = %v", err)
|
||||
}
|
||||
@@ -1434,7 +1434,7 @@ func TestAddChunkValidationErrors(t *testing.T) {
|
||||
name: "empty content",
|
||||
req: &service.AddChunkRequest{DatasetID: "kb-1", DocumentID: "doc-1", Content: " "},
|
||||
setup: func(svc *ChunkService) {
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return nil }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return nil }
|
||||
svc.getKnowledgebaseByIDFunc = func(string) (*entity.Knowledgebase, error) {
|
||||
return &entity.Knowledgebase{ID: "kb-1", TenantID: "user-1", EmbdID: "embed-1"}, nil
|
||||
}
|
||||
@@ -1448,7 +1448,7 @@ func TestAddChunkValidationErrors(t *testing.T) {
|
||||
if tt.setup != nil {
|
||||
tt.setup(svc)
|
||||
}
|
||||
_, err := svc.AddChunk(ctx, tt.req, "user-1")
|
||||
_, err := svc.AddChunk(ctx, tt.req)
|
||||
if err == nil || !strings.Contains(err.Error(), tt.wantMsg) {
|
||||
t.Fatalf("error = %v, want substring %q", err, tt.wantMsg)
|
||||
}
|
||||
@@ -1469,7 +1469,7 @@ func TestAddChunkImageAndTagFeatureValidation(t *testing.T) {
|
||||
docEngine: &addChunkTestEngine{},
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
documentDAO: dao.NewDocumentDAO(),
|
||||
checkDatasetAccessFunc: func(string, string) error { return nil },
|
||||
checkDatasetAccessFunc: func(string) error { return nil },
|
||||
getKnowledgebaseByIDFunc: func(id string) (*entity.Knowledgebase, error) {
|
||||
return &entity.Knowledgebase{ID: id, TenantID: userID, EmbdID: "embed-1"}, nil
|
||||
},
|
||||
@@ -1504,7 +1504,7 @@ func TestAddChunkImageAndTagFeatureValidation(t *testing.T) {
|
||||
DocumentID: documentID,
|
||||
Content: "chunk body",
|
||||
ImageBase64: strPtr("not-base64"),
|
||||
}, userID)
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid `image_base64`") {
|
||||
t.Fatalf("expected invalid image error, got %v", err)
|
||||
}
|
||||
@@ -1516,7 +1516,7 @@ func TestAddChunkImageAndTagFeatureValidation(t *testing.T) {
|
||||
DocumentID: documentID,
|
||||
Content: "chunk body",
|
||||
TagFeas: map[string]interface{}{"tag1": "bad"},
|
||||
}, userID)
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "`tag_feas` values must be finite numbers") || resp != nil {
|
||||
t.Fatalf("expected tag_feas validation error, got resp=%#v err=%v", resp, err)
|
||||
}
|
||||
@@ -1527,7 +1527,7 @@ func TestAddChunkImageAndTagFeatureValidation(t *testing.T) {
|
||||
Content: "chunk body",
|
||||
TagFeas: map[string]interface{}{"tag1": float64(1)},
|
||||
ImageBase64: strPtr(validJPEG),
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddChunk() with image error = %v", err)
|
||||
}
|
||||
@@ -1553,7 +1553,7 @@ func TestAddChunkIncrementsStatsAfterInsert(t *testing.T) {
|
||||
docEngine: engine,
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
documentDAO: dao.NewDocumentDAO(),
|
||||
checkDatasetAccessFunc: func(string, string) error { return nil },
|
||||
checkDatasetAccessFunc: func(string) error { return nil },
|
||||
getKnowledgebaseByIDFunc: func(id string) (*entity.Knowledgebase, error) {
|
||||
return &entity.Knowledgebase{ID: id, TenantID: userID, EmbdID: "embed-1"}, nil
|
||||
},
|
||||
@@ -1580,7 +1580,7 @@ func TestAddChunkIncrementsStatsAfterInsert(t *testing.T) {
|
||||
DatasetID: datasetID,
|
||||
DocumentID: documentID,
|
||||
Content: "chunk body",
|
||||
}, userID)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddChunk() error = %v", err)
|
||||
}
|
||||
@@ -1773,7 +1773,7 @@ func TestRemoveChunksRejectsInvalidDatasetBeforeMutation(t *testing.T) {
|
||||
if !deleteAll {
|
||||
req.ChunkIDs = []string{"chunk-1"}
|
||||
}
|
||||
count, err := svc.RemoveChunks(t.Context(), req, "owner")
|
||||
count, err := svc.RemoveChunks(t.Context(), req)
|
||||
if err == nil || count != 0 {
|
||||
t.Fatalf("invalid dataset deletion = (%d, %v), want error and zero count", count, err)
|
||||
}
|
||||
@@ -1806,7 +1806,7 @@ func TestRemoveChunksDecrementsStatsAfterDelete(t *testing.T) {
|
||||
DatasetID: "kb-1",
|
||||
DocID: "doc-1",
|
||||
ChunkIDs: []string{"chunk-1", "chunk-2", "chunk-3"},
|
||||
}, "user-1")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("RemoveChunks() error = %v", err)
|
||||
}
|
||||
@@ -1871,7 +1871,7 @@ func TestRemoveChunksSkipsStatsWhenNothingDeleted(t *testing.T) {
|
||||
DatasetID: "kb-1",
|
||||
DocID: "doc-1",
|
||||
DeleteAll: true,
|
||||
}, "user-1")
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("RemoveChunks() error = %v", err)
|
||||
}
|
||||
@@ -1905,7 +1905,7 @@ func TestRemoveChunksReturnsStatsError(t *testing.T) {
|
||||
DatasetID: "kb-1",
|
||||
DocID: "doc-1",
|
||||
ChunkIDs: []string{"chunk-1", "chunk-2"},
|
||||
}, "user-1")
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "failed to update chunk stats") {
|
||||
t.Fatalf("expected stats update error, got count=%d err=%v", deletedCount, err)
|
||||
}
|
||||
@@ -2543,7 +2543,7 @@ func TestSwitchChunksUpdatesDocEngineWithAvailableInt(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := chunkTestContext(t, "user-1", "tenant-1")
|
||||
if err = svc.SwitchChunks(ctx, "user-1", "kb-1", "doc-1", 0, []string{"chunk-1", "chunk-2"}); err != nil {
|
||||
if err = svc.SwitchChunks(ctx, "kb-1", "doc-1", 0, []string{"chunk-1", "chunk-2"}); err != nil {
|
||||
t.Fatalf("SwitchChunks() error = %v", err)
|
||||
}
|
||||
|
||||
@@ -2679,9 +2679,9 @@ func TestChunkServiceParse_RejectsBatchWithRunningIngestionTask(t *testing.T) {
|
||||
insertChunkTestIngestionTask(t, "task-2", "user-1", "doc-2", "kb-1", common.RUNNING)
|
||||
|
||||
svc := newParseTestService(t)
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return nil }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return nil }
|
||||
ctx := t.Context()
|
||||
_, _, err := svc.Parse(ctx, "user-1", "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1", "doc-2"}})
|
||||
_, _, err := svc.Parse(ctx, "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1", "doc-2"}})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for RUNNING ingestion task, got nil")
|
||||
}
|
||||
@@ -2695,16 +2695,16 @@ func TestChunkServiceParse_CallsStartParseDocumentsWithRerunWithDelete(t *testin
|
||||
insertChunkTestDoc(t, "doc-1", "kb-1")
|
||||
|
||||
svc := newParseTestService(t)
|
||||
svc.checkDatasetAccessFunc = func(string, string) error { return nil }
|
||||
svc.checkDatasetAccessFunc = func(string) error { return nil }
|
||||
var calledOpts document.StartParseOptions
|
||||
var calledDocID string
|
||||
svc.startParseDocumentsFunc = func(ctx context.Context, doc *entity.Document, kb *entity.Knowledgebase, userID string, opts document.StartParseOptions) error {
|
||||
svc.startParseDocumentsFunc = func(ctx context.Context, doc *entity.Document, kb *entity.Knowledgebase, opts document.StartParseOptions) error {
|
||||
calledOpts = opts
|
||||
calledDocID = doc.ID
|
||||
return nil
|
||||
}
|
||||
ctx := t.Context()
|
||||
_, code, err := svc.Parse(ctx, "user-1", "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, code, err := svc.Parse(ctx, "kb-1", &service.ParseFileRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse: %v", err)
|
||||
}
|
||||
@@ -2727,12 +2727,12 @@ func TestChunkServiceParse_ReturnsPartialSuccessForDuplicateDocumentIDs(t *testi
|
||||
insertChunkTestDoc(t, "doc-1", datasetID)
|
||||
|
||||
svc := newParseTestService(t)
|
||||
svc.startParseDocumentsFunc = func(context.Context, *entity.Document, *entity.Knowledgebase, string, document.StartParseOptions) error {
|
||||
svc.startParseDocumentsFunc = func(context.Context, *entity.Document, *entity.Knowledgebase, document.StartParseOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx := chunkTestContext(t, userID, userID)
|
||||
result, code, err := svc.Parse(ctx, userID, datasetID, &service.ParseFileRequest{
|
||||
result, code, err := svc.Parse(ctx, datasetID, &service.ParseFileRequest{
|
||||
DocumentIDs: []string{"doc-1", "doc-1"},
|
||||
})
|
||||
if err == nil {
|
||||
@@ -2763,7 +2763,7 @@ func TestStopParsing_CallsCancelIngestionTask(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
ctx := chunkTestContext(t, "user-1", "user-1")
|
||||
_, _, err := svc.StopParsing(ctx, "user-1", "kb-1", service.StopParsingRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, _, err := svc.StopParsing(ctx, "kb-1", service.StopParsingRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("StopParsing: %v", err)
|
||||
}
|
||||
@@ -2786,7 +2786,7 @@ func TestStopParsing_RejectsDocumentWithoutIngestionTask(t *testing.T) {
|
||||
return document.ErrParseNotRunning
|
||||
}
|
||||
ctx := chunkTestContext(t, "user-1", "user-1")
|
||||
_, code, err := svc.StopParsing(ctx, "user-1", "kb-1", service.StopParsingRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, code, err := svc.StopParsing(ctx, "kb-1", service.StopParsingRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for document without ingestion task")
|
||||
}
|
||||
@@ -2821,7 +2821,7 @@ func TestStopParsing_DoesNotDeleteChunksOrResetCountersAfterCancel(t *testing.T)
|
||||
engine := &parseTestDocEngine{chunkStoreExists: true}
|
||||
svc.docEngine = engine
|
||||
ctx := chunkTestContext(t, "user-1", "user-1")
|
||||
_, _, err := svc.StopParsing(ctx, "user-1", "kb-1", service.StopParsingRequest{DocumentIDs: []string{"doc-1"}})
|
||||
_, _, err := svc.StopParsing(ctx, "kb-1", service.StopParsingRequest{DocumentIDs: []string{"doc-1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("StopParsing: %v", err)
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ type CompilationStatus struct {
|
||||
}
|
||||
|
||||
// GetDatasetCompilationStatus returns the scheduling-row lifecycle state for a
|
||||
// dataset after verifying the calling user owns it. When no row exists the
|
||||
// dataset after verifying the caller can read it. When no row exists the
|
||||
// dataset has never had any compile work, so the state is idle.
|
||||
func (d *DatasetService) GetDatasetCompilationStatus(ctx context.Context, userID, datasetID, kind string) (CompilationStatus, common.ErrorCode, error) {
|
||||
func (d *DatasetService) GetDatasetCompilationStatus(ctx context.Context, datasetID, kind string) (CompilationStatus, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return CompilationStatus{}, common.CodeDataError, errors.New("dataset_id is required")
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestGetDatasetCompilationStatus_NoRowIsIdle(t *testing.T) {
|
||||
insertCompilationOwnerKB(t, "kb-no-row", "user-1")
|
||||
|
||||
st, code, err := testCompilationStatusService().GetDatasetCompilationStatus(
|
||||
withDatasetTestContext(t.Context(), "user-1", "user-1"), "user-1", "kb-no-row", "graph")
|
||||
withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-no-row", "graph")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func TestGetDatasetCompilationStatus_FullOutput(t *testing.T) {
|
||||
}
|
||||
|
||||
st, code, err := testCompilationStatusService().GetDatasetCompilationStatus(
|
||||
withDatasetTestContext(t.Context(), "user-1", "user-1"), "user-1", "kb-full", "graph")
|
||||
withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-full", "graph")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -198,7 +198,7 @@ func TestGetDatasetCompilationStatus_Unauthorized(t *testing.T) {
|
||||
}
|
||||
|
||||
st, code, err := testCompilationStatusService().GetDatasetCompilationStatus(
|
||||
withDatasetTestContext(t.Context(), "user-2", "user-1"), "user-2", "kb-other", "graph")
|
||||
withDatasetTestContext(t.Context(), "user-2", "user-1"), "kb-other", "graph")
|
||||
if err == nil {
|
||||
t.Fatalf("expected authorization error, got nil (status=%+v)", st)
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func TestGetDatasetCompilationStatus_Unauthorized(t *testing.T) {
|
||||
func TestGetDatasetCompilationStatus_EmptyID(t *testing.T) {
|
||||
setupCompilationStatusTestDB(t)
|
||||
_, code, err := testCompilationStatusService().GetDatasetCompilationStatus(
|
||||
t.Context(), "user-1", "", "graph")
|
||||
t.Context(), "", "graph")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for empty dataset_id")
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ func (d *DatasetService) dedupeDatasetName(ctx context.Context, name, tenantID s
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DatasetService) GetDataset(ctx context.Context, datasetID, userID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) GetDataset(ctx context.Context, datasetID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
datasetID = strings.TrimSpace(datasetID)
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeDataError, errors.New("lack of \"Dataset ID\"")
|
||||
@@ -588,7 +588,7 @@ func datasetNameAndID(kb *entity.Knowledgebase) string {
|
||||
return fmt.Sprintf("%s (%s)", kb.Name, kb.ID)
|
||||
}
|
||||
|
||||
func (d *DatasetService) ListDatasets(ctx context.Context, id, name string, page, pageSize int, terms []dao.OrderTerm, keywords string, ownerIDs []string, parserID, userID string, ids []string, canvasID string) ([]map[string]interface{}, int64, common.ErrorCode, error) {
|
||||
func (d *DatasetService) ListDatasets(ctx context.Context, id, name string, page, pageSize int, terms []dao.OrderTerm, keywords string, ownerIDs []string, parserID string, ids []string, canvasID string) ([]map[string]interface{}, int64, common.ErrorCode, error) {
|
||||
id = strings.TrimSpace(id)
|
||||
canvasID = strings.TrimSpace(canvasID)
|
||||
if id != "" && len(ids) > 0 {
|
||||
@@ -722,7 +722,7 @@ func (d *DatasetService) ListDatasets(ctx context.Context, id, name string, page
|
||||
}
|
||||
if len(deniedIDs) > 0 {
|
||||
common.Warn("User lacks permission for datasets",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("user_id", permission.UserIDFromContext(ctx)),
|
||||
zap.Strings("dataset_ids", common.Deduplicate(deniedIDs)),
|
||||
)
|
||||
}
|
||||
@@ -751,7 +751,7 @@ func (d *DatasetService) ListDatasets(ctx context.Context, id, name string, page
|
||||
return data, total, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (d *DatasetService) ListDatasetFilters(ctx context.Context, userID, canvasID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) ListDatasetFilters(ctx context.Context, canvasID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
owners := make([]*entity.DatasetOwnerFilter, 0)
|
||||
err := dao.DB.Transaction(func(tx *gorm.DB) error {
|
||||
query := permission.ScopeQuery{Kind: permission.ResourceKindDataset, Operation: permission.OperationRead}
|
||||
|
||||
@@ -37,7 +37,7 @@ type embeddingCheckSample struct {
|
||||
// CheckEmbedding verifies that a candidate embedding model is compatible with a
|
||||
// dataset's existing vectors (the standard "switch embedding model" validation).
|
||||
// It is independent of the retired RunIndex/graph_rag_queue scheduling path.
|
||||
func (d *DatasetService) CheckEmbedding(ctx context.Context, userID, datasetID string, req *service.CheckEmbeddingRequest) (*service.EmbeddingCheckResponse, common.ErrorCode, error) {
|
||||
func (d *DatasetService) CheckEmbedding(ctx context.Context, datasetID string, req *service.CheckEmbeddingRequest) (*service.EmbeddingCheckResponse, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeDataError, errors.New(`lack of "Dataset ID"`)
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestUpdateDataset_ExplicitEmptyVersionRejected(t *testing.T) {
|
||||
|
||||
parseType := 2
|
||||
empty := ""
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-empty-upd", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-empty-upd", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
ParseType: &parseType,
|
||||
PipelineVersionID: &empty,
|
||||
@@ -145,7 +145,7 @@ func TestUpdateDataset_PipelineChangeWithoutVersionRejected(t *testing.T) {
|
||||
insertDatasetUpdateKB(t, "kb-switch", "tenant-1", "Original") // builtin, no pipeline bound
|
||||
|
||||
parseType := 2
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-switch", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-switch", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
ParseType: &parseType,
|
||||
})
|
||||
@@ -170,7 +170,7 @@ func TestUpdateDataset_PreservePinnedVersionOnPartialUpdate(t *testing.T) {
|
||||
seedBoundKB(t, db, "kb-preserve", canvas1, ver1)
|
||||
|
||||
name := "renamed-partial"
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-preserve", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-preserve", service.UpdateDatasetRequest{
|
||||
Name: &name,
|
||||
})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestUpdateDataset_ParserConfigRebuildUsesPinnedVersionDSL(t *testing.T) {
|
||||
seedBoundKBWithVersion(t, db, "kb-1", canvasID, "v-old")
|
||||
|
||||
// Request omits pipeline_id AND pipeline_version_id; only parser_config changes.
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1",
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1",
|
||||
service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{"Parser:Shared": map[string]interface{}{"k": "override"}},
|
||||
})
|
||||
@@ -144,7 +144,7 @@ func TestUpdateDataset_SingleFieldVersionPatchDoesNotClearPin(t *testing.T) {
|
||||
seedBoundKBWithVersion(t, db, "kb-1", canvasID, "v-old")
|
||||
|
||||
// Single-field PATCH: only pipeline_version_id, pipeline_id omitted.
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1",
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1",
|
||||
service.UpdateDatasetRequest{
|
||||
PipelineVersionID: sptr("v-new"),
|
||||
})
|
||||
|
||||
@@ -44,7 +44,7 @@ type IngestionMessagesResponse struct {
|
||||
Terminal bool `json:"terminal"`
|
||||
}
|
||||
|
||||
func (d *DatasetService) GetIngestionSummary(ctx context.Context, datasetID, userID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) GetIngestionSummary(ctx context.Context, datasetID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeDataError, errors.New(`lack of "Dataset ID"`)
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func (d *DatasetService) getDownloadStatus(ctx context.Context, datasetID string
|
||||
|
||||
// ListIngestionMessages returns events owned by the requested immutable
|
||||
// pipeline log.
|
||||
func (d *DatasetService) ListIngestionMessages(ctx context.Context, datasetID, userID, logID string, limit int, afterID, beforeID *int) (*IngestionMessagesResponse, common.ErrorCode, error) {
|
||||
func (d *DatasetService) ListIngestionMessages(ctx context.Context, datasetID, logID string, limit int, afterID, beforeID *int) (*IngestionMessagesResponse, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeArgumentError, errors.New(`lack of "Dataset ID"`)
|
||||
}
|
||||
@@ -229,7 +229,7 @@ func isReadableIngestionLog(log *entity.PipelineOperationLog) bool {
|
||||
}
|
||||
|
||||
// ListSyncLogs returns paginated sync logs for connectors currently linked to an accessible dataset.
|
||||
func (d *DatasetService) ListSyncLogs(ctx context.Context, datasetID, userID string, page, pageSize int) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) ListSyncLogs(ctx context.Context, datasetID string, page, pageSize int) (map[string]interface{}, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeDataError, errors.New(`lack of "Dataset ID"`)
|
||||
}
|
||||
@@ -287,7 +287,7 @@ func (d *DatasetService) ListSyncLogs(ctx context.Context, datasetID, userID str
|
||||
}, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (d *DatasetService) ListIngestionLogs(ctx context.Context, datasetID, userID string, page, pageSize int, terms []dao.OrderTerm, operationStatus []string, createDateFrom, createDateTo, logType, keywords, documentID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) ListIngestionLogs(ctx context.Context, datasetID string, page, pageSize int, terms []dao.OrderTerm, operationStatus []string, createDateFrom, createDateTo, logType, keywords, documentID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeDataError, errors.New(`lack of "Dataset ID"`)
|
||||
}
|
||||
@@ -349,7 +349,7 @@ func (d *DatasetService) ListIngestionLogs(ctx context.Context, datasetID, userI
|
||||
}, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (d *DatasetService) GetIngestionLog(ctx context.Context, datasetID, userID, logID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) GetIngestionLog(ctx context.Context, datasetID, logID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeDataError, errors.New(`lack of "Dataset ID"`)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestListIngestionMessagesUsesRunScopedKeysets(t *testing.T) {
|
||||
insertMessageEvent(t, db, "run-1", "task-1", dao.EventTypeMessage, "detail")
|
||||
}
|
||||
|
||||
response, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "run-1", 2, nil, nil)
|
||||
response, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "run-1", 2, nil, nil)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionMessages = (%+v, %v, %v), want success", response, code, err)
|
||||
}
|
||||
@@ -34,7 +34,7 @@ func TestListIngestionMessagesUsesRunScopedKeysets(t *testing.T) {
|
||||
assertMessageEventIDs(t, response.Items, 4, 5)
|
||||
|
||||
afterID := 2
|
||||
response, code, err = NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "run-1", 2, &afterID, nil)
|
||||
response, code, err = NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "run-1", 2, &afterID, nil)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("after page = (%+v, %v, %v), want success", response, code, err)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func TestListIngestionMessagesUsesRunScopedKeysets(t *testing.T) {
|
||||
}
|
||||
|
||||
beforeID := 4
|
||||
if _, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "run-1", 2, &afterID, &beforeID); err == nil || code != common.CodeArgumentError {
|
||||
if _, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "run-1", 2, &afterID, &beforeID); err == nil || code != common.CodeArgumentError {
|
||||
t.Fatalf("mutually exclusive cursors = code:%v err:%v, want argument error", code, err)
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func TestListIngestionMessagesRejectsUnnumberedRun(t *testing.T) {
|
||||
insertMessageRun(t, "legacy-run", "kb-1", "doc-1", entity.TaskStatusRunning, 0)
|
||||
insertMessageEvent(t, db, "legacy-run", "task-1", dao.EventTypeMessage, "old detail")
|
||||
|
||||
response, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "legacy-run", 200, nil, nil)
|
||||
response, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "legacy-run", 200, nil, nil)
|
||||
if response != nil || err == nil || code != common.CodeDataError {
|
||||
t.Fatalf("ListIngestionMessages = (%+v, %v, %v), want not-found data error", response, code, err)
|
||||
}
|
||||
@@ -89,7 +89,7 @@ func TestDatasetIngestionLogUsesEventStream(t *testing.T) {
|
||||
insertMessageRun(t, "numbered-sentinel-run", "kb-1", entity.DatasetLogDocumentID, entity.TaskStatusDone, 1)
|
||||
insertMessageEvent(t, db, "dataset-run", "dataset-run", dao.EventTypeTerminal, "Knowledge compilation completed")
|
||||
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", 1, 30, nil, nil, "", "", "dataset", "", "")
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", 1, 30, nil, nil, "", "", "dataset", "", "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionLogs = (%+v, %v, %v), want success", result, code, err)
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func TestDatasetIngestionLogUsesEventStream(t *testing.T) {
|
||||
}
|
||||
assertLatestEventMap(t, logs[0], 1, "Knowledge compilation completed")
|
||||
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "dataset-run", 200, nil, nil)
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "dataset-run", 200, nil, nil)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionMessages = (%+v, %v, %v), want success", messages, code, err)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func TestDatasetIngestionLogUsesEventStream(t *testing.T) {
|
||||
t.Fatalf("dataset messages = %+v, want terminal event stream", messages)
|
||||
}
|
||||
|
||||
log, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "dataset-run")
|
||||
log, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "dataset-run")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("GetIngestionLog = (%+v, %v, %v), want success", log, code, err)
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func TestListSyncLogsReturnsOnlyCurrentlyLinkedDatasetConnectors(t *testing.T) {
|
||||
permission.OperationUse, permission.OperationRead); err != nil {
|
||||
t.Fatalf("check linked sync log access fixture: %v", err)
|
||||
}
|
||||
result, code, err := NewDatasetService().ListSyncLogs(ctx, "kb-1", "user-1", 1, 30)
|
||||
result, code, err := NewDatasetService().ListSyncLogs(ctx, "kb-1", 1, 30)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListSyncLogs = (%+v, %v, %v), want success", result, code, err)
|
||||
}
|
||||
@@ -192,7 +192,7 @@ func TestListIngestionLogsIncludesPythonFileLogsAndStatusFilters(t *testing.T) {
|
||||
t.Fatalf("insert Python run: %v", err)
|
||||
}
|
||||
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", 1, 30, nil, []string{"DONE"}, "", "", "file", "", "")
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", 1, 30, nil, []string{"DONE"}, "", "", "file", "", "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionLogs = (%+v, %v, %v), want success", result, code, err)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func TestListIngestionLogsIncludesPythonFileLogsAndStatusFilters(t *testing.T) {
|
||||
t.Fatalf("Python file log = %#v, want file-log fields", byID["python-run"])
|
||||
}
|
||||
|
||||
detail, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "python-run")
|
||||
detail, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "python-run")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("GetIngestionLog = (%+v, %v, %v), want success", detail, code, err)
|
||||
}
|
||||
@@ -225,7 +225,7 @@ func TestListIngestionLogsIncludesPythonFileLogsAndStatusFilters(t *testing.T) {
|
||||
t.Fatalf("Python detail = %#v, want file-log fields", detail)
|
||||
}
|
||||
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "python-run", 200, nil, nil)
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "python-run", 200, nil, nil)
|
||||
if err != nil || code != common.CodeSuccess || !messages.Terminal || len(messages.Items) != 0 {
|
||||
t.Fatalf("Python ListIngestionMessages = (%+v, %v, %v), want empty terminal success", messages, code, err)
|
||||
}
|
||||
@@ -247,7 +247,7 @@ func TestPythonDatasetLogIsReadableAndKeepsProgressMessage(t *testing.T) {
|
||||
t.Fatalf("insert Python dataset run: %v", err)
|
||||
}
|
||||
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", 1, 30, nil, []string{"DONE"}, "", "", "dataset", "", "")
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", 1, 30, nil, []string{"DONE"}, "", "", "dataset", "", "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionLogs = (%+v, %v, %v), want success", result, code, err)
|
||||
}
|
||||
@@ -259,7 +259,7 @@ func TestPythonDatasetLogIsReadableAndKeepsProgressMessage(t *testing.T) {
|
||||
t.Fatalf("Python dataset log fields = %#v, want stored status and progress_msg", logs[0])
|
||||
}
|
||||
|
||||
log, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "python-dataset-run")
|
||||
log, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "python-dataset-run")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("GetIngestionLog = (%+v, %v, %v), want success", log, code, err)
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func TestPythonDatasetLogIsReadableAndKeepsProgressMessage(t *testing.T) {
|
||||
t.Fatalf("detail progress_msg = %#v, want %q", log["progress_msg"], progressMessage)
|
||||
}
|
||||
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "python-dataset-run", 200, nil, nil)
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "python-dataset-run", 200, nil, nil)
|
||||
if err != nil || code != common.CodeSuccess || !messages.Terminal || len(messages.Items) != 0 {
|
||||
t.Fatalf("legacy ListIngestionMessages = (%+v, %v, %v), want empty terminal success", messages, code, err)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ func TestListIngestionLogsEmbedsLatestRunEventWithoutProgressMessage(t *testing.
|
||||
insertMessageEvent(t, db, "run-1", "task-1", dao.EventTypeTerminal, "latest first")
|
||||
insertMessageEvent(t, db, "run-2", "task-2", dao.EventTypeMessage, "latest second")
|
||||
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", 1, 30, nil, nil, "", "", "file", "", "")
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", 1, 30, nil, nil, "", "", "file", "", "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionLogs = (%+v, %v, %v), want success", result, code, err)
|
||||
}
|
||||
@@ -320,7 +320,7 @@ func TestGetIngestionLogEmbedsLatestRunEvent(t *testing.T) {
|
||||
insertMessageRun(t, "run-1", "kb-1", "doc-1", entity.TaskStatusDone, 1)
|
||||
insertMessageEvent(t, db, "run-1", "task-1", dao.EventTypeMessage, "latest detail")
|
||||
|
||||
result, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "run-1")
|
||||
result, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "run-1")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("GetIngestionLog = (%+v, %v, %v), want success", result, code, err)
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func TestIngestionEventWriterFeedsRunScopedLogReaders(t *testing.T) {
|
||||
t.Fatalf("record progress event: %v", err)
|
||||
}
|
||||
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "run-1", 200, nil, nil)
|
||||
messages, code, err := NewDatasetService().ListIngestionMessages(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "run-1", 200, nil, nil)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionMessages = (%+v, %v, %v), want success", messages, code, err)
|
||||
}
|
||||
@@ -352,7 +352,7 @@ func TestIngestionEventWriterFeedsRunScopedLogReaders(t *testing.T) {
|
||||
t.Fatalf("messages = %+v, want lifecycle then latest progress", messages.Items)
|
||||
}
|
||||
|
||||
log, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", "run-1")
|
||||
log, code, err := NewDatasetService().GetIngestionLog(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "run-1")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("GetIngestionLog = (%+v, %v, %v), want success", log, code, err)
|
||||
}
|
||||
@@ -446,7 +446,7 @@ func TestListIngestionLogsRejectsUnknownLogType(t *testing.T) {
|
||||
{name: "empty", logType: "", wantCode: common.CodeDataError},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", "user-1", 1, 30, nil, nil, "", "", tc.logType, "", "")
|
||||
result, code, err := NewDatasetService().ListIngestionLogs(withDatasetTestContext(t.Context(), "user-1", "user-1"), "kb-1", 1, 30, nil, nil, "", "", tc.logType, "", "")
|
||||
if tc.wantCode == common.CodeSuccess {
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListIngestionLogs(%q) = (%+v, %v, %v), want success", tc.logType, result, code, err)
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestDatasetServiceListDatasetsFiltersByIDs(t *testing.T) {
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
data, total, code, err := testDatasetListService(t).ListDatasets(ctx,
|
||||
"", "", 1, 30, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", nil, "", "tenant-1", []string{"kb-1"}, "",
|
||||
"", nil, "", []string{"kb-1"}, "",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ListDatasets failed: %v", err)
|
||||
@@ -69,7 +69,7 @@ func TestDatasetServiceListDatasetsIDsAccessibleViaTeamTenant(t *testing.T) {
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "owner-1")
|
||||
data, total, code, err := testDatasetListService(t).ListDatasets(ctx,
|
||||
"", "", 1, 30, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", nil, "", "user-1", []string{"kb-team"}, "",
|
||||
"", nil, "", []string{"kb-team"}, "",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ListDatasets failed: %v", err)
|
||||
@@ -98,7 +98,7 @@ func TestDatasetServiceListDatasetsAndFiltersUseAuthorizedScope(t *testing.T) {
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "owner-1")
|
||||
data, total, code, err := testDatasetListService(t).ListDatasets(ctx,
|
||||
"", "", 1, 30, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", []string{"owner-1"}, "", "user-1", nil, "",
|
||||
"", []string{"owner-1"}, "", nil, "",
|
||||
)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasets = (%#v, %d, %v), want success", data, code, err)
|
||||
@@ -107,7 +107,7 @@ func TestDatasetServiceListDatasetsAndFiltersUseAuthorizedScope(t *testing.T) {
|
||||
t.Fatalf("ListDatasets returned %#v (total=%d), want only the shared dataset", data, total)
|
||||
}
|
||||
|
||||
filters, code, err := testDatasetListService(t).ListDatasetFilters(ctx, "user-1", "")
|
||||
filters, code, err := testDatasetListService(t).ListDatasetFilters(ctx, "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasetFilters = (%#v, %v, %v), want success", filters, code, err)
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func TestDatasetServiceListDatasetsForCanvasUsesCallerOwnerIntersection(t *testi
|
||||
|
||||
service := testDatasetListService(t)
|
||||
data, total, code, err := service.ListDatasets(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "", "", 1, 30,
|
||||
[]dao.OrderTerm{{Column: "create_time", Desc: true}}, "", nil, "", "user-1", nil, "canvas-shared")
|
||||
[]dao.OrderTerm{{Column: "create_time", Desc: true}}, "", nil, "", nil, "canvas-shared")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasets(canvas) = (%#v, %d, %v), want success", data, code, err)
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func TestDatasetServiceListDatasetsForCanvasUsesCallerOwnerIntersection(t *testi
|
||||
}
|
||||
|
||||
data, total, code, err = service.ListDatasets(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "", "", 1, 30,
|
||||
[]dao.OrderTerm{{Column: "create_time", Desc: true}}, "", nil, "", "user-1",
|
||||
[]dao.OrderTerm{{Column: "create_time", Desc: true}}, "", nil, "",
|
||||
[]string{"kb-caller-only", "kb-shared", "kb-owner-only"}, "canvas-shared")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasets(canvas, ids) = (%#v, %d, %v), want success", data, code, err)
|
||||
@@ -188,7 +188,7 @@ func TestDatasetServiceListDatasetsPaginatesInsidePermissionScope(t *testing.T)
|
||||
|
||||
service := testDatasetListService(t)
|
||||
terms := []dao.OrderTerm{{Column: "name"}}
|
||||
pageOne, total, code, err := service.ListDatasets(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "", "", 1, 1, terms, "", nil, "", "user-1", nil, "")
|
||||
pageOne, total, code, err := service.ListDatasets(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "", "", 1, 1, terms, "", nil, "", nil, "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasets(page 1) = (%#v, %d, %v), want success", pageOne, code, err)
|
||||
}
|
||||
@@ -196,7 +196,7 @@ func TestDatasetServiceListDatasetsPaginatesInsidePermissionScope(t *testing.T)
|
||||
t.Fatalf("page 1 = %#v (total=%d), want kb-own and two accessible datasets", pageOne, total)
|
||||
}
|
||||
|
||||
pageTwo, secondTotal, code, err := service.ListDatasets(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "", "", 2, 1, terms, "", nil, "", "user-1", nil, "")
|
||||
pageTwo, secondTotal, code, err := service.ListDatasets(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "", "", 2, 1, terms, "", nil, "", nil, "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasets(page 2) = (%#v, %d, %v), want success", pageTwo, code, err)
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func TestDatasetServiceListDatasetFiltersReturnsEmptyOwnerList(t *testing.T) {
|
||||
pushServiceDB(t, db)
|
||||
ensureDatasetTestMembership(t, "user-without-datasets", "user-without-datasets", "owner")
|
||||
|
||||
filters, code, err := testDatasetListService(t).ListDatasetFilters(withDatasetTestContext(t.Context(), "user-without-datasets", "user-without-datasets"), "user-without-datasets", "")
|
||||
filters, code, err := testDatasetListService(t).ListDatasetFilters(withDatasetTestContext(t.Context(), "user-without-datasets", "user-without-datasets"), "")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("ListDatasetFilters = (%#v, %v, %v), want success", filters, code, err)
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func TestDatasetServiceListDatasetsRejectsIDAndIDsTogether(t *testing.T) {
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "tenant-1")
|
||||
_, _, code, err := testDatasetListService(t).ListDatasets(ctx,
|
||||
"kb-1", "", 1, 30, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", nil, "", "tenant-1", []string{"kb-1"}, "",
|
||||
"", nil, "", []string{"kb-1"}, "",
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatal("expected id/ids conflict error")
|
||||
@@ -268,7 +268,7 @@ func TestDatasetServiceListDatasetsFiltersInaccessibleIDs(t *testing.T) {
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "user-1")
|
||||
data, total, code, err := testDatasetListService(t).ListDatasets(ctx,
|
||||
"", "", 1, 30, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", nil, "", "user-1", []string{"kb-valid", "kb-private", "kb-missing"}, "",
|
||||
"", nil, "", []string{"kb-valid", "kb-private", "kb-missing"}, "",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ListDatasets failed: %v", err)
|
||||
@@ -289,7 +289,7 @@ func TestDatasetServiceListDatasetsReturnsEmptyForAllInaccessibleIDs(t *testing.
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "user-1")
|
||||
data, total, code, err := testDatasetListService(t).ListDatasets(ctx,
|
||||
"", "", 1, 30, []dao.OrderTerm{{Column: "create_time", Desc: true}},
|
||||
"", nil, "", "user-1", []string{"kb-private", "kb-missing"}, "",
|
||||
"", nil, "", []string{"kb-private", "kb-missing"}, "",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("ListDatasets failed: %v", err)
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
)
|
||||
|
||||
// UpdateDocumentMetadataConfig updates the metadata config for a document in a dataset.
|
||||
func (d *DatasetService) UpdateDocumentMetadataConfig(ctx context.Context, userID, datasetID, documentID string, req map[string]interface{}) (*entity.Document, common.ErrorCode, error) {
|
||||
userID = strings.TrimSpace(userID)
|
||||
func (d *DatasetService) UpdateDocumentMetadataConfig(ctx context.Context, datasetID, documentID string, req map[string]interface{}) (*entity.Document, common.ErrorCode, error) {
|
||||
userID := strings.TrimSpace(permission.UserIDFromContext(ctx))
|
||||
datasetID = strings.TrimSpace(datasetID)
|
||||
if err := permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindDataset, ID: datasetID}, permission.OperationUpdate); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
@@ -69,9 +69,8 @@ func (d *DatasetService) UpdateDocumentMetadataConfig(ctx context.Context, userI
|
||||
}
|
||||
|
||||
// GetMetadataConfig gets the auto-metadata configuration for a dataset.
|
||||
func (d *DatasetService) GetMetadataConfig(ctx context.Context, datasetID, userID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) GetMetadataConfig(ctx context.Context, datasetID string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
datasetID = strings.TrimSpace(datasetID)
|
||||
userID = strings.TrimSpace(userID)
|
||||
if err := permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindDataset, ID: datasetID}, permission.OperationRead); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
return nil, code, permissionErr
|
||||
@@ -100,9 +99,9 @@ func (d *DatasetService) GetMetadataConfig(ctx context.Context, datasetID, userI
|
||||
}
|
||||
|
||||
// UpdateMetadataConfig updates the auto-metadata configuration for a dataset.
|
||||
func (d *DatasetService) UpdateMetadataConfig(ctx context.Context, datasetID, userID string, req *service.MetadataConfigRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) UpdateMetadataConfig(ctx context.Context, datasetID string, req *service.MetadataConfigRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
datasetID = strings.TrimSpace(datasetID)
|
||||
userID = strings.TrimSpace(userID)
|
||||
userID := strings.TrimSpace(permission.UserIDFromContext(ctx))
|
||||
|
||||
if err := permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindDataset, ID: datasetID}, permission.OperationUpdate); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
|
||||
@@ -110,7 +110,7 @@ func TestDatasetServiceUpdateDocumentMetadataConfig(t *testing.T) {
|
||||
metadata := map[string]interface{}{"author": "Alice", "year": float64(2026)}
|
||||
doc, code, err := testDatasetServiceForDocumentMetadataConfig(t).UpdateDocumentMetadataConfig(
|
||||
ctx,
|
||||
"user-1",
|
||||
|
||||
"kb-1",
|
||||
"doc-1",
|
||||
map[string]interface{}{"metadata": metadata},
|
||||
@@ -162,7 +162,7 @@ func TestDatasetServiceUpdateDocumentMetadataConfigRequiresMetadata(t *testing.T
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "user-1")
|
||||
_, code, err := testDatasetServiceForDocumentMetadataConfig(t).UpdateDocumentMetadataConfig(
|
||||
ctx,
|
||||
"user-1",
|
||||
|
||||
"kb-1",
|
||||
"doc-1",
|
||||
map[string]interface{}{},
|
||||
@@ -188,7 +188,7 @@ func TestDatasetServiceUpdateDocumentMetadataConfigRejectsNonOwner(t *testing.T)
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "owner-1")
|
||||
_, code, err := testDatasetServiceForDocumentMetadataConfig(t).UpdateDocumentMetadataConfig(
|
||||
ctx,
|
||||
"user-1",
|
||||
|
||||
"kb-1",
|
||||
"doc-1",
|
||||
map[string]interface{}{"metadata": map[string]interface{}{"author": "Alice"}},
|
||||
@@ -221,7 +221,7 @@ func TestDatasetServiceUpdateDocumentMetadataConfigAllowsTeamMember(t *testing.T
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "owner-1")
|
||||
doc, code, err := testDatasetServiceForDocumentMetadataConfig(t).UpdateDocumentMetadataConfig(
|
||||
ctx,
|
||||
"user-1",
|
||||
|
||||
"kb-1",
|
||||
"doc-1",
|
||||
map[string]interface{}{"metadata": map[string]interface{}{"author": "Alice"}},
|
||||
@@ -267,7 +267,7 @@ func TestDatasetServiceUpdateMetadataConfigSyncsExtractorSchema(t *testing.T) {
|
||||
result, code, err := (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", "tenant-1", &service.MetadataConfigRequest{
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Metadata: []service.MetadataConfigField{
|
||||
{Key: "author", Type: "string"},
|
||||
},
|
||||
@@ -321,7 +321,7 @@ func TestDatasetServiceUpdateMetadataConfigSyncsExtractorSchema(t *testing.T) {
|
||||
_, code, err = (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", "tenant-1", &service.MetadataConfigRequest{
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Metadata: []service.MetadataConfigField{
|
||||
{Key: "author", Type: "string"},
|
||||
},
|
||||
@@ -370,7 +370,7 @@ func TestDatasetServiceUpdateMetadataConfigSyncsExtractorSchema(t *testing.T) {
|
||||
_, code, err = (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", "tenant-1", &service.MetadataConfigRequest{
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Metadata: []service.MetadataConfigField{},
|
||||
BuiltInMetadata: []service.MetadataConfigField{},
|
||||
})
|
||||
@@ -413,7 +413,7 @@ func TestDatasetServiceGetMetadataConfig(t *testing.T) {
|
||||
_, code, err := (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", "tenant-1", &service.MetadataConfigRequest{
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Metadata: []service.MetadataConfigField{
|
||||
{Key: "author", Type: "string"},
|
||||
},
|
||||
@@ -431,7 +431,7 @@ func TestDatasetServiceGetMetadataConfig(t *testing.T) {
|
||||
result, code, err := (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).GetMetadataConfig(ctx, "kb-1", "tenant-1")
|
||||
}).GetMetadataConfig(ctx, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetMetadataConfig failed: %v", err)
|
||||
}
|
||||
@@ -466,7 +466,7 @@ func TestUpdateMetadataConfig_TenantLookupFailureDoesNotPersist(t *testing.T) {
|
||||
result, code, err := (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", "missing-tenant", &service.MetadataConfigRequest{
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Metadata: []service.MetadataConfigField{
|
||||
{Key: "author", Type: "string"},
|
||||
},
|
||||
@@ -496,7 +496,7 @@ func TestUpdateMetadataConfig_ExplicitEnabledPreservedWhenEmpty(t *testing.T) {
|
||||
result, code, err := (&DatasetService{
|
||||
kbDAO: dao.NewKnowledgebaseDAO(),
|
||||
tenantDAO: dao.NewTenantDAO(),
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", "tenant-1", &service.MetadataConfigRequest{
|
||||
}).UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Enabled: &enabled,
|
||||
Metadata: []service.MetadataConfigField{},
|
||||
BuiltInMetadata: []service.MetadataConfigField{},
|
||||
@@ -535,7 +535,7 @@ func TestUpdateDocumentMetadataConfig_KBAndTenantLookupFailure(t *testing.T) {
|
||||
|
||||
// Missing KB -> database layer should surface as CodeServerError / not found
|
||||
_, code, err := testDatasetServiceForDocumentMetadataConfig(t).UpdateDocumentMetadataConfig(
|
||||
ctx, "owner-1", "kb-missing", "doc-ok",
|
||||
ctx, "kb-missing", "doc-ok",
|
||||
map[string]interface{}{"metadata": map[string]interface{}{"a": "b"}},
|
||||
)
|
||||
if code == common.CodeSuccess || err == nil {
|
||||
@@ -547,7 +547,7 @@ func TestUpdateDocumentMetadataConfig_KBAndTenantLookupFailure(t *testing.T) {
|
||||
insertDatasetMetadataConfigDoc(t, "doc2", "kb-bad-tenant", entity.JSONMap{})
|
||||
missingTenantCtx := withDatasetTestContext(t.Context(), "missing-tenant", "missing-tenant")
|
||||
_, code, err = testDatasetServiceForDocumentMetadataConfig(t).UpdateDocumentMetadataConfig(
|
||||
missingTenantCtx, "missing-tenant", "kb-bad-tenant", "doc2",
|
||||
missingTenantCtx, "kb-bad-tenant", "doc2",
|
||||
map[string]interface{}{"metadata": map[string]interface{}{"a": "b"}},
|
||||
)
|
||||
if code != common.CodeServerError || err == nil {
|
||||
|
||||
@@ -156,7 +156,7 @@ func TestUpdateDataset_RewritesVersionOnPipelineChange(t *testing.T) {
|
||||
// C5/G3: switching the bound pipeline now requires an explicit version (the
|
||||
// empty version is no longer auto-backfilled to the new canvas latest). Supply
|
||||
// canvas2's version to verify the rewrite still pins the chosen version.
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-change", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-change", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas2,
|
||||
ParseType: &parseType,
|
||||
PipelineVersionID: &ver2,
|
||||
@@ -185,7 +185,7 @@ func TestUpdateDataset_PreservesVersionWhenPipelineUnchanged(t *testing.T) {
|
||||
|
||||
parseType := 2
|
||||
name := "kb-preserve-renamed"
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-preserve", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-preserve", service.UpdateDatasetRequest{
|
||||
Name: &name,
|
||||
PipelineID: &canvas1,
|
||||
ParseType: &parseType,
|
||||
@@ -351,7 +351,7 @@ func TestUpdateDataset_RebindsExplicitVersion(t *testing.T) {
|
||||
seedBoundKB(t, db, "kb-repin", canvas1, verOld)
|
||||
|
||||
parseType := 2
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-repin", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-repin", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
PipelineVersionID: &verNew,
|
||||
ParseType: &parseType,
|
||||
@@ -411,7 +411,7 @@ func TestUpdateDataset_EmptyVersionClearsPin(t *testing.T) {
|
||||
|
||||
parseType := 2
|
||||
empty := ""
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-clear", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-clear", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
ParseType: &parseType,
|
||||
PipelineVersionID: &empty,
|
||||
@@ -456,7 +456,7 @@ func TestUpdateDataset_VersionChangeResetsParserConfigToNewVersionDefault(t *tes
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
parseType := 2
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-c3", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-c3", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
PipelineVersionID: &verNew,
|
||||
ParseType: &parseType,
|
||||
@@ -528,7 +528,7 @@ func TestUpdateDataset_VersionChangeKeepsSubmittedParserConfig(t *testing.T) {
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
parseType := 2
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-c3-echo", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-c3-echo", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
PipelineVersionID: &verNew,
|
||||
ParseType: &parseType,
|
||||
@@ -585,7 +585,7 @@ func TestUpdateDataset_SameVersionKeepsParserConfig(t *testing.T) {
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
parseType := 2
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-c3-same", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-c3-same", service.UpdateDatasetRequest{
|
||||
PipelineID: &canvas1,
|
||||
PipelineVersionID: &verOld, // same version, no change
|
||||
ParseType: &parseType,
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestSDKDatasetUnicodeCharacterLimit(t *testing.T) {
|
||||
pushServiceDB(t, db)
|
||||
insertDatasetUpdateKB(t, "kb-1", "tenant-1", "Original")
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{Name: new(strings.Repeat("资", n))})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{Name: new(strings.Repeat("资", n))})
|
||||
if (err == nil) != (n == 128) {
|
||||
t.Fatalf("update characters=%d code=%d err=%v", n, code, err)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func TestSDKDatasetNegativePagerankDiagnostic(t *testing.T) {
|
||||
db := setupDatasetUpdateTestDB(t)
|
||||
pushServiceDB(t, db)
|
||||
insertDatasetUpdateKB(t, "kb-1", "tenant-1", "Original")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{Pagerank: new(int64(-1))})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{Pagerank: new(int64(-1))})
|
||||
if code != common.CodeDataError || err == nil || !strings.Contains(err.Error(), "greater than or equal to 0") {
|
||||
t.Fatalf("code=%d err=%v", code, err)
|
||||
}
|
||||
|
||||
@@ -17,14 +17,14 @@ import (
|
||||
"ragflow/internal/service/nlp"
|
||||
)
|
||||
|
||||
func (d *DatasetService) SearchDataset(ctx context.Context, datasetID, userID string, req *service.SearchDatasetRequest) (*service.SearchDatasetsResponse, error) {
|
||||
func (d *DatasetService) SearchDataset(ctx context.Context, datasetID string, req *service.SearchDatasetRequest) (*service.SearchDatasetsResponse, error) {
|
||||
if datasetID == "" {
|
||||
return nil, fmt.Errorf("dataset_id is required")
|
||||
}
|
||||
return d.SearchDatasets(ctx, req.ToSearchDatasetsRequest(datasetID), userID)
|
||||
return d.SearchDatasets(ctx, req.ToSearchDatasetsRequest(datasetID))
|
||||
}
|
||||
|
||||
func (d *DatasetService) SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest, userID string) (*service.SearchDatasetsResponse, error) {
|
||||
func (d *DatasetService) SearchDatasets(ctx context.Context, req *service.SearchDatasetsRequest) (*service.SearchDatasetsResponse, error) {
|
||||
if req.Question == "" {
|
||||
return nil, fmt.Errorf("question is required")
|
||||
}
|
||||
@@ -34,6 +34,7 @@ func (d *DatasetService) SearchDatasets(ctx context.Context, req *service.Search
|
||||
if err := service.ValidateKnowledgeCompilationScope(req.KnowledgeCompilationScope); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userID := permission.UserIDFromContext(ctx)
|
||||
common.Info("SearchDatasets started", zap.String("userID", userID), zap.Any("datasets", req.DatasetIDs), zap.String("question", req.Question))
|
||||
|
||||
page := 1
|
||||
|
||||
@@ -119,7 +119,7 @@ func TestSearchDatasetsEmbeddingConsistencyIntegration(t *testing.T) {
|
||||
}
|
||||
req := f.request(0.7, 0)
|
||||
req.DatasetIDs = []string{"kb-a", "kb-b"}
|
||||
result, err := f.service.SearchDatasets(f.ctx, req, f.owner)
|
||||
result, err := f.service.SearchDatasets(f.ctx, req)
|
||||
if err == nil || !strings.Contains(err.Error(), "datasets use different embedding models") || result != nil {
|
||||
t.Fatalf("different base models must be rejected before retrieval: result=%+v err=%v", result, err)
|
||||
}
|
||||
@@ -280,7 +280,7 @@ func (f *searchIntegrationFixture) request(weight, threshold float64) *service.S
|
||||
|
||||
func (f *searchIntegrationFixture) search(t *testing.T, req *service.SearchDatasetsRequest) *service.SearchDatasetsResponse {
|
||||
t.Helper()
|
||||
result, err := f.service.SearchDatasets(f.ctx, req, f.owner)
|
||||
result, err := f.service.SearchDatasets(f.ctx, req)
|
||||
if err != nil {
|
||||
t.Fatalf("SearchDatasets: %v", err)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
permissionresponse "ragflow/internal/permission/response"
|
||||
)
|
||||
|
||||
func (d *DatasetService) AggregateTags(ctx context.Context, datasetIDs []string, userID string) ([]map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) AggregateTags(ctx context.Context, datasetIDs []string) ([]map[string]interface{}, common.ErrorCode, error) {
|
||||
if len(datasetIDs) == 0 {
|
||||
return nil, common.CodeDataError, errors.New("Lack of dataset_ids in query parameters")
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestDatasetServiceAggregateTagsSeedsFromTagFile(t *testing.T) {
|
||||
}
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "user-1")
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kbInput}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kbInput})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func TestDatasetServiceAggregateTagsMergesAcrossTagFiles(t *testing.T) {
|
||||
}
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "user-1")
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kb1Input, kb2Input}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kb1Input, kb2Input})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func TestDatasetServiceAggregateTagsEmptyWhenNoTagFile(t *testing.T) {
|
||||
}
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "user-1")
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kbInput}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kbInput})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func TestDatasetServiceAggregateTagsRejectsUnauthorizedDataset(t *testing.T) {
|
||||
insertAggregateTagsKB(t, kbID, "tenant-9", string(entity.TenantPermissionMe), "", 0)
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "tenant-9")
|
||||
|
||||
_, code, err := testDatasetServiceForAggregateTags(t, nil).AggregateTags(ctx, []string{kbInput}, "user-1")
|
||||
_, code, err := testDatasetServiceForAggregateTags(t, nil).AggregateTags(ctx, []string{kbInput})
|
||||
if err == nil {
|
||||
t.Fatal("expected authorization error")
|
||||
}
|
||||
@@ -246,7 +246,7 @@ func TestDatasetServiceAggregateTagsScopesLoaderToDatasetTenant(t *testing.T) {
|
||||
return map[string]int{"finance": 1}, nil
|
||||
}
|
||||
|
||||
if _, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kbInput}, "user-1"); err != nil {
|
||||
if _, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(ctx, []string{kbInput}); err != nil {
|
||||
t.Fatalf("AggregateTags failed: code=%d err=%v", code, err)
|
||||
}
|
||||
if gotOwner != kbTenant {
|
||||
@@ -292,7 +292,7 @@ func TestDatasetServiceAggregateTagsReadsDocumentTagFile(t *testing.T) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -324,7 +324,7 @@ func TestDatasetServiceAggregateTagsCountsSnapshotOnce(t *testing.T) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -360,7 +360,7 @@ func TestDatasetServiceAggregateTagsUnionsDocumentTagFiles(t *testing.T) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -396,7 +396,7 @@ func TestDatasetServiceAggregateTagsCountsSharedFileOnce(t *testing.T) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kb1Input, kb2Input}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kb1Input, kb2Input})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -439,7 +439,7 @@ func TestDatasetServiceAggregateTagsSkipsUnresolvableDocumentTagSource(t *testin
|
||||
return map[string]int{"finance": 2}, nil
|
||||
}
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{deadKB, liveKB}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{deadKB, liveKB})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -467,7 +467,7 @@ func TestDatasetServiceAggregateTagsIgnoresUnappliedDatasetTagSource(t *testing.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput}, "user-1")
|
||||
result, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput})
|
||||
if err != nil {
|
||||
t.Fatalf("AggregateTags failed: %v", err)
|
||||
}
|
||||
@@ -495,7 +495,7 @@ func TestDatasetServiceAggregateTagsFailsOnStorageErrorForDocumentTagSource(t *t
|
||||
return nil, errors.New("tag source file: no storage backend registered")
|
||||
}
|
||||
|
||||
_, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput}, "user-1")
|
||||
_, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "user-1"), []string{kbInput})
|
||||
if err == nil {
|
||||
t.Fatal("expected an error for a storage failure on a document-level tag source")
|
||||
}
|
||||
@@ -524,7 +524,7 @@ func TestDatasetServiceAggregateTagsScopesDocumentTagSourceToDatasetTenant(t *te
|
||||
return map[string]int{"finance": 1}, nil
|
||||
}
|
||||
|
||||
if _, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "tenant-owner"), []string{kbInput}, "user-1"); err != nil {
|
||||
if _, code, err := testDatasetServiceForAggregateTags(t, loader).AggregateTags(withDatasetTestContext(t.Context(), "user-1", "tenant-owner"), []string{kbInput}); err != nil {
|
||||
t.Fatalf("AggregateTags failed: code=%d err=%v", code, err)
|
||||
}
|
||||
if gotOwner != kbTenant {
|
||||
|
||||
@@ -31,9 +31,9 @@ type datasetPagerankUpdate struct {
|
||||
datasetID string
|
||||
}
|
||||
|
||||
func (d *DatasetService) UpdateDataset(ctx context.Context, datasetID, userID string, req service.UpdateDatasetRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (d *DatasetService) UpdateDataset(ctx context.Context, datasetID string, req service.UpdateDatasetRequest) (map[string]interface{}, common.ErrorCode, error) {
|
||||
datasetID = strings.TrimSpace(datasetID)
|
||||
userID = strings.TrimSpace(userID)
|
||||
userID := strings.TrimSpace(permission.UserIDFromContext(ctx))
|
||||
if _, err := d.kbDAO.GetByID(ctx, dao.DB, datasetID); err != nil {
|
||||
if dao.IsNotFoundErr(err) {
|
||||
code, permissionErr := permissionresponse.NormalizeHidden(permission.ErrResourceNotFound)
|
||||
@@ -184,7 +184,7 @@ func (d *DatasetService) UpdateDataset(ctx context.Context, datasetID, userID st
|
||||
var scheduledTaskIDs []string
|
||||
var pagerankUpdate *datasetPagerankUpdate
|
||||
err = dao.DB.Transaction(func(tx *gorm.DB) error {
|
||||
lockedKB, code, authErr := d.lockAccessibleDatasetForUpdate(ctx, tx, datasetID, userID)
|
||||
lockedKB, code, authErr := d.lockAccessibleDatasetForUpdate(ctx, tx, datasetID)
|
||||
if authErr != nil {
|
||||
txCode = code
|
||||
return authErr
|
||||
@@ -547,7 +547,7 @@ func publishDatasetSyncerTasks(taskIDs []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DatasetService) lockAccessibleDatasetForUpdate(ctx context.Context, tx *gorm.DB, datasetID, userID string) (*entity.Knowledgebase, common.ErrorCode, error) {
|
||||
func (d *DatasetService) lockAccessibleDatasetForUpdate(ctx context.Context, tx *gorm.DB, datasetID string) (*entity.Knowledgebase, common.ErrorCode, error) {
|
||||
var kb entity.Knowledgebase
|
||||
err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("id = ? AND status = ?", datasetID, string(entity.StatusValid)).
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestDatasetServiceUpdateDatasetUpdatesFields(t *testing.T) {
|
||||
parseType := 1
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Name: &name,
|
||||
Description: &description,
|
||||
Language: &language,
|
||||
@@ -105,12 +105,12 @@ func TestUpdateDataset_ClearsAvatarWithEmptyString(t *testing.T) {
|
||||
|
||||
svc := testDatasetUpdateService(t)
|
||||
initial := "data:image/png;base64,iVBORw0KGgo="
|
||||
if _, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{Avatar: &initial}); err != nil || code != common.CodeSuccess {
|
||||
if _, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{Avatar: &initial}); err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("set avatar failed: code=%d err=%v", code, err)
|
||||
}
|
||||
|
||||
empty := ""
|
||||
result, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{Avatar: &empty})
|
||||
result, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{Avatar: &empty})
|
||||
if err != nil {
|
||||
t.Fatalf("clear avatar failed: %v", err)
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func TestUpdateDataset_ParentChildConfigReachesGeneralChunker(t *testing.T) {
|
||||
pushServiceDB(t, db)
|
||||
insertDatasetUpdateKB(t, "kb-1", "tenant-1", "Original")
|
||||
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"GeneralChunker:SixApplesFall": map[string]interface{}{
|
||||
"parent_child": map[string]interface{}{
|
||||
@@ -188,7 +188,7 @@ func TestUpdateDatasetPreservesParentChildChunkerRuntimeConfig(t *testing.T) {
|
||||
t.Fatalf("seed parent-child parser config: %v", err)
|
||||
}
|
||||
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
// Component-scoped override (flat parser-level keys are rejected now).
|
||||
ParserConfig: map[string]interface{}{
|
||||
"GeneralChunker:SixApplesFall": map[string]interface{}{"chunk_token_size": float64(256)},
|
||||
@@ -220,7 +220,7 @@ func TestUpdateDataset_RejectsSimultaneousParserIDAndPipelineID(t *testing.T) {
|
||||
pipelineID := "abcdef0123456789abcdef0123456789"
|
||||
|
||||
ctx := t.Context()
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserID: &chunkMethod,
|
||||
PipelineID: &pipelineID,
|
||||
})
|
||||
@@ -246,7 +246,7 @@ func TestUpdateDataset_ParseTypeBuiltinClearsPipelineID(t *testing.T) {
|
||||
parseType := 1
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserID: &chunkMethod,
|
||||
ParseType: &parseType,
|
||||
})
|
||||
@@ -278,7 +278,7 @@ func TestUpdateDataset_ParseTypePipelineRejectsParserID(t *testing.T) {
|
||||
pipelineID := "ABCDEF0123456789ABCDEF0123456789"
|
||||
parseType := 2
|
||||
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
ParserID: &chunkMethod,
|
||||
PipelineID: &pipelineID,
|
||||
ParseType: &parseType,
|
||||
@@ -323,7 +323,7 @@ func TestUpdateDataset_PipelineSwitchKeepsSubmittedParserConfig(t *testing.T) {
|
||||
seedCanvasVersion(t, db, pipelineID, verID, 0)
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
PipelineID: &pipelineID,
|
||||
ParseType: &parseType,
|
||||
PipelineVersionID: &verID,
|
||||
@@ -385,7 +385,7 @@ func TestUpdateDataset_ParseTypePipelineCleansConfigAgainstCanvas(t *testing.T)
|
||||
}
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
PipelineID: &pipelineID,
|
||||
ParseType: &parseType,
|
||||
ParserConfig: override,
|
||||
@@ -428,7 +428,7 @@ func TestUpdateDatasetRejectsInvalidPages(t *testing.T) {
|
||||
chunkMethod := "manual"
|
||||
parseType := 1
|
||||
ctx := t.Context()
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserID: &chunkMethod,
|
||||
ParseType: &parseType,
|
||||
ParserConfig: map[string]interface{}{
|
||||
@@ -454,7 +454,7 @@ func TestDatasetServiceGetDatasetReturnsEmptyConnectorList(t *testing.T) {
|
||||
insertDatasetUpdateKB(t, datasetID, "tenant-1", "Original")
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).GetDataset(ctx, "11111111-1111-4111-8111-111111111111", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).GetDataset(ctx, "11111111-1111-4111-8111-111111111111")
|
||||
if err != nil {
|
||||
t.Fatalf("GetDataset failed: %v", err)
|
||||
}
|
||||
@@ -479,7 +479,7 @@ func TestDatasetServiceUpdateDatasetRejectsMissingDataset(t *testing.T) {
|
||||
|
||||
name := "Renamed"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "missing-kb", "tenant-1", service.UpdateDatasetRequest{Name: &name})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "missing-kb", service.UpdateDatasetRequest{Name: &name})
|
||||
if err == nil {
|
||||
t.Fatal("expected missing dataset error")
|
||||
}
|
||||
@@ -499,7 +499,7 @@ func TestDatasetServiceUpdateDatasetRejectsNonOwner(t *testing.T) {
|
||||
|
||||
name := "Renamed"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-2", "tenant-2")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-2", service.UpdateDatasetRequest{Name: &name})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{Name: &name})
|
||||
if err == nil {
|
||||
t.Fatal("expected permission error")
|
||||
}
|
||||
@@ -524,7 +524,7 @@ func TestDatasetServiceUpdateDatasetRejectsTeamMemberPermissionChange(t *testing
|
||||
|
||||
permission := string(entity.TenantPermissionMe)
|
||||
ctx := withDatasetTestContext(t.Context(), "user-1", "owner-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "user-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Permission: &permission,
|
||||
})
|
||||
if err == nil {
|
||||
@@ -557,7 +557,7 @@ func TestDatasetServiceUpdateDatasetAllowsTeamMemberToResubmitUnchangedPermissio
|
||||
|
||||
name := "Renamed by team member"
|
||||
permission := string(entity.TenantPermissionTeam)
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "kb-1", "user-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "user-1", "owner-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
Name: &name,
|
||||
Permission: &permission,
|
||||
})
|
||||
@@ -601,7 +601,7 @@ func TestDatasetServiceUpdateDatasetResolvesEmbeddingInDatasetTenant(t *testing.
|
||||
enableDatasetTestModelAccess(t, "tenant-1", "user-1", "instance-1")
|
||||
|
||||
permission := string(entity.TenantPermissionTeam)
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "user-1", "tenant-1"), "kb-1", "user-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "user-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
Permission: &permission,
|
||||
EmbeddingModel: &embeddingModelID,
|
||||
})
|
||||
@@ -628,7 +628,7 @@ func TestDatasetServiceUpdateDatasetValidatesName(t *testing.T) {
|
||||
|
||||
name := " "
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{Name: &name})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{Name: &name})
|
||||
if err == nil {
|
||||
t.Fatal("expected name validation error")
|
||||
}
|
||||
@@ -648,7 +648,7 @@ func TestDatasetServiceUpdateDatasetRejectsDuplicateName(t *testing.T) {
|
||||
|
||||
name := "Existing"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{Name: &name})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{Name: &name})
|
||||
if err == nil {
|
||||
t.Fatal("expected duplicate name error")
|
||||
}
|
||||
@@ -666,7 +666,7 @@ func TestDatasetServiceUpdateDatasetRejectsNoPropertiesModified(t *testing.T) {
|
||||
insertDatasetUpdateKB(t, "kb-1", "tenant-1", "Original")
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{})
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{})
|
||||
if err == nil {
|
||||
t.Fatal("expected no-op update error")
|
||||
}
|
||||
@@ -686,7 +686,7 @@ func TestDatasetServiceUpdateDatasetLinksConnectors(t *testing.T) {
|
||||
|
||||
autoParse := "0"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Connectors: &[]service.DatasetConnectorRequest{{ID: "connector-1", AutoParse: autoParse}},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -723,7 +723,7 @@ func TestDatasetServiceUpdateDatasetRejectsCrossTenantConnector(t *testing.T) {
|
||||
|
||||
autoParse := "0"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Connectors: &[]service.DatasetConnectorRequest{{ID: "connector-1", AutoParse: autoParse}},
|
||||
})
|
||||
if err == nil {
|
||||
@@ -757,7 +757,7 @@ func TestDatasetServiceUpdateDatasetAcceptsProviderInstanceEmbedding(t *testing.
|
||||
|
||||
embeddingModel := "embedding-2@test@ZHIPU-AI"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
EmbeddingModel: &embeddingModel,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -789,7 +789,7 @@ func TestDatasetServiceUpdateDatasetAcceptsEmbeddingModelID(t *testing.T) {
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
embeddingModelID := "aabbccdd11223344aabbccdd11223344"
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
EmbeddingModel: &embeddingModelID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -821,7 +821,7 @@ func TestDatasetServiceUpdateDatasetRejectsEmptyConnectorID(t *testing.T) {
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
connectors := []service.DatasetConnectorRequest{{ID: " "}}
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Connectors: &connectors,
|
||||
})
|
||||
if err == nil {
|
||||
@@ -857,7 +857,7 @@ func TestDatasetServiceUpdateDatasetRejectsInvalidEmbeddingModelFormat(t *testin
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
embdModel := tc.embeddingModel
|
||||
_, code, err := svc.UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := svc.UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
EmbeddingModel: &embdModel,
|
||||
})
|
||||
if err == nil {
|
||||
@@ -881,7 +881,7 @@ func TestDatasetServiceUpdateDatasetRejectsDuplicateNameCaseInsensitive(t *testi
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
uppercaseName := "EXISTING"
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Name: &uppercaseName,
|
||||
})
|
||||
if err == nil {
|
||||
@@ -909,7 +909,7 @@ func TestDatasetServiceUpdateDatasetPreservesUnmodifiedFields(t *testing.T) {
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
newName := "Renamed Only"
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Name: &newName,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -954,7 +954,7 @@ func TestDatasetServiceUpdateDatasetPreservesParserConfigOnEmptyUpdate(t *testin
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
name := "Updated Name"
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
Name: &name,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -1241,7 +1241,7 @@ func TestUpdateDataset_StripsUnknownParam_Builtin(t *testing.T) {
|
||||
insertDatasetUpdateKB(t, "kb-1", "tenant-1", "Original")
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"Parser:HipSignsRhyme": map[string]interface{}{
|
||||
"no_such_param": 1,
|
||||
@@ -1278,7 +1278,7 @@ func TestUpdateDataset_AcceptsValidComponentParams_Builtin(t *testing.T) {
|
||||
insertDatasetUpdateKB(t, "kb-1", "tenant-1", "Original")
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"Parser:HipSignsRhyme": map[string]interface{}{
|
||||
"pdf": map[string]interface{}{"parse_method": "deepdoc"},
|
||||
@@ -1327,7 +1327,7 @@ func TestUpdateDatasetUsesTenantModelIDForExtractorDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"Parser:HipSignsRhyme": map[string]interface{}{
|
||||
"pdf": map[string]interface{}{"parse_method": "deepdoc"},
|
||||
@@ -1399,7 +1399,7 @@ func TestUpdateDataset_PreservesIncomingMetadataWhenCleaningParserConfig(t *test
|
||||
},
|
||||
}
|
||||
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: parserConfig,
|
||||
})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
@@ -1454,7 +1454,7 @@ func TestUpdateDataset_PreservesExistingMetadataWhenParserConfigOmitsIt(t *testi
|
||||
t.Fatalf("seed parser_config: %v", err)
|
||||
}
|
||||
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"Parser:HipSignsRhyme": map[string]interface{}{
|
||||
"pdf": map[string]interface{}{"parse_method": "deepdoc"},
|
||||
@@ -1497,7 +1497,7 @@ func TestUpdateDataset_StripsCanvasUnknownParam(t *testing.T) {
|
||||
insertDatasetUpdateCanvasKB(t, "kb-1", "tenant-1", "Original", "canvas-1")
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"Parser:NoSuch": map[string]interface{}{
|
||||
"pdf": map[string]interface{}{},
|
||||
@@ -1536,7 +1536,7 @@ func TestUpdateDataset_AcceptsValidCanvasComponentParams(t *testing.T) {
|
||||
insertDatasetUpdateCanvasKB(t, "kb-1", "tenant-1", "Original", "canvas-1")
|
||||
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
result, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserConfig: map[string]interface{}{
|
||||
"Parser:CustomRhyme": map[string]interface{}{
|
||||
"pdf": map[string]interface{}{},
|
||||
@@ -1568,7 +1568,7 @@ func TestUpdateDataset_SwitchCanvasToBuiltinValidatesAgainstBuiltin(t *testing.T
|
||||
chunkMethod := "naive"
|
||||
ctx := withDatasetTestContext(t.Context(), "tenant-1", "tenant-1")
|
||||
parseType := 1
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", "tenant-1", service.UpdateDatasetRequest{
|
||||
_, code, err := testDatasetUpdateService(t).UpdateDataset(ctx, "kb-1", service.UpdateDatasetRequest{
|
||||
ParserID: &chunkMethod,
|
||||
ParseType: &parseType,
|
||||
ParserConfig: map[string]interface{}{
|
||||
@@ -1602,13 +1602,13 @@ func TestUpdateDatasetPagerankUnchangedIsANoOpOnInfinity(t *testing.T) {
|
||||
svc.docEngine = fakeChatDocEngine{}
|
||||
|
||||
unchanged := int64(0) // insertDatasetUpdateKB stores pagerank 0
|
||||
if _, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1",
|
||||
if _, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1",
|
||||
service.UpdateDatasetRequest{Pagerank: &unchanged}); err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("unchanged pagerank: code=%d err=%v, want success (a no-op, as in Python)", code, err)
|
||||
}
|
||||
|
||||
changed := int64(7)
|
||||
_, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1", "tenant-1",
|
||||
_, code, err := svc.UpdateDataset(withDatasetTestContext(t.Context(), "tenant-1", "tenant-1"), "kb-1",
|
||||
service.UpdateDatasetRequest{Pagerank: &changed})
|
||||
if err == nil || !strings.Contains(err.Error(), "can only be set when doc_engine is elasticsearch") {
|
||||
t.Fatalf("changed pagerank: code=%d err=%v, want the doc_engine message", code, err)
|
||||
|
||||
@@ -29,7 +29,7 @@ type ApplyDatasetParserConfigResponse struct {
|
||||
|
||||
// ApplyDatasetParserConfig resets document parser settings without changing
|
||||
// parsed content or scheduling ingestion. Dataset grants cover its documents.
|
||||
func (s *DocumentService) ApplyDatasetParserConfig(ctx context.Context, userID, datasetID string) (*ApplyDatasetParserConfigResponse, common.ErrorCode, error) {
|
||||
func (s *DocumentService) ApplyDatasetParserConfig(ctx context.Context, datasetID string) (*ApplyDatasetParserConfigResponse, common.ErrorCode, error) {
|
||||
datasetID = strings.TrimSpace(datasetID)
|
||||
if datasetID == "" {
|
||||
return nil, common.CodeArgumentError, fmt.Errorf("dataset_id is required")
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestApplyDatasetParserConfigReplacesSettingsAndPreservesContent(t *testing.
|
||||
t.Fatal(err)
|
||||
}
|
||||
svc := testDocumentService(t)
|
||||
result, code, err := svc.ApplyDatasetParserConfig(documentTestContext(t.Context()), "user-1", "kb-1")
|
||||
result, code, err := svc.ApplyDatasetParserConfig(documentTestContext(t.Context()), "kb-1")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("apply: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func TestApplyDatasetParserConfigReplacesSettingsAndPreservesContent(t *testing.
|
||||
if err != nil || other.ParserID != "naive" || len(other.ParserConfig) != 0 {
|
||||
t.Fatalf("other dataset changed: doc=%+v err=%v", other, err)
|
||||
}
|
||||
result, code, err = svc.ApplyDatasetParserConfig(documentTestContext(t.Context()), "user-1", "kb-1")
|
||||
result, code, err = svc.ApplyDatasetParserConfig(documentTestContext(t.Context()), "kb-1")
|
||||
if err != nil || code != common.CodeSuccess || result.UpdatedDocuments != 0 || result.UnchangedDocuments != 205 {
|
||||
t.Fatalf("repeat: result=%+v code=%d err=%v", result, code, err)
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func TestApplyDatasetParserConfigRollsBackAllBatches(t *testing.T) {
|
||||
} else {
|
||||
insertTestIngestionTaskWithStatus(t, "task-1", "user-1", "doc-200", "kb-1", failure)
|
||||
}
|
||||
result, code, err := testDocumentService(t).ApplyDatasetParserConfig(documentTestContext(t.Context()), "user-1", "kb-1")
|
||||
result, code, err := testDocumentService(t).ApplyDatasetParserConfig(documentTestContext(t.Context()), "kb-1")
|
||||
if err == nil || code != wantCode || result != nil {
|
||||
t.Fatalf("result=%+v code=%d err=%v", result, code, err)
|
||||
}
|
||||
@@ -136,7 +136,7 @@ func TestApplyDatasetParserConfigResolvesMixedFileTypes(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
_, code, err := testDocumentService(t).ApplyDatasetParserConfig(documentTestContext(t.Context()), "user-1", "kb-1")
|
||||
_, code, err := testDocumentService(t).ApplyDatasetParserConfig(documentTestContext(t.Context()), "kb-1")
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("apply: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -159,21 +159,21 @@ func TestApplyDatasetParserConfigAuthorizationAndEmptyDataset(t *testing.T) {
|
||||
}
|
||||
svc := testDocumentService(t)
|
||||
for _, id := range []string{"kb-1", "missing"} {
|
||||
result, code, err := svc.ApplyDatasetParserConfig(documentTestActorContext(t.Context(), "outsider", "tenant-other"), "outsider", id)
|
||||
result, code, err := svc.ApplyDatasetParserConfig(documentTestActorContext(t.Context(), "outsider", "tenant-other"), id)
|
||||
if err == nil || code == common.CodeSuccess || result != nil {
|
||||
t.Fatalf("unauthorized apply: result=%+v code=%d err=%v", result, code, err)
|
||||
}
|
||||
}
|
||||
result, code, err := svc.ApplyDatasetParserConfig(documentTestContext(t.Context()), "user-1", "kb-1")
|
||||
result, code, err := svc.ApplyDatasetParserConfig(documentTestContext(t.Context()), "kb-1")
|
||||
if err != nil || code != common.CodeSuccess || result.TotalDocuments != 0 {
|
||||
t.Fatalf("empty dataset: result=%+v code=%d err=%v", result, code, err)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(documentTestContext(t.Context()))
|
||||
cancel()
|
||||
if _, _, err := svc.ApplyDatasetParserConfig(ctx, "user-1", "kb-1"); err == nil {
|
||||
if _, _, err := svc.ApplyDatasetParserConfig(ctx, "kb-1"); err == nil {
|
||||
t.Fatal("canceled request succeeded")
|
||||
}
|
||||
if _, _, err := svc.ApplyDatasetParserConfig(t.Context(), "user-1", " "); err == nil || !strings.Contains(err.Error(), "dataset_id") {
|
||||
if _, _, err := svc.ApplyDatasetParserConfig(t.Context(), " "); err == nil || !strings.Contains(err.Error(), "dataset_id") {
|
||||
t.Fatalf("empty dataset ID: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func TestApplyDatasetParserConfigCopiesPipelineVersion(t *testing.T) {
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, code, err := testDocumentService(t).ApplyDatasetParserConfig(documentTestContext(t.Context()), "user-1", "kb-1")
|
||||
_, code, err := testDocumentService(t).ApplyDatasetParserConfig(documentTestContext(t.Context()), "kb-1")
|
||||
doc, readErr := dao.NewDocumentDAO().GetByID(t.Context(), db, "doc-1")
|
||||
if readErr != nil {
|
||||
t.Fatal(readErr)
|
||||
|
||||
@@ -132,7 +132,7 @@ func TestIngest_ApplyKB_RealDatasetAPIChain(t *testing.T) {
|
||||
|
||||
ctx := documentTestContext(t.Context())
|
||||
enabled := true
|
||||
if _, code, err := newDatasetService().UpdateMetadataConfig(ctx, "kb-1", "user-1", &service.MetadataConfigRequest{
|
||||
if _, code, err := newDatasetService().UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Enabled: &enabled,
|
||||
Metadata: []service.MetadataConfigField{{Key: "author", Type: "string"}},
|
||||
BuiltInMetadata: []service.MetadataConfigField{{Key: "document_name", Type: "string"}},
|
||||
@@ -163,7 +163,7 @@ func TestIngest_ApplyKB_RealDatasetAPIChain(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
@@ -247,7 +247,7 @@ func TestIngest_ApplyKB_DisabledDatasetAPIChain(t *testing.T) {
|
||||
|
||||
ctx := documentTestContext(t.Context())
|
||||
disabled := false
|
||||
if _, code, err := newDatasetService().UpdateMetadataConfig(ctx, "kb-1", "user-1", &service.MetadataConfigRequest{
|
||||
if _, code, err := newDatasetService().UpdateMetadataConfig(ctx, "kb-1", &service.MetadataConfigRequest{
|
||||
Enabled: &disabled,
|
||||
}); err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("UpdateMetadataConfig err=%v code=%v", err, code)
|
||||
@@ -257,7 +257,7 @@ func TestIngest_ApplyKB_DisabledDatasetAPIChain(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
@@ -386,7 +386,7 @@ func TestIngest_ApplyKB_RealCreatedDatasetChain(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
|
||||
code, err = svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err = svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
|
||||
@@ -24,7 +24,7 @@ var ErrDocumentImageNotFound = errors.New("document image not found")
|
||||
|
||||
// GetDocumentImage serves legacy KB-scoped image IDs only when an indexed
|
||||
// chunk proves which accessible document owns the exact image ID.
|
||||
func (s *DocumentService) GetDocumentImage(ctx context.Context, userID, imageID string) ([]byte, error) {
|
||||
func (s *DocumentService) GetDocumentImage(ctx context.Context, imageID string) ([]byte, error) {
|
||||
bucket, _, ok := strings.Cut(imageID, "-")
|
||||
if !ok || len(bucket) != 32 {
|
||||
return nil, ErrDocumentImageNotFound
|
||||
@@ -36,12 +36,12 @@ func (s *DocumentService) GetDocumentImage(ctx context.Context, userID, imageID
|
||||
if err != nil || docID == "" {
|
||||
return nil, ErrDocumentImageNotFound
|
||||
}
|
||||
return s.GetDocumentImageForDocument(ctx, userID, docID, imageID)
|
||||
return s.GetDocumentImageForDocument(ctx, docID, imageID)
|
||||
}
|
||||
|
||||
// GetDocumentImageForDocument retrieves an image after proving that the exact
|
||||
// composite image ID belongs to an indexed chunk of an accessible document.
|
||||
func (s *DocumentService) GetDocumentImageForDocument(ctx context.Context, userID, docID, imageID string) ([]byte, error) {
|
||||
func (s *DocumentService) GetDocumentImageForDocument(ctx context.Context, docID, imageID string) ([]byte, error) {
|
||||
doc, err := s.documentDAO.GetByID(ctx, dao.DB, docID)
|
||||
if err != nil || doc == nil || service.CheckDocumentAccess(ctx, doc.ID, doc.KbID, permission.OperationRead) != nil {
|
||||
return nil, ErrDocumentImageNotFound
|
||||
@@ -119,14 +119,14 @@ func (s *DocumentService) imageDocumentID(ctx context.Context, kbID, imageID, do
|
||||
|
||||
// GetDocumentArtifact retrieves a sandbox artifact from object storage.
|
||||
//
|
||||
// userID scopes the lookup: a CodeExec sandbox artifact is only
|
||||
// The request identity scopes the lookup: a CodeExec sandbox artifact is only
|
||||
// returned when the caller owns (or has team access to) at least
|
||||
// one agent session whose `message` references this filename (or
|
||||
// its `documents/artifact/<name>` URL form). The authorization
|
||||
// gate runs BEFORE the storage read so a probe of an unknown
|
||||
// filename cannot distinguish "you cannot see it" from "it
|
||||
// exists" — both return ErrArtifactNotFound. Mirrors PR #16169.
|
||||
func (s *DocumentService) GetDocumentArtifact(ctx context.Context, filename, userID string) (*ArtifactResponse, error) {
|
||||
func (s *DocumentService) GetDocumentArtifact(ctx context.Context, filename string) (*ArtifactResponse, error) {
|
||||
basename := filepath.Base(filename)
|
||||
if basename != filename || strings.Contains(filename, "/") || strings.Contains(filename, "\\") {
|
||||
return nil, ErrArtifactInvalidFilename
|
||||
@@ -138,7 +138,7 @@ func (s *DocumentService) GetDocumentArtifact(ctx context.Context, filename, use
|
||||
return nil, ErrArtifactInvalidFileType
|
||||
}
|
||||
|
||||
if !s.sandboxArtifactAccessible(ctx, basename, userID) {
|
||||
if !s.sandboxArtifactAccessible(ctx, basename) {
|
||||
// Same error as "object does not exist" to avoid leaking
|
||||
// whether the artifact exists for a different user/agent.
|
||||
return nil, ErrArtifactNotFound
|
||||
@@ -170,34 +170,21 @@ func (s *DocumentService) GetDocumentArtifact(ctx context.Context, filename, use
|
||||
}, nil
|
||||
}
|
||||
|
||||
// sandboxArtifactDialogIDsForUser returns the distinct agent
|
||||
// (canvas) dialog_ids for sessions owned by userID whose
|
||||
// `message` blob references filename. A CodeExec artifact URL
|
||||
// appears in `message` as either a bare filename or the
|
||||
// `documents/artifact/<name>` form, so the helper matches both.
|
||||
//
|
||||
// Implemented as a direct GORM query on the
|
||||
// API4Conversation table — GORM's `Contains` maps to MySQL
|
||||
// `LIKE '%...%'` which is fine here because the storage path is
|
||||
// short and indexed lookup on (user_id, exp_user_id) keeps the
|
||||
// scan narrow.
|
||||
func (s *DocumentService) sandboxArtifactDialogIDsForUser(ctx context.Context, filename, userID string) []string {
|
||||
// sandboxArtifactDialogIDs returns the distinct agent (canvas) dialog IDs for
|
||||
// sessions owned by the request identity whose message references filename.
|
||||
// Artifact URLs appear as either a bare filename or the
|
||||
// documents/artifact/<name> form.
|
||||
func (s *DocumentService) sandboxArtifactDialogIDs(ctx context.Context, filename string) []string {
|
||||
userID := permission.UserIDFromContext(ctx)
|
||||
if filename == "" || userID == "" {
|
||||
return nil
|
||||
}
|
||||
// Escape SQL LIKE wildcards (%, _) before building the pattern.
|
||||
// Without escaping, a caller could submit a filename like
|
||||
// "%.png" or "_" and the LIKE query would match arbitrary
|
||||
// referenced artifacts in any user's conversation — letting the
|
||||
// caller pass the authorization check against one filename and
|
||||
// then GET another artifact by name (PR review round 5, Major #8).
|
||||
//
|
||||
// Escape character: '!'. We avoid '\\' because SQL string
|
||||
// literal parsing of '\\' is driver-specific (SQLite treats
|
||||
// it as a single backslash, MySQL treats it as one, Postgres
|
||||
// rejects the unterminated string) — '!' is a benign character
|
||||
// in real filenames (artifact names rarely contain '!') and
|
||||
// parses identically in every driver.
|
||||
|
||||
// Escape SQL LIKE wildcards before building the pattern. Without this, a
|
||||
// filename such as %.png could match another user's referenced artifact.
|
||||
// Use ! as the escape character because backslash handling differs between
|
||||
// database drivers, while ! is parsed consistently by SQLite, MySQL, and
|
||||
// Postgres.
|
||||
filenameSafe := escapeSQLLikePattern(filename)
|
||||
artifactRefSafe := escapeSQLLikePattern("documents/artifact/" + filename)
|
||||
filenamePattern := "%" + filenameSafe + "%"
|
||||
@@ -207,34 +194,28 @@ func (s *DocumentService) sandboxArtifactDialogIDsForUser(ctx context.Context, f
|
||||
Select("c.dialog_id").
|
||||
Joins("JOIN api_4_conversation_message AS cm ON cm.conversation_id = c.id").
|
||||
Where("c.user_id = ? OR c.exp_user_id = ?", userID, userID).
|
||||
Where(`cm.content LIKE ? ESCAPE '!' OR cm.content LIKE ? ESCAPE '!'`,
|
||||
Where("cm.content LIKE ? ESCAPE '!' OR cm.content LIKE ? ESCAPE '!'",
|
||||
filenamePattern, artifactRefPattern).
|
||||
Distinct("c.dialog_id").
|
||||
Rows()
|
||||
Distinct("c.dialog_id").Rows()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var d string
|
||||
if err = rows.Scan(&d); err == nil && d != "" {
|
||||
dialogIDs[d] = struct{}{}
|
||||
var dialogID string
|
||||
if err = rows.Scan(&dialogID); err == nil && dialogID != "" {
|
||||
dialogIDs[dialogID] = struct{}{}
|
||||
}
|
||||
}
|
||||
out := make([]string, 0, len(dialogIDs))
|
||||
for d := range dialogIDs {
|
||||
out = append(out, d)
|
||||
for dialogID := range dialogIDs {
|
||||
out = append(out, dialogID)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// sandboxArtifactAccessible reports whether userID may read at least one
|
||||
// canvas whose session references filename.
|
||||
func (s *DocumentService) sandboxArtifactAccessible(ctx context.Context, filename, userID string) bool {
|
||||
if userID == "" {
|
||||
return false
|
||||
}
|
||||
for _, dialogID := range s.sandboxArtifactDialogIDsForUser(ctx, filename, userID) {
|
||||
func (s *DocumentService) sandboxArtifactAccessible(ctx context.Context, filename string) bool {
|
||||
for _, dialogID := range s.sandboxArtifactDialogIDs(ctx, filename) {
|
||||
if permission.NewDatabaseChecker(dao.DB).CheckResource(ctx, permission.ResourceRef{Kind: permission.ResourceKindCanvas, ID: dialogID}, permission.OperationRead) == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ func (s *DocumentService) DeleteDocument(ctx context.Context, id string) error {
|
||||
//
|
||||
// ids: specific document IDs; deleteAll: delete all docs in the dataset.
|
||||
// Returns the number of successfully deleted documents.
|
||||
func (s *DocumentService) DeleteDocuments(ctx context.Context, ids []string, deleteAll bool, datasetID, userID string) (int, error) {
|
||||
func (s *DocumentService) DeleteDocuments(ctx context.Context, ids []string, deleteAll bool, datasetID string) (int, error) {
|
||||
// Deleting the entire dataset requires dataset-level access; deleting
|
||||
// explicit documents also accepts direct document grants.
|
||||
if deleteAll {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (s *DocumentService) BatchUpdateDocumentStatus(ctx context.Context, userID, datasetID, status string, documentIDs []string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
func (s *DocumentService) BatchUpdateDocumentStatus(ctx context.Context, datasetID, status string, documentIDs []string) (map[string]interface{}, common.ErrorCode, error) {
|
||||
statusInt, convErr := strconv.Atoi(status)
|
||||
if convErr != nil {
|
||||
return nil, common.CodeArgumentError, fmt.Errorf("invalid status: %s", status)
|
||||
@@ -138,7 +138,7 @@ func (s *DocumentService) BatchUpdateDocumentStatus(ctx context.Context, userID,
|
||||
return result, common.CodeSuccess, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) UpdateDatasetDocument(ctx context.Context, userID, datasetID, documentID string, req *UpdateDatasetDocumentRequest, present map[string]bool) (*UpdateDatasetDocumentResponse, common.ErrorCode, error) {
|
||||
func (s *DocumentService) UpdateDatasetDocument(ctx context.Context, datasetID, documentID string, req *UpdateDatasetDocumentRequest, present map[string]bool) (*UpdateDatasetDocumentResponse, common.ErrorCode, error) {
|
||||
if err := service.CheckDocumentAccess(ctx, documentID, datasetID, permission.OperationUpdate); err != nil {
|
||||
code, permissionErr := permissionresponse.Normalize(err)
|
||||
return nil, code, permissionErr
|
||||
@@ -159,7 +159,7 @@ func (s *DocumentService) UpdateDatasetDocument(ctx context.Context, userID, dat
|
||||
}
|
||||
return nil, common.CodeServerError, err
|
||||
}
|
||||
if code, err := s.validateDatasetDocumentUpdate(ctx, datasetID, documentID, userID, doc, req, present); err != nil {
|
||||
if code, err := s.validateDatasetDocumentUpdate(ctx, datasetID, documentID, doc, req, present); err != nil {
|
||||
return nil, code, err
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ func (s *DocumentService) UpdateDatasetDocument(ctx context.Context, userID, dat
|
||||
}
|
||||
pipelinepkg.ApplyParentChildChunkerConfig(cleaned, parentChildOverrides)
|
||||
llmID := ""
|
||||
if modelInfo, modelErr := service.NewModelFactory().ResolveDefaultInfo(ctx, service.Identity{UserID: userID, TenantID: kb.TenantID}, entity.ModelTypeChat); modelErr == nil {
|
||||
if modelInfo, modelErr := service.NewModelFactory().ResolveDefaultInfo(ctx, service.Identity{TenantID: kb.TenantID}, entity.ModelTypeChat); modelErr == nil {
|
||||
llmID = service.ModelReference(modelInfo)
|
||||
}
|
||||
cleaned = service.ApplyComponentScopedParserConfig(cleaned, llmID)
|
||||
@@ -323,7 +323,7 @@ func (s *DocumentService) UpdateDatasetDocument(ctx context.Context, userID, dat
|
||||
zap.String("parserID", effParserID), zap.Error(cpErr))
|
||||
} else {
|
||||
llmID := ""
|
||||
if modelInfo, llmErr := service.NewModelFactory().ResolveDefaultInfo(ctx, service.Identity{UserID: userID, TenantID: kb.TenantID}, entity.ModelTypeChat); llmErr == nil {
|
||||
if modelInfo, llmErr := service.NewModelFactory().ResolveDefaultInfo(ctx, service.Identity{TenantID: kb.TenantID}, entity.ModelTypeChat); llmErr == nil {
|
||||
llmID = service.ModelReference(modelInfo)
|
||||
} else if !errors.Is(llmErr, service.ErrNoDefaultModel) {
|
||||
common.Warn("document pipeline/version switch: failed to resolve default chat model, falling back to no llm binding",
|
||||
@@ -420,7 +420,7 @@ func (s *DocumentService) validateDocumentModifiable(ctx context.Context, doc *e
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DocumentService) validateDatasetDocumentUpdate(ctx context.Context, datasetID, documentID, userID string, doc *entity.Document, req *UpdateDatasetDocumentRequest, present map[string]bool) (common.ErrorCode, error) {
|
||||
func (s *DocumentService) validateDatasetDocumentUpdate(ctx context.Context, datasetID, documentID string, doc *entity.Document, req *UpdateDatasetDocumentRequest, present map[string]bool) (common.ErrorCode, error) {
|
||||
if req == nil {
|
||||
return common.CodeDataError, errors.New("invalid request payload")
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestUpdateDatasetDocumentParentChildPatchUsesRequestedFields(t *testing.T)
|
||||
}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParserConfig: map[string]any{"GeneralChunker:SixApplesFall": tc.patch},
|
||||
}, map[string]bool{"parser_config": true})
|
||||
if code != common.CodeSuccess || err != nil {
|
||||
@@ -94,7 +94,7 @@ func TestUpdateDatasetDocumentConfigPatchPreservesOmittedSettings(t *testing.T)
|
||||
if err := db.Model(&entity.Document{}).Where("id = ?", "doc-1").Updates(updates).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("update: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func TestUpdateDatasetDocumentRejectsUnsupportedFileFamilyBeforeWrites(t *testin
|
||||
svc := testDocumentService(t)
|
||||
svc.docEngine = engine
|
||||
svc.metadataSvc = service.NewMetadataServiceForTest(dao.NewKnowledgebaseDAO(), engine)
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if code != common.CodeDataError || err == nil || !strings.Contains(err.Error(), "does not support") {
|
||||
t.Fatalf("expected unsupported file rejection: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -207,7 +207,7 @@ func TestUpdateDatasetDocumentParserAliasPreservesConfig(t *testing.T) {
|
||||
req.ParserConfig = map[string]any{"Parser:HipSignsRhyme": map[string]any{"enable_vision_enhancement": true}}
|
||||
present["parser_config"] = true
|
||||
}
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("update: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -259,7 +259,7 @@ func TestUpdateDatasetDocumentSelectionWithoutConfig(t *testing.T) {
|
||||
} else {
|
||||
req.ChunkMethod = new("manual")
|
||||
}
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("update: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func TestUpdateDatasetDocumentChunkMethodAcceptsCurrentTemplates(t *testing.T) {
|
||||
pushServiceDB(t, db)
|
||||
insertTestKB(t, "kb-1", "tenant-1", 1, 0, 0)
|
||||
insertNamedTestDoc(t, "doc-1", "kb-1", file.name, 0, 0)
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ChunkMethod: &file.method,
|
||||
}, map[string]bool{"chunk_method": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
@@ -339,7 +339,7 @@ func TestUpdateDatasetDocumentUsesEffectiveParserForConfig(t *testing.T) {
|
||||
} else if err := db.Model(&entity.Document{}).Where("id = ?", "doc-1").Update("parser_id", "manual").Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("update: code=%d err=%v", code, err)
|
||||
}
|
||||
@@ -379,7 +379,7 @@ func TestUpdateDatasetDocumentKeepsDocumentCanvasForConfigPatch(t *testing.T) {
|
||||
if err := db.Model(&entity.Document{}).Where("id = ?", "doc-1").Update("pipeline_id", pipelineID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParserConfig: map[string]any{"Parser:custom": map[string]any{"vlm": map[string]any{"llm_id": "vision-model"}}},
|
||||
}, map[string]bool{"parser_config": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
@@ -455,7 +455,7 @@ func TestUpdateDatasetDocumentRejectsParserErrorsBeforeWrites(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
svc.docEngine = engine
|
||||
svc.metadataSvc = service.NewMetadataServiceForTest(dao.NewKnowledgebaseDAO(), engine)
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if code != common.CodeDataError || err == nil || !strings.Contains(err.Error(), wantError) {
|
||||
t.Fatalf("expected %q, got code=%d err=%v", wantError, code, err)
|
||||
}
|
||||
@@ -497,7 +497,7 @@ func TestBatchUpdateDocumentStatusReturnsPermissionErrorWhenAllDenied(t *testing
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestActorContext(t.Context(), "member", "tenant-1")
|
||||
_, code, err := svc.BatchUpdateDocumentStatus(ctx, "member", "kb-1", "0", []string{"doc-1"})
|
||||
_, code, err := svc.BatchUpdateDocumentStatus(ctx, "kb-1", "0", []string{"doc-1"})
|
||||
if err == nil {
|
||||
t.Fatal("expected permission error for an inaccessible document")
|
||||
}
|
||||
@@ -530,7 +530,7 @@ func TestBatchUpdateDocumentStatusRetainsPartialFailure(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestActorContext(t.Context(), "member", "tenant-1")
|
||||
_, code, err := svc.BatchUpdateDocumentStatus(ctx, "member", "kb-1", "1", []string{"member-doc", "owner-doc"})
|
||||
_, code, err := svc.BatchUpdateDocumentStatus(ctx, "kb-1", "1", []string{"member-doc", "owner-doc"})
|
||||
if err == nil {
|
||||
t.Fatal("expected a partial failure")
|
||||
}
|
||||
|
||||
@@ -13,11 +13,15 @@ import (
|
||||
permissionresponse "ragflow/internal/permission/response"
|
||||
)
|
||||
|
||||
func (s *DocumentService) ListIngestionTasks(ctx context.Context, userID string, datasetID *string, page, pageSize int) ([]*entity.IngestionTask, error) {
|
||||
return s.ingestionTaskSvc.ListByUser(ctx, userID, datasetID, page, pageSize)
|
||||
func (s *DocumentService) ListIngestionTasks(ctx context.Context, datasetID *string, page, pageSize int) ([]*entity.IngestionTask, error) {
|
||||
return s.ingestionTaskSvc.ListByUser(ctx, permission.UserIDFromContext(ctx), datasetID, page, pageSize)
|
||||
}
|
||||
|
||||
func (s *DocumentService) IngestDocuments(ctx context.Context, datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
func (s *DocumentService) IngestDocuments(ctx context.Context, datasetID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
userID := permission.UserIDFromContext(ctx)
|
||||
if userID == "" {
|
||||
return nil, permission.ErrUnauthenticated
|
||||
}
|
||||
responses, err := s.ingestionTaskSvc.CreateForDocuments(ctx, datasetID, userID, docIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -26,15 +30,17 @@ func (s *DocumentService) IngestDocuments(ctx context.Context, datasetID, userID
|
||||
return responses, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) StopIngestionTasks(ctx context.Context, tasks []string, userID string) ([]*entity.IngestionTask, error) {
|
||||
func (s *DocumentService) StopIngestionTasks(ctx context.Context, tasks []string) ([]*entity.IngestionTask, error) {
|
||||
userID := permission.UserIDFromContext(ctx)
|
||||
return s.ingestionTaskSvc.RequestStopMany(ctx, tasks, &userID)
|
||||
}
|
||||
|
||||
func (s *DocumentService) RemoveIngestionTasks(ctx context.Context, tasks []string, userID string) ([]map[string]string, error) {
|
||||
func (s *DocumentService) RemoveIngestionTasks(ctx context.Context, tasks []string) ([]map[string]string, error) {
|
||||
userID := permission.UserIDFromContext(ctx)
|
||||
return s.ingestionTaskSvc.RemoveMany(ctx, tasks, &userID)
|
||||
}
|
||||
|
||||
func (s *DocumentService) Ingest(ctx context.Context, userID string, req *IngestDocumentRequest) (common.ErrorCode, error) {
|
||||
func (s *DocumentService) Ingest(ctx context.Context, req *IngestDocumentRequest) (common.ErrorCode, error) {
|
||||
run := fmt.Sprint(req.Run)
|
||||
|
||||
docs, err := s.documentDAO.GetByIDs(ctx, dao.DB, req.DocIDs)
|
||||
@@ -112,7 +118,7 @@ func (s *DocumentService) Ingest(ctx context.Context, userID string, req *Ingest
|
||||
for _, vd := range toStart {
|
||||
doc := vd.doc
|
||||
kb := vd.kb
|
||||
if err = s.StartParseDocuments(ctx, doc, kb, userID, StartParseOptions{
|
||||
if err = s.StartParseDocuments(ctx, doc, kb, StartParseOptions{
|
||||
ApplyKB: req.ApplyKB,
|
||||
RerunWithDelete: req.Delete,
|
||||
}); err != nil {
|
||||
|
||||
@@ -139,7 +139,7 @@ func (s *DocumentService) ListDocuments(ctx context.Context, page, pageSize int)
|
||||
return responses, total, nil
|
||||
}
|
||||
|
||||
func (s *DocumentService) GetThumbnails(ctx context.Context, userID string, docIDs []string) (map[string]string, error) {
|
||||
func (s *DocumentService) GetThumbnails(ctx context.Context, docIDs []string) (map[string]string, error) {
|
||||
if len(docIDs) == 0 {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func updateDatasetDocumentRejected(t *testing.T, status string, req *UpdateDatas
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", req, present)
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", req, present)
|
||||
if err == nil {
|
||||
t.Fatalf("expected task-state rejection, got nil error (code=%v)", code)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func updateDatasetDocumentAllowed(t *testing.T, status string, req *UpdateDatase
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", req, present)
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", req, present)
|
||||
if err != nil && strings.Contains(err.Error(), "cannot be modified") {
|
||||
t.Fatalf("editable doc was wrongly rejected: %v", err)
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func TestUpdateDatasetDocumentRunningEmptyPresentNotRejected(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1",
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{}, map[string]bool{})
|
||||
if err != nil {
|
||||
t.Fatalf("empty PATCH on RUNNING doc should not be rejected: code=%v err=%v", code, err)
|
||||
@@ -231,7 +231,7 @@ func TestUpdateDatasetDocumentAllowsDoneRename(t *testing.T) {
|
||||
name := "renamed.txt"
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1",
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{Name: &name},
|
||||
map[string]bool{"name": true})
|
||||
if err != nil && strings.Contains(err.Error(), "cannot be modified") {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"ragflow/internal/entity"
|
||||
"ragflow/internal/ingestion/knowledge_compile"
|
||||
ingestionpipeline "ragflow/internal/ingestion/pipeline"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/storage"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -112,7 +113,10 @@ func (s *DocumentService) deletePreparedDocumentTask(ctx context.Context, docID
|
||||
// KB config (ApplyKB), validates storage, and enqueues an ingestion task.
|
||||
// Extracted from Ingest so other entry points (e.g. ChunkService.Parse)
|
||||
// can reuse the same start-parse flow.
|
||||
func (s *DocumentService) StartParseDocuments(ctx context.Context, doc *entity.Document, kb *entity.Knowledgebase, userID string, opts StartParseOptions) error {
|
||||
func (s *DocumentService) StartParseDocuments(ctx context.Context, doc *entity.Document, kb *entity.Knowledgebase, opts StartParseOptions) error {
|
||||
if permission.UserIDFromContext(ctx) == "" {
|
||||
return permission.ErrUnauthenticated
|
||||
}
|
||||
// Validate storage first so we don't clear prior results and then fail
|
||||
// because the document can't be read, leaving the document with neither
|
||||
// old nor new parse results.
|
||||
@@ -134,7 +138,7 @@ func (s *DocumentService) StartParseDocuments(ctx context.Context, doc *entity.D
|
||||
}
|
||||
}
|
||||
|
||||
responses, err := s.IngestDocuments(ctx, doc.KbID, userID, []string{doc.ID})
|
||||
responses, err := s.IngestDocuments(ctx, doc.KbID, []string{doc.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -431,7 +435,7 @@ func (s *DocumentService) clearKBChunkNumWhenRerun(doc *entity.Document) error {
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (s *DocumentService) ParseDocuments(ctx context.Context, datasetID, userID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
func (s *DocumentService) ParseDocuments(ctx context.Context, datasetID string, docIDs []string) ([]*service.ParseDocumentResponse, error) {
|
||||
// deduplicate the document id
|
||||
uniqueDocIDs := common.Deduplicate(docIDs)
|
||||
if uniqueDocIDs == nil || len(uniqueDocIDs) == 0 {
|
||||
|
||||
@@ -1210,7 +1210,7 @@ func TestSyncDocumentUpsertRemovesStagedBlobWhenInsertFails(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
_, err := svc.Upsert(ctx, service.DocumentUpsertInput{
|
||||
TaskContext: service.SyncTaskContext{
|
||||
Connector: entity.Connector{TenantID: "tenant-1"},
|
||||
Connector: entity.Connector{UserID: "tenant-1", TenantID: "tenant-1"},
|
||||
Knowledgebase: entity.Knowledgebase{
|
||||
ID: "missing-kb",
|
||||
TenantID: "tenant-1",
|
||||
@@ -1264,7 +1264,7 @@ func TestSyncDocumentUpsertWithNilDatasetParserConfig(t *testing.T) {
|
||||
|
||||
result, err := testDocumentService(t).Upsert(ctx, service.DocumentUpsertInput{
|
||||
TaskContext: service.SyncTaskContext{
|
||||
Connector: entity.Connector{TenantID: "tenant-1"},
|
||||
Connector: entity.Connector{UserID: "tenant-1", TenantID: "tenant-1"},
|
||||
Knowledgebase: kb,
|
||||
},
|
||||
SourceType: "github",
|
||||
@@ -1326,7 +1326,7 @@ func TestSyncDocumentUpsertRemovesStagedBlobWhenUpdateFails(t *testing.T) {
|
||||
mockStorage.afterPut = cancel
|
||||
_, err := svc.Upsert(ctx, service.DocumentUpsertInput{
|
||||
TaskContext: service.SyncTaskContext{
|
||||
Connector: entity.Connector{TenantID: "tenant-1"},
|
||||
Connector: entity.Connector{UserID: "tenant-1", TenantID: "tenant-1"},
|
||||
Knowledgebase: entity.Knowledgebase{
|
||||
ID: "kb-sync",
|
||||
TenantID: "tenant-1",
|
||||
@@ -1583,7 +1583,7 @@ func TestDeleteDocuments_DeleteAll(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
deleted, err := svc.DeleteDocuments(ctx, nil, true, "kb-1", "user-1")
|
||||
deleted, err := svc.DeleteDocuments(ctx, nil, true, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteDocuments failed: %v", err)
|
||||
}
|
||||
@@ -1613,7 +1613,7 @@ func TestDeleteDocuments_ByIDs(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
deleted, err := svc.DeleteDocuments(ctx, []string{"doc-1", "doc-2"}, false, "kb-1", "user-1")
|
||||
deleted, err := svc.DeleteDocuments(ctx, []string{"doc-1", "doc-2"}, false, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteDocuments failed: %v", err)
|
||||
}
|
||||
@@ -1641,7 +1641,7 @@ func TestDeleteDocuments_WrongDataset(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
ctx := t.Context()
|
||||
|
||||
_, err := svc.DeleteDocuments(ctx, []string{"doc-1"}, false, "kb-1", "user-1")
|
||||
_, err := svc.DeleteDocuments(ctx, []string{"doc-1"}, false, "kb-1")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for doc not belonging to dataset")
|
||||
}
|
||||
@@ -1657,7 +1657,7 @@ func TestDeleteDocuments_NotAccessible(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
// user-1 has no user_tenant entry → accessible returns false
|
||||
_, err := svc.DeleteDocuments(ctx, []string{"doc-1"}, false, "kb-1", "user-1")
|
||||
_, err := svc.DeleteDocuments(ctx, []string{"doc-1"}, false, "kb-1")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for inaccessible dataset")
|
||||
}
|
||||
@@ -1673,7 +1673,7 @@ func TestDeleteDocuments_EmptyIDs(t *testing.T) {
|
||||
|
||||
// Empty ids, no deleteAll → returns 0, no error
|
||||
ctx := t.Context()
|
||||
deleted, err := svc.DeleteDocuments(ctx, []string{}, false, "kb-1", "user-1")
|
||||
deleted, err := svc.DeleteDocuments(ctx, []string{}, false, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -1694,7 +1694,7 @@ func TestDeleteDocuments_Deduplicate(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
deleted, err := svc.DeleteDocuments(ctx, []string{"doc-1", "doc-1", "doc-1"}, false, "kb-1", "user-1")
|
||||
deleted, err := svc.DeleteDocuments(ctx, []string{"doc-1", "doc-1", "doc-1"}, false, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteDocuments failed: %v", err)
|
||||
}
|
||||
@@ -1773,7 +1773,7 @@ func TestStartParseDocuments_ApplyKBMergesDatasetParserConfig(t *testing.T) {
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
kb, err := svc.kbDAO.GetByID(ctx, db, "kb-1")
|
||||
if err != nil {
|
||||
@@ -1784,7 +1784,7 @@ func TestStartParseDocuments_ApplyKBMergesDatasetParserConfig(t *testing.T) {
|
||||
t.Fatalf("load doc: %v", err)
|
||||
}
|
||||
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{ApplyKB: true}); err != nil {
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{ApplyKB: true}); err != nil {
|
||||
t.Fatalf("StartParseDocuments: %v", err)
|
||||
}
|
||||
|
||||
@@ -1885,7 +1885,7 @@ func TestIngest_ApplyKBPersistsDatasetParserConfig(t *testing.T) {
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
@@ -1974,7 +1974,7 @@ func TestStartParseDocuments_ApplyKBUsesDatasetDefaults(t *testing.T) {
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
kb, err := svc.kbDAO.GetByID(ctx, db, "kb-1")
|
||||
if err != nil {
|
||||
@@ -1985,7 +1985,7 @@ func TestStartParseDocuments_ApplyKBUsesDatasetDefaults(t *testing.T) {
|
||||
t.Fatalf("load doc: %v", err)
|
||||
}
|
||||
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{ApplyKB: true}); err != nil {
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{ApplyKB: true}); err != nil {
|
||||
t.Fatalf("StartParseDocuments: %v", err)
|
||||
}
|
||||
|
||||
@@ -2080,7 +2080,7 @@ func TestIngest_ApplyKBDisabledLeavesParserConfigUnchanged(t *testing.T) {
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
@@ -2165,7 +2165,7 @@ func TestStartParseDocuments_ApplyKBPersistFailureNamesDocument(t *testing.T) {
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
kb, err := svc.kbDAO.GetByID(ctx, db, "kb-1")
|
||||
if err != nil {
|
||||
@@ -2176,7 +2176,7 @@ func TestStartParseDocuments_ApplyKBPersistFailureNamesDocument(t *testing.T) {
|
||||
t.Fatalf("load doc: %v", err)
|
||||
}
|
||||
|
||||
err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{ApplyKB: true})
|
||||
err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{ApplyKB: true})
|
||||
if err == nil {
|
||||
t.Fatal("expected an error when the merged parser config cannot be persisted")
|
||||
}
|
||||
@@ -2261,7 +2261,7 @@ func TestStartParseDocuments_EnqueuesIngestionTask(t *testing.T) {
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
kb, err := svc.kbDAO.GetByID(ctx, db, "kb-1")
|
||||
if err != nil {
|
||||
@@ -2272,7 +2272,7 @@ func TestStartParseDocuments_EnqueuesIngestionTask(t *testing.T) {
|
||||
t.Fatalf("load doc: %v", err)
|
||||
}
|
||||
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{}); err != nil {
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{}); err != nil {
|
||||
t.Fatalf("StartParseDocuments: %v", err)
|
||||
}
|
||||
|
||||
@@ -2317,6 +2317,28 @@ func TestStartParseDocuments_EnqueuesIngestionTask(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIngestDocumentsRejectsMissingUserIdentity(t *testing.T) {
|
||||
db := setupServiceTestDB(t)
|
||||
pushServiceDB(t, db)
|
||||
|
||||
svc := testDocumentService(t)
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
if _, err := svc.IngestDocuments(t.Context(), "kb-1", []string{"doc-1"}); !errors.Is(err, permission.ErrUnauthenticated) {
|
||||
t.Fatalf("IngestDocuments error = %v, want unauthenticated", err)
|
||||
}
|
||||
if len(publisher.messages) != 0 {
|
||||
t.Fatalf("parse publications = %d, want none", len(publisher.messages))
|
||||
}
|
||||
var taskCount int64
|
||||
if err := db.Model(&entity.IngestionTask{}).Count(&taskCount).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if taskCount != 0 {
|
||||
t.Fatalf("ingestion tasks = %d, want none", taskCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartParseDocumentsSerializesConcurrentReruns(t *testing.T) {
|
||||
db := setupConcurrentServiceTestDB(t)
|
||||
pushServiceDB(t, db)
|
||||
@@ -2347,7 +2369,7 @@ func TestStartParseDocumentsSerializesConcurrentReruns(t *testing.T) {
|
||||
secondPublisher := &runningTaskPublisher{db: db, published: make(chan struct{})}
|
||||
secondService := testDocumentService(t)
|
||||
secondService.ingestionTaskSvc.SetTaskPublisher(secondPublisher)
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
kb, err := firstService.kbDAO.GetByID(ctx, db, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("load knowledgebase: %v", err)
|
||||
@@ -2359,13 +2381,13 @@ func TestStartParseDocumentsSerializesConcurrentReruns(t *testing.T) {
|
||||
|
||||
firstDone := make(chan error, 1)
|
||||
go func() {
|
||||
firstDone <- firstService.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{RerunWithDelete: true})
|
||||
firstDone <- firstService.StartParseDocuments(ctx, doc, kb, StartParseOptions{RerunWithDelete: true})
|
||||
}()
|
||||
<-firstTaskLookup
|
||||
|
||||
secondDone := make(chan error, 1)
|
||||
go func() {
|
||||
secondDone <- secondService.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{RerunWithDelete: true})
|
||||
secondDone <- secondService.StartParseDocuments(ctx, doc, kb, StartParseOptions{RerunWithDelete: true})
|
||||
}()
|
||||
|
||||
secondPublishedBeforeFirstCompletes := false
|
||||
@@ -2398,7 +2420,7 @@ func TestStartParseDocumentsSupersedesQueuedRun(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(&recordingTaskPublisher{})
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
kb, err := svc.kbDAO.GetByID(ctx, db, "kb-1")
|
||||
if err != nil {
|
||||
t.Fatalf("load knowledgebase: %v", err)
|
||||
@@ -2408,10 +2430,10 @@ func TestStartParseDocumentsSupersedesQueuedRun(t *testing.T) {
|
||||
t.Fatalf("load document: %v", err)
|
||||
}
|
||||
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{}); err != nil {
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{}); err != nil {
|
||||
t.Fatalf("start first parse: %v", err)
|
||||
}
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{RerunWithDelete: true}); err != nil {
|
||||
if err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{RerunWithDelete: true}); err != nil {
|
||||
t.Fatalf("rerun queued parse: %v", err)
|
||||
}
|
||||
|
||||
@@ -2865,7 +2887,7 @@ func TestArtifactHelpers(t *testing.T) {
|
||||
func TestGetDocumentArtifact_InvalidFilename(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
ctx := t.Context()
|
||||
_, err := svc.GetDocumentArtifact(ctx, "../test.txt", "user-1")
|
||||
_, err := svc.GetDocumentArtifact(ctx, "../test.txt")
|
||||
if !errors.Is(err, ErrArtifactInvalidFilename) {
|
||||
t.Errorf("expected ErrArtifactInvalidFilename, got %v", err)
|
||||
}
|
||||
@@ -2874,7 +2896,7 @@ func TestGetDocumentArtifact_InvalidFilename(t *testing.T) {
|
||||
func TestGetDocumentArtifact_InvalidFileType(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
ctx := t.Context()
|
||||
_, err := svc.GetDocumentArtifact(ctx, "test.exe", "user-1")
|
||||
_, err := svc.GetDocumentArtifact(ctx, "test.exe")
|
||||
if !errors.Is(err, ErrArtifactInvalidFileType) {
|
||||
t.Errorf("expected ErrArtifactInvalidFileType, got %v", err)
|
||||
}
|
||||
@@ -3079,7 +3101,7 @@ func TestUpdateDatasetDocumentRejectsNonOwner(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestActorContext(t.Context(), "user-2", "tenant-1")
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-2", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{}, map[string]bool{})
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{}, map[string]bool{})
|
||||
if err == nil {
|
||||
t.Fatal("expected ownership error")
|
||||
}
|
||||
@@ -3100,7 +3122,7 @@ func TestUpdateDatasetDocumentRejectsCounterMutation(t *testing.T) {
|
||||
chunkCount := int64(6)
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ChunkCount: &chunkCount,
|
||||
}, map[string]bool{"chunk_count": true})
|
||||
if err == nil {
|
||||
@@ -3138,7 +3160,7 @@ func TestUpdateDatasetDocumentRejectsZeroImmutableFields(t *testing.T) {
|
||||
t.Fatalf("prepare document: %v", err)
|
||||
}
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &tt.request, tt.present)
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &tt.request, tt.present)
|
||||
if err == nil {
|
||||
t.Fatalf("expected %s mutation error", tt.name)
|
||||
}
|
||||
@@ -3287,7 +3309,7 @@ func TestUpdateDatasetDocumentSavesVisionModelAcrossFileTypes(t *testing.T) {
|
||||
} else {
|
||||
req.ChunkMethod = &file.parserID
|
||||
}
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", req, present)
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", req, present)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("save vision model: code=%v err=%v", code, err)
|
||||
}
|
||||
@@ -3321,7 +3343,7 @@ func TestUpdateDatasetDocumentRejectedParserConfigPreservesNames(t *testing.T) {
|
||||
insertTestFile(t, "file-1", "folder-1", "old.pdf", new("old.pdf"))
|
||||
insertTestFile2Document(t, "f2d-1", "file-1", "doc-1")
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
Name: new("must_not_persist.pdf"),
|
||||
ParserConfig: map[string]any{
|
||||
"Parser:HipSignsRhyme": map[string]any{
|
||||
@@ -3362,7 +3384,7 @@ func TestUpdateDatasetDocumentRenameUpdatesDocumentAndFile(t *testing.T) {
|
||||
newName := "new.pdf"
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
Name: &newName,
|
||||
}, map[string]bool{"name": true})
|
||||
if err != nil {
|
||||
@@ -3428,7 +3450,7 @@ func TestUpdateDatasetDocumentParserConfigPreservesParseResults(t *testing.T) {
|
||||
engine := &rerunDeleteDocEngine{}
|
||||
svc := testDocumentService(t)
|
||||
svc.docEngine = engine
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &request, present)
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &request, present)
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("UpdateDatasetDocument failed: code=%v err=%v", code, err)
|
||||
}
|
||||
@@ -3863,7 +3885,7 @@ func TestIngest_RunningTask_SkippedWhenBatchStartingParse(t *testing.T) {
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1", "doc-2", "doc-3"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
@@ -3906,7 +3928,7 @@ func TestIngest_AllActiveTasks_ReturnsSuccessIdempotent(t *testing.T) {
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1", "doc-2"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: true,
|
||||
@@ -3939,7 +3961,7 @@ func TestIngest_CompletedTask_SkippedWhenDeleteIsFalse(t *testing.T) {
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1", "doc-2"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: false,
|
||||
@@ -3981,7 +4003,7 @@ func TestIngest_FailedAndStoppedTasks_Restarted(t *testing.T) {
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1", "doc-2"},
|
||||
Run: string(entity.TaskStatusRunning),
|
||||
Delete: false,
|
||||
@@ -4010,7 +4032,7 @@ func TestUpdateDatasetDocumentPropagatesMetadataDeleteFailure(t *testing.T) {
|
||||
svc.docEngine = engine
|
||||
svc.metadataSvc = service.NewMetadataServiceForTest(nil, nil)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
MetaFields: map[string]any{"new": "value"},
|
||||
}, map[string]bool{"meta_fields": true})
|
||||
if err == nil {
|
||||
@@ -4351,7 +4373,7 @@ func TestUpdateDatasetDocumentPipelineIDTakesPrecedenceOverParserID(t *testing.T
|
||||
parseType := 2
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
PipelineID: &pipelineID,
|
||||
PipelineVersionID: sptr("v-pre-0001"),
|
||||
ParseType: &parseType,
|
||||
@@ -4387,7 +4409,7 @@ func TestUpdateDatasetDocumentParseTypeBuiltin(t *testing.T) {
|
||||
pipelineIDEmpty := ""
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParseType: &parseType,
|
||||
ParserID: &parserID,
|
||||
PipelineID: &pipelineIDEmpty,
|
||||
@@ -4424,7 +4446,7 @@ func TestUpdateDatasetDocumentParseTypePipeline(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
seedDocVersion(t, db, pipelineID, "v-ptp-0001", 0)
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParseType: &parseType,
|
||||
PipelineID: &pipelineID,
|
||||
PipelineVersionID: sptr("v-ptp-0001"),
|
||||
@@ -4455,7 +4477,7 @@ func TestUpdateDatasetDocumentParseTypePipelineRejectsParserID(t *testing.T) {
|
||||
pipelineID := "1234567890abcdef1234567890abcdef"
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParseType: &parseType,
|
||||
ParserID: &parserID,
|
||||
PipelineID: &pipelineID,
|
||||
@@ -4485,7 +4507,7 @@ func TestUpdateDatasetDocumentParentChildConfigReachesCanvasChunker(t *testing.T
|
||||
// submitted parent_child would be dropped on a switch; this test verifies the
|
||||
// config reaches the canvas chunker when no switch occurs.
|
||||
bindDocCanvas(t, db, "kb-1", "doc-1", pipelineID, "v-pcc-0001")
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParserConfig: map[string]any{
|
||||
"GeneralChunker:SixApplesFall": map[string]any{
|
||||
"parent_child": map[string]any{
|
||||
@@ -4520,7 +4542,7 @@ func TestUpdateDatasetDocumentRejectsInvalidPages(t *testing.T) {
|
||||
parserID := "manual"
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParseType: &parseType,
|
||||
ParserID: &parserID,
|
||||
ParserConfig: map[string]any{
|
||||
@@ -4545,7 +4567,7 @@ func TestUpdateDatasetDocumentParentChildConfigReachesGeneralChunker(t *testing.
|
||||
insertTestKB(t, "kb-1", "tenant-1", 1, 10, 5)
|
||||
insertNamedTestDoc(t, "doc-1", "kb-1", "doc.txt", 10, 5)
|
||||
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParserConfig: map[string]any{
|
||||
"GeneralChunker:SixApplesFall": map[string]any{
|
||||
"parent_child": map[string]any{
|
||||
@@ -4583,7 +4605,7 @@ func TestUpdateDatasetDocumentEnabledUpdatesStatus(t *testing.T) {
|
||||
enabled := 0
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
resp, code, err := svc.UpdateDatasetDocument(ctx, "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
Enabled: &enabled,
|
||||
}, map[string]bool{"enabled": true})
|
||||
if err != nil {
|
||||
@@ -4754,7 +4776,7 @@ func TestGetDocumentArtifact_AuthGate(t *testing.T) {
|
||||
}
|
||||
|
||||
// Case 0: the owner can fetch the artifact referenced by their session.
|
||||
artifact, err := svc.GetDocumentArtifact(ctx, "result.png", "user-1")
|
||||
artifact, err := svc.GetDocumentArtifact(ctx, "result.png")
|
||||
if err != nil {
|
||||
t.Fatalf("owner: want artifact, got %v", err)
|
||||
}
|
||||
@@ -4763,12 +4785,13 @@ func TestGetDocumentArtifact_AuthGate(t *testing.T) {
|
||||
}
|
||||
|
||||
// Case 1: empty user -> not allowed.
|
||||
if _, err := svc.GetDocumentArtifact(ctx, "result.png", ""); !errors.Is(err, ErrArtifactNotFound) {
|
||||
if _, err := svc.GetDocumentArtifact(t.Context(), "result.png"); !errors.Is(err, ErrArtifactNotFound) {
|
||||
t.Errorf("empty user: want ErrArtifactNotFound, got %v", err)
|
||||
}
|
||||
|
||||
// Case 2: another user without any session reference -> not allowed.
|
||||
if _, err := svc.GetDocumentArtifact(ctx, "result.png", "user-2"); !errors.Is(err, ErrArtifactNotFound) {
|
||||
otherUserCtx := documentTestActorContext(t.Context(), "user-2", "tenant-1")
|
||||
if _, err := svc.GetDocumentArtifact(otherUserCtx, "result.png"); !errors.Is(err, ErrArtifactNotFound) {
|
||||
t.Errorf("unrelated user: want ErrArtifactNotFound, got %v", err)
|
||||
}
|
||||
|
||||
@@ -4791,7 +4814,7 @@ func TestGetDocumentArtifact_AuthGate(t *testing.T) {
|
||||
}); err != nil {
|
||||
t.Fatalf("seed conv 2: %v", err)
|
||||
}
|
||||
if _, err := svc.GetDocumentArtifact(ctx, "result.png", "user-2"); !errors.Is(err, ErrArtifactNotFound) {
|
||||
if _, err := svc.GetDocumentArtifact(otherUserCtx, "result.png"); !errors.Is(err, ErrArtifactNotFound) {
|
||||
t.Errorf("user-2 with unrelated session: want ErrArtifactNotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -4861,7 +4884,7 @@ func TestGetThumbnails_AlignsWithPythonFormatting(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
got, err := svc.GetThumbnails(ctx, "user-1", []string{"doc-file", "doc-base64", "doc-other", "missing-doc"})
|
||||
got, err := svc.GetThumbnails(ctx, []string{"doc-file", "doc-base64", "doc-other", "missing-doc"})
|
||||
if err != nil {
|
||||
t.Fatalf("GetThumbnails failed: %v", err)
|
||||
}
|
||||
@@ -4900,7 +4923,7 @@ func TestGetDocumentImageRejectsUnindexedObjectBeforeStorage(t *testing.T) {
|
||||
svc := NewDocumentService()
|
||||
svc.docEngine = engine
|
||||
|
||||
if _, err := svc.GetDocumentImage(ctx, "user-1", kbID+"-raw.pdf"); !errors.Is(err, ErrDocumentImageNotFound) {
|
||||
if _, err := svc.GetDocumentImage(ctx, kbID+"-raw.pdf"); !errors.Is(err, ErrDocumentImageNotFound) {
|
||||
t.Fatalf("GetDocumentImage() error = %v", err)
|
||||
}
|
||||
if store.getCalls != 0 {
|
||||
@@ -4934,7 +4957,7 @@ func TestGetDocumentImageForDocumentAllowsIndexedSharedBucketImage(t *testing.T)
|
||||
svc := NewDocumentService()
|
||||
svc.docEngine = engine
|
||||
|
||||
got, err := svc.GetDocumentImageForDocument(ctx, "user-1", "doc-1", imageID)
|
||||
got, err := svc.GetDocumentImageForDocument(ctx, "doc-1", imageID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDocumentImageForDocument() error = %v", err)
|
||||
}
|
||||
@@ -4988,7 +5011,7 @@ func TestStartParseDocuments_FailsBeforeClearing(t *testing.T) {
|
||||
insertTestDoc(t, "doc-1", "kb-1", 10, 5)
|
||||
insertTestIngestionTaskWithStatus(t, "task-1", "user-1", "doc-1", "kb-1", common.COMPLETED)
|
||||
|
||||
ctx := t.Context()
|
||||
ctx := documentTestContext(t.Context())
|
||||
doc, err := dao.NewDocumentDAO().GetByID(ctx, db, "doc-1")
|
||||
if err != nil {
|
||||
t.Fatalf("get doc: %v", err)
|
||||
@@ -5004,7 +5027,7 @@ func TestStartParseDocuments_FailsBeforeClearing(t *testing.T) {
|
||||
}
|
||||
|
||||
svc := testDocumentService(t)
|
||||
err = svc.StartParseDocuments(ctx, doc, kb, "user-1", StartParseOptions{RerunWithDelete: true})
|
||||
err = svc.StartParseDocuments(ctx, doc, kb, StartParseOptions{RerunWithDelete: true})
|
||||
if err == nil {
|
||||
t.Fatal("expected error from GetDocumentStorageAddress, got nil")
|
||||
}
|
||||
@@ -5030,7 +5053,7 @@ func TestIngest_CancelDoesNotDeleteIngestionTask(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
_, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
_, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
Delete: true,
|
||||
@@ -5059,7 +5082,7 @@ func TestIngest_CancelUnstartedDocument(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5087,7 +5110,7 @@ func TestIngest_CancelCompletedDocument(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5115,7 +5138,7 @@ func TestIngest_CancelUnstartedWithInFlightTask(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5147,7 +5170,7 @@ func TestIngest_CancelAgainWhenAlreadyStopped(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5172,7 +5195,7 @@ func TestIngest_CancelScheduledTask_TransitionsToStopped(t *testing.T) {
|
||||
|
||||
svc := testDocumentService(t)
|
||||
ctx := documentTestContext(t.Context())
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5231,7 +5254,7 @@ func TestIngest_CancelRunningTask_LifecycleToStopped(t *testing.T) {
|
||||
ctx := documentTestContext(t.Context())
|
||||
|
||||
// 1. First cancel call: transitions RUNNING -> STOPPING
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5259,7 +5282,7 @@ func TestIngest_CancelRunningTask_LifecycleToStopped(t *testing.T) {
|
||||
}
|
||||
|
||||
// 2. Duplicate cancel while in STOPPING must succeed as an idempotent no-op
|
||||
code, err = svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err = svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: string(entity.TaskStatusCancel),
|
||||
})
|
||||
@@ -5311,7 +5334,7 @@ func TestIngest_DeleteOnlyCleansTasks(t *testing.T) {
|
||||
svc := testDocumentService(t)
|
||||
svc.purgeTaskState = func(context.Context, string) error { return nil }
|
||||
ctx := documentTestContext(t.Context())
|
||||
code, err := svc.Ingest(ctx, "user-1", &IngestDocumentRequest{
|
||||
code, err := svc.Ingest(ctx, &IngestDocumentRequest{
|
||||
DocIDs: []string{"doc-1"},
|
||||
Run: "0",
|
||||
Delete: true,
|
||||
|
||||
@@ -85,7 +85,7 @@ func TestUpdateDatasetDocumentListReturnsPinnedVersion(t *testing.T) {
|
||||
pid := canvasID
|
||||
pv := "v-pinned"
|
||||
pt := 2
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParseType: &pt,
|
||||
PipelineID: &pid,
|
||||
PipelineVersionID: &pv,
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestUpdateDatasetDocument_PipelineChangeWithoutVersionRejected(t *testing.T
|
||||
|
||||
// Switch to canvasB, omitting pipeline_version_id entirely.
|
||||
parseType := 2 // Pipeline mode
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{ParseType: &parseType, PipelineID: sptr(canvasB)},
|
||||
map[string]bool{"pipeline_id": true})
|
||||
if err == nil || code == common.CodeSuccess {
|
||||
|
||||
@@ -95,7 +95,7 @@ func TestUpdateDatasetDocumentUsesPinnedPipelineVersionForParserConfig(t *testin
|
||||
}
|
||||
|
||||
svc := testDocumentService(t)
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
_, code, err := svc.UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1", &UpdateDatasetDocumentRequest{
|
||||
ParserConfig: map[string]any{"Parser:Shared": map[string]any{"k": "override_val"}},
|
||||
}, map[string]bool{"parser_config": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
|
||||
@@ -91,7 +91,7 @@ func TestUpdateDatasetDocument_PersistsPinnedPipelineVersion(t *testing.T) {
|
||||
seedDocVersion(t, db, canvasID, "v-new", 200)
|
||||
bindDocCanvas(t, db, "kb-1", "doc-1", canvasID, "v-old")
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("v-new")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
@@ -117,7 +117,7 @@ func TestUpdateDatasetDocument_RejectsForeignPipelineVersion(t *testing.T) {
|
||||
seedDocVersion(t, db, canvasB, "v-foreign", 100)
|
||||
bindDocCanvas(t, db, "kb-1", "doc-1", canvasA, "v-a")
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("v-foreign")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err == nil {
|
||||
@@ -145,7 +145,7 @@ func TestUpdateDatasetDocument_RejectsMissingPipelineVersion(t *testing.T) {
|
||||
seedDocVersion(t, db, canvasID, "v-a", 100)
|
||||
bindDocCanvas(t, db, "kb-1", "doc-1", canvasID, "v-a")
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("does-not-exist")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err == nil || code != common.CodeDataError {
|
||||
@@ -167,7 +167,7 @@ func TestUpdateDatasetDocument_PreservesPipelineVersionWhenAbsent(t *testing.T)
|
||||
seedDocVersion(t, db, canvasID, "v-old", 100)
|
||||
bindDocCanvas(t, db, "kb-1", "doc-1", canvasID, "v-old")
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{}, map[string]bool{})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
t.Fatalf("UpdateDatasetDocument err=%v code=%d", err, code)
|
||||
@@ -191,7 +191,7 @@ func TestUpdateDatasetDocument_EmptyPipelineVersionTracksLatest(t *testing.T) {
|
||||
// C5/G3: an explicit EMPTY version in Pipeline mode is REJECTED (design
|
||||
// section 3: empty version is no longer auto-backfilled to latest). The pin
|
||||
// must be preserved and the save must fail with ErrPipelineVersionEmpty.
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err == nil || code == common.CodeSuccess {
|
||||
@@ -211,7 +211,7 @@ func TestUpdateDatasetDocument_BuiltinIgnoresPipelineVersion(t *testing.T) {
|
||||
insertTestKB(t, "kb-1", "tenant-1", 1, 10, 5)
|
||||
insertNamedTestDoc(t, "doc-1", "kb-1", "doc.txt", 10, 5)
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("v-whatever")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
@@ -234,7 +234,7 @@ func TestUpdateDatasetDocument_CleansParserConfigWithNewVersion(t *testing.T) {
|
||||
seedDocVersion(t, db, canvasID, "v-new", 200)
|
||||
bindDocCanvas(t, db, "kb-1", "doc-1", canvasID, "v-old")
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{
|
||||
PipelineVersionID: sptr("v-new"),
|
||||
ParserConfig: map[string]any{"Parser:Shared": map[string]any{"k": "override"}},
|
||||
@@ -278,7 +278,7 @@ func TestUpdateDatasetDocument_VersionSwitchResetsParserConfigToNewDefault(t *te
|
||||
t.Fatalf("seed parser_config: %v", err)
|
||||
}
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("v-new")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
@@ -320,7 +320,7 @@ func TestUpdateDatasetDocument_SameVersionKeepsParserConfig(t *testing.T) {
|
||||
t.Fatalf("seed parser_config: %v", err)
|
||||
}
|
||||
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "user-1", "kb-1", "doc-1",
|
||||
_, code, err := testDocumentService(t).UpdateDatasetDocument(documentTestContext(t.Context()), "kb-1", "doc-1",
|
||||
&UpdateDatasetDocumentRequest{PipelineVersionID: sptr("v-old")},
|
||||
map[string]bool{"pipeline_version_id": true})
|
||||
if err != nil || code != common.CodeSuccess {
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
"ragflow/internal/dao"
|
||||
"ragflow/internal/entity"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/service"
|
||||
"ragflow/internal/storage"
|
||||
"ragflow/internal/utility"
|
||||
@@ -36,6 +37,18 @@ import (
|
||||
|
||||
var syncFilenameUnsafeRE = regexp.MustCompile(`[\\/:*?"<>|]+`)
|
||||
|
||||
func syncDocumentParseContext(ctx context.Context, taskContext service.SyncTaskContext) (context.Context, error) {
|
||||
userID := strings.TrimSpace(taskContext.Connector.UserID)
|
||||
if userID == "" {
|
||||
return nil, fmt.Errorf("connector %s has no owner user_id", taskContext.Connector.ID)
|
||||
}
|
||||
tenantID := strings.TrimSpace(taskContext.Connector.TenantID)
|
||||
if tenantID == "" {
|
||||
return nil, fmt.Errorf("connector %s has no tenant_id", taskContext.Connector.ID)
|
||||
}
|
||||
return permission.WithIdentity(ctx, userID, tenantID), nil
|
||||
}
|
||||
|
||||
// Upsert stores one normalized source document.
|
||||
func (s *DocumentService) Upsert(ctx context.Context, input service.DocumentUpsertInput) (service.DocumentUpsertResult, error) {
|
||||
if input.DocumentID == "" {
|
||||
@@ -223,7 +236,11 @@ func (s *DocumentService) afterSyncDocumentUpsert(ctx context.Context, input ser
|
||||
}
|
||||
// do auto_parse if enabled
|
||||
if input.AutoParse {
|
||||
if err := s.StartParseDocuments(ctx, doc, &input.TaskContext.Knowledgebase, input.TaskContext.Connector.TenantID, StartParseOptions{RerunWithDelete: rerun}); err != nil {
|
||||
parseCtx, err := syncDocumentParseContext(ctx, input.TaskContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.StartParseDocuments(parseCtx, doc, &input.TaskContext.Knowledgebase, StartParseOptions{RerunWithDelete: rerun}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -264,7 +281,11 @@ func (s *DocumentService) ensureSyncDocumentPostWrite(ctx context.Context, input
|
||||
if task != nil {
|
||||
return nil
|
||||
}
|
||||
return s.StartParseDocuments(ctx, doc, &input.TaskContext.Knowledgebase, input.TaskContext.Connector.TenantID, StartParseOptions{})
|
||||
parseCtx, err := syncDocumentParseContext(ctx, input.TaskContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.StartParseDocuments(parseCtx, doc, &input.TaskContext.Knowledgebase, StartParseOptions{})
|
||||
}
|
||||
|
||||
// updateSyncDocumentFile updates the file-manager row linked to a synced document.
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"ragflow/internal/common"
|
||||
"ragflow/internal/dao"
|
||||
"ragflow/internal/entity"
|
||||
"ragflow/internal/permission"
|
||||
"ragflow/internal/service"
|
||||
"ragflow/internal/storage"
|
||||
syncerconnector "ragflow/internal/syncer/connector"
|
||||
@@ -47,7 +48,7 @@ func setupSyncDocumentTest(t *testing.T, existing bool) (*DocumentService, *gorm
|
||||
t.Cleanup(func() { factory.SetStorage(original) })
|
||||
svc := testDocumentService(t)
|
||||
input := service.DocumentUpsertInput{
|
||||
TaskContext: service.SyncTaskContext{Knowledgebase: *kb, Connector: entity.Connector{TenantID: "tenant-1"}},
|
||||
TaskContext: service.SyncTaskContext{Knowledgebase: *kb, Connector: entity.Connector{UserID: "user-1", TenantID: "tenant-1"}},
|
||||
SourceType: "github_connector-1",
|
||||
DocumentID: "doc-sync",
|
||||
SourceDocument: syncerconnector.SourceDocument{
|
||||
@@ -395,8 +396,78 @@ func TestSyncDocumentAcknowledgesParseEnqueueAfterCancellation(t *testing.T) {
|
||||
if len(publisher.messages) != 1 {
|
||||
t.Fatalf("parse publications = %d, want 1", len(publisher.messages))
|
||||
}
|
||||
task, err := svc.ingestionTaskDAO.GetByID(t.Context(), db, publisher.messages[0].TaskID)
|
||||
if err != nil {
|
||||
t.Fatalf("load enqueued ingestion task: %v", err)
|
||||
}
|
||||
if task.UserID != input.TaskContext.Connector.UserID {
|
||||
t.Fatalf("ingestion task user id = %q, want connector owner %q", task.UserID, input.TaskContext.Connector.UserID)
|
||||
}
|
||||
result, err = svc.Upsert(t.Context(), input)
|
||||
if err != nil || result.Action != service.DocumentActionSkipped || len(publisher.messages) != 1 {
|
||||
t.Fatalf("completed revision retried enqueue: result = %+v err = %v publications = %d", result, err, len(publisher.messages))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncDocumentAutoParseRejectsMissingConnectorOwner(t *testing.T) {
|
||||
svc, db, _, input := setupSyncDocumentTest(t, false)
|
||||
input.AutoParse = true
|
||||
input.TaskContext.Connector.UserID = ""
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
|
||||
if _, err := svc.Upsert(t.Context(), input); err == nil || !strings.Contains(err.Error(), "no owner user_id") {
|
||||
t.Fatalf("Upsert error = %v, want missing connector owner", err)
|
||||
}
|
||||
if len(publisher.messages) != 0 {
|
||||
t.Fatalf("parse publications = %d, want none", len(publisher.messages))
|
||||
}
|
||||
var taskCount int64
|
||||
if err := db.Model(&entity.IngestionTask{}).Where("document_id = ?", input.DocumentID).Count(&taskCount).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if taskCount != 0 {
|
||||
t.Fatalf("ingestion tasks = %d, want none", taskCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncDocumentReenqueuesParseWithConnectorOwner(t *testing.T) {
|
||||
svc, db, _, input := setupSyncDocumentTest(t, true)
|
||||
publisher := &recordingTaskPublisher{}
|
||||
svc.ingestionTaskSvc.SetTaskPublisher(publisher)
|
||||
|
||||
if _, err := svc.Upsert(t.Context(), input); err != nil {
|
||||
t.Fatalf("sync document before parse retry: %v", err)
|
||||
}
|
||||
input.AutoParse = true
|
||||
result, err := svc.Upsert(t.Context(), input)
|
||||
if err != nil || result.Action != service.DocumentActionSkipped {
|
||||
t.Fatalf("parse retry = %+v, err = %v", result, err)
|
||||
}
|
||||
if len(publisher.messages) != 1 {
|
||||
t.Fatalf("parse publications = %d, want 1", len(publisher.messages))
|
||||
}
|
||||
task, err := svc.ingestionTaskDAO.GetByID(t.Context(), db, publisher.messages[0].TaskID)
|
||||
if err != nil {
|
||||
t.Fatalf("load enqueued ingestion task: %v", err)
|
||||
}
|
||||
if task.UserID != input.TaskContext.Connector.UserID {
|
||||
t.Fatalf("ingestion task user id = %q, want connector owner %q", task.UserID, input.TaskContext.Connector.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncDocumentParseContextUsesConnectorIdentity(t *testing.T) {
|
||||
ctx := permission.WithIdentity(t.Context(), "request-user", "request-tenant")
|
||||
parseCtx, err := syncDocumentParseContext(ctx, service.SyncTaskContext{
|
||||
Connector: entity.Connector{ID: "connector-1", UserID: "connector-owner", TenantID: "connector-tenant"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := permission.UserIDFromContext(parseCtx); got != "connector-owner" {
|
||||
t.Fatalf("parse context user id = %q, want connector-owner", got)
|
||||
}
|
||||
if got := permission.TenantIDFromContext(parseCtx); got != "connector-tenant" {
|
||||
t.Fatalf("parse context tenant id = %q, want connector-tenant", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user