mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-17 21:27:23 +08:00
feat[Go]: implement POST /api/v1/files/link-to-datasets (#15674)
### What problem does this PR solve? Closes #15673 — ports the Python `file2document_api.py` `convert()` endpoint to Go. | Method | Path | Handler | |--------|------|---------| | POST | `/api/v1/files/link-to-datasets` | `FileHandler.LinkToDatasets` | ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- #### Implementation notes **Files changed:** ``` internal/service/file2document.go – new service (File2DocumentService) internal/dao/file2document.go – added Create method internal/handler/file.go – FileHandler gains file2DocumentService; LinkToDatasets HTTP handler internal/router/router.go – route registered ``` **Functional parity table:** | Concern | Go behaviour | |---------|-------------| | Required fields | `file_ids` and `kb_ids` both required; missing either → `CodeDataError` mirroring Python `@validate_request` | | File existence | `fileDAO.GetByIDs(fileIDs)` builds a set; any missing ID → `"File not found!"` | | KB existence | `kbDAO.GetByID(kbID)` per KB; missing → `"Can't find this dataset!"` | | Folder expansion | `getAllInnermostFileIDs` recursively calls `fileDAO.ListByParentID` — mirrors `FileService.get_all_innermost_file_ids` | | File permissions | `checkFileTeamPermission`: `file.TenantID == userID` OR user in tenant's team — mirrors `check_file_team_permission` | | KB permissions | `checkKBTeamPermission`: `kb.TenantID == userID` OR user in tenant's team — mirrors `check_kb_team_permission` | | Fire-and-forget | `go convertFiles(...)` goroutine after all validation passes — mirrors `loop.run_in_executor(None, _convert_files, …)` | | Conversion | `convertFiles`: for each file → delete existing mappings + hard-delete old documents → create new `Document` in each target KB → create `File2Document` mapping — mirrors Python `_convert_files` | | `getParser` | Extension-based lookup with fallback to `kb.ParserID` — mirrors `FileService.get_parser` | | Immediate return | `true` returned to caller as soon as goroutine is scheduled | --------- Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"ragflow/internal/common"
|
||||
@@ -32,15 +34,17 @@ import (
|
||||
|
||||
// FileHandler file handler
|
||||
type FileHandler struct {
|
||||
fileService *service.FileService
|
||||
userService *service.UserService
|
||||
fileService *service.FileService
|
||||
userService *service.UserService
|
||||
file2DocumentService *service.File2DocumentService
|
||||
}
|
||||
|
||||
// NewFileHandler create file handler
|
||||
func NewFileHandler(fileService *service.FileService, userService *service.UserService) *FileHandler {
|
||||
return &FileHandler{
|
||||
fileService: fileService,
|
||||
userService: userService,
|
||||
fileService: fileService,
|
||||
userService: userService,
|
||||
file2DocumentService: service.NewFile2DocumentService(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,3 +556,69 @@ func (h *FileHandler) Download(c *gin.Context) {
|
||||
// Send file data
|
||||
c.Data(http.StatusOK, contentType, blob)
|
||||
}
|
||||
|
||||
// LinkToDatasets links files (or folder trees) to one or more datasets.
|
||||
// Mirrors Python POST /api/v1/files/link-to-datasets (convert).
|
||||
// @Summary Link files to datasets
|
||||
// @Description Associate files with target knowledge-base datasets, re-indexing
|
||||
// as needed. Folder inputs are expanded to their innermost files.
|
||||
// The heavy DB work runs in a goroutine; the endpoint returns immediately.
|
||||
// @Tags file
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body service.LinkToDatasetsRequest true "file_ids and kb_ids"
|
||||
// @Success 200 {object} map[string]interface{}
|
||||
// @Router /api/v1/files/link-to-datasets [post]
|
||||
func (h *FileHandler) LinkToDatasets(c *gin.Context) {
|
||||
user, errorCode, errorMessage := GetUser(c)
|
||||
if errorCode != common.CodeSuccess {
|
||||
jsonError(c, errorCode, errorMessage)
|
||||
return
|
||||
}
|
||||
|
||||
var req service.LinkToDatasetsRequest
|
||||
// Tolerate bind errors: a malformed or empty body simply leaves the fields
|
||||
// empty, which the validate_request-style check below reports as missing
|
||||
// arguments — matching Python's @validate_request behaviour and code.
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
|
||||
// Mirror Python @validate_request("file_ids", "kb_ids"): missing arguments
|
||||
// return ARGUMENT_ERROR (101) with data=null and the aggregated message.
|
||||
var missing []string
|
||||
if len(req.FileIDs) == 0 {
|
||||
missing = append(missing, "file_ids")
|
||||
}
|
||||
if len(req.KbIDs) == 0 {
|
||||
missing = append(missing, "kb_ids")
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
jsonError(c, common.CodeArgumentError, fmt.Sprintf("required argument are missing: %s; ", strings.Join(missing, ",")))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.file2DocumentService.LinkToDatasets(user.ID, &req); err != nil {
|
||||
jsonError(c, linkToDatasetsErrorCode(err), err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": common.CodeSuccess,
|
||||
"data": true,
|
||||
"message": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// linkToDatasetsErrorCode maps File2DocumentService sentinel errors to
|
||||
// Python-compatible response codes. File/dataset-not-found and no-authorization
|
||||
// use DATA_ERROR (102), matching Python's get_data_error_result in convert();
|
||||
// any other (internal) error is reported as a server error.
|
||||
func linkToDatasetsErrorCode(err error) common.ErrorCode {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrLinkFileNotFound),
|
||||
errors.Is(err, service.ErrLinkDatasetNotFound),
|
||||
errors.Is(err, service.ErrLinkNoAuthorization):
|
||||
return common.CodeDataError
|
||||
default:
|
||||
return common.CodeServerError
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user