mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-20 22:51:06 +08:00
Refa: files /file API to RESTFul style (#13741)
### What problem does this PR solve? Files /file API to RESTFul style. ### Type of change - [x] Documentation Update - [x] Refactoring --------- Co-authored-by: writinwaters <cai.keith@gmail.com> Co-authored-by: Liu An <asiro@qq.com>
This commit is contained in:
@@ -101,6 +101,20 @@ def document_app_module(monkeypatch):
|
||||
|
||||
deepdoc_html_module.RAGFlowHtmlParser = _StubHtmlParser
|
||||
monkeypatch.setitem(sys.modules, "deepdoc.parser.html_parser", deepdoc_html_module)
|
||||
deepdoc_mineru_module = ModuleType("deepdoc.parser.mineru_parser")
|
||||
|
||||
class _StubMinerUParser:
|
||||
pass
|
||||
|
||||
deepdoc_mineru_module.MinerUParser = _StubMinerUParser
|
||||
monkeypatch.setitem(sys.modules, "deepdoc.parser.mineru_parser", deepdoc_mineru_module)
|
||||
deepdoc_paddleocr_module = ModuleType("deepdoc.parser.paddleocr_parser")
|
||||
|
||||
class _StubPaddleOCRParser:
|
||||
pass
|
||||
|
||||
deepdoc_paddleocr_module.PaddleOCRParser = _StubPaddleOCRParser
|
||||
monkeypatch.setitem(sys.modules, "deepdoc.parser.paddleocr_parser", deepdoc_paddleocr_module)
|
||||
monkeypatch.setitem(sys.modules, "xgboost", ModuleType("xgboost"))
|
||||
|
||||
stub_apps = ModuleType("api.apps")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
|
||||
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -225,9 +225,10 @@ class _DummyFile:
|
||||
|
||||
|
||||
class _DummyRequest:
|
||||
def __init__(self, form=None, files=None):
|
||||
def __init__(self, form=None, files=None, args=None):
|
||||
self._form = form or {}
|
||||
self._files = files or _DummyFiles()
|
||||
self.args = args or {}
|
||||
|
||||
@property
|
||||
def form(self):
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
#
|
||||
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
import importlib.util
|
||||
import sys
|
||||
from types import ModuleType
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class _AwaitableValue:
|
||||
def __init__(self, value):
|
||||
self._value = value
|
||||
|
||||
def __await__(self):
|
||||
async def _co():
|
||||
return self._value
|
||||
|
||||
return _co().__await__()
|
||||
|
||||
|
||||
class _DummyFiles(dict):
|
||||
def getlist(self, key):
|
||||
value = self.get(key, [])
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return [value]
|
||||
|
||||
|
||||
class _DummyFile:
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
|
||||
class _DummyRequest:
|
||||
def __init__(self, *, files=None, args=None):
|
||||
self._files = files or _DummyFiles()
|
||||
self.args = args or {}
|
||||
|
||||
@property
|
||||
def files(self):
|
||||
return _AwaitableValue(self._files)
|
||||
|
||||
|
||||
def _run(coro):
|
||||
return asyncio.run(coro)
|
||||
|
||||
|
||||
def _load_document_app_module(monkeypatch):
|
||||
repo_root = Path(__file__).resolve().parents[4]
|
||||
common_mod = ModuleType("common")
|
||||
common_mod.bulk_upload_documents = lambda *_args, **_kwargs: []
|
||||
common_mod.delete_document = lambda *_args, **_kwargs: None
|
||||
common_mod.list_documents = lambda *_args, **_kwargs: {"data": {"docs": []}}
|
||||
monkeypatch.setitem(sys.modules, "common", common_mod)
|
||||
module_path = repo_root / "test" / "testcases" / "test_web_api" / "test_document_app" / "conftest.py"
|
||||
spec = importlib.util.spec_from_file_location("test_document_app_unit_conftest", module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules["test_document_app_unit_conftest"] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module.document_app_module.__wrapped__(monkeypatch)
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_upload_info_rejects_mixed_inputs(monkeypatch):
|
||||
module = _load_document_app_module(monkeypatch)
|
||||
files = _DummyFiles({"file": [_DummyFile("a.txt")]})
|
||||
monkeypatch.setattr(module, "request", _DummyRequest(files=files, args={"url": "https://example.com/a.txt"}))
|
||||
|
||||
res = _run(module.upload_info())
|
||||
assert res["code"] == module.RetCode.BAD_REQUEST
|
||||
assert "not both" in res["message"]
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_upload_info_requires_file_or_url(monkeypatch):
|
||||
module = _load_document_app_module(monkeypatch)
|
||||
monkeypatch.setattr(module, "request", _DummyRequest(files=_DummyFiles()))
|
||||
|
||||
res = _run(module.upload_info())
|
||||
assert res["code"] == module.RetCode.BAD_REQUEST
|
||||
assert "Missing input" in res["message"]
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
def test_upload_info_supports_url_single_and_multiple_files(monkeypatch):
|
||||
module = _load_document_app_module(monkeypatch)
|
||||
captured = []
|
||||
|
||||
def fake_upload_info(user_id, file_obj, url=None):
|
||||
captured.append((user_id, getattr(file_obj, "filename", None), url))
|
||||
if url is not None:
|
||||
return {"kind": "url", "value": url}
|
||||
return {"kind": "file", "value": file_obj.filename}
|
||||
|
||||
monkeypatch.setattr(module.FileService, "upload_info", fake_upload_info)
|
||||
|
||||
monkeypatch.setattr(module, "request", _DummyRequest(files=_DummyFiles(), args={"url": "https://example.com/a.txt"}))
|
||||
res = _run(module.upload_info())
|
||||
assert res["code"] == 0
|
||||
assert res["data"] == {"kind": "url", "value": "https://example.com/a.txt"}
|
||||
|
||||
monkeypatch.setattr(module, "request", _DummyRequest(files=_DummyFiles({"file": _DummyFile("single.txt")})))
|
||||
res = _run(module.upload_info())
|
||||
assert res["code"] == 0
|
||||
assert res["data"] == {"kind": "file", "value": "single.txt"}
|
||||
|
||||
monkeypatch.setattr(
|
||||
module,
|
||||
"request",
|
||||
_DummyRequest(files=_DummyFiles({"file": [_DummyFile("a.txt"), _DummyFile("b.txt")]})),
|
||||
)
|
||||
res = _run(module.upload_info())
|
||||
assert res["code"] == 0
|
||||
assert res["data"] == [
|
||||
{"kind": "file", "value": "a.txt"},
|
||||
{"kind": "file", "value": "b.txt"},
|
||||
]
|
||||
assert captured == [
|
||||
("user-1", None, "https://example.com/a.txt"),
|
||||
("user-1", "single.txt", None),
|
||||
("user-1", "a.txt", None),
|
||||
("user-1", "b.txt", None),
|
||||
]
|
||||
@@ -143,6 +143,10 @@ def _load_file2document_module(monkeypatch):
|
||||
def get_by_id(_file_id):
|
||||
return True, _DummyFile(_file_id, _FileType.DOC.value)
|
||||
|
||||
@staticmethod
|
||||
def get_parser(_file_type, _file_name, parser_id):
|
||||
return parser_id
|
||||
|
||||
file_service_mod.FileService = _StubFileService
|
||||
monkeypatch.setitem(sys.modules, "api.db.services.file_service", file_service_mod)
|
||||
services_pkg.file_service = file_service_mod
|
||||
@@ -284,7 +288,14 @@ def test_convert_branch_matrix_unit(monkeypatch):
|
||||
"get_by_id",
|
||||
lambda _file_id: (True, _DummyFile("inner-1", module.FileType.DOC.value, name="inner.txt", location="inner.loc", size=2)),
|
||||
)
|
||||
monkeypatch.setattr(module.DocumentService, "insert", lambda _payload: SimpleNamespace(id="doc-new"))
|
||||
inserted = {}
|
||||
|
||||
def _insert(payload):
|
||||
inserted.update(payload)
|
||||
return SimpleNamespace(id="doc-new")
|
||||
|
||||
monkeypatch.setattr(module.DocumentService, "insert", _insert)
|
||||
monkeypatch.setattr(module.FileService, "get_parser", lambda _ft, _name, _parser_id: "picked-parser")
|
||||
monkeypatch.setattr(
|
||||
module.File2DocumentService,
|
||||
"insert",
|
||||
@@ -293,6 +304,8 @@ def test_convert_branch_matrix_unit(monkeypatch):
|
||||
res = _run(module.convert())
|
||||
assert res["code"] == 0
|
||||
assert res["data"] == [{"file_id": "inner-1", "document_id": "doc-new"}]
|
||||
assert inserted["parser_id"] == "picked-parser"
|
||||
assert inserted["pipeline_id"] == "p1"
|
||||
|
||||
req_state["file_ids"] = ["f1"]
|
||||
monkeypatch.setattr(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user