diff --git a/api/apps/restful_apis/document_api.py b/api/apps/restful_apis/document_api.py index e23e2c47a2..cf1b5341a8 100644 --- a/api/apps/restful_apis/document_api.py +++ b/api/apps/restful_apis/document_api.py @@ -811,15 +811,10 @@ def _get_docs_with_request(req, dataset_id:str): msg = f"Invalid filter conditions: {', '.join(invalid_types)} type{'s' if len(invalid_types) > 1 else ''}" return RetCode.DATA_ERROR, msg, [], 0 - # map run status (text or numeric) - align with API parameter - run_status = q.getlist("run") - run_status_text_to_numeric = {"UNSTART": "0", "RUNNING": "1", "CANCEL": "2", "DONE": "3", "FAIL": "4"} - run_status_converted = [run_status_text_to_numeric.get(v, v) for v in run_status] - if run_status_converted: - invalid_status = {s for s in run_status_converted if s not in run_status_text_to_numeric.values()} - if invalid_status: - msg = f"Invalid filter run status conditions: {', '.join(invalid_status)}" - return RetCode.DATA_ERROR, msg, [], 0 + run_status_converted, invalid_status = _parse_run_status_filter(q) + if invalid_status: + msg = f"Invalid filter run status conditions: {', '.join(invalid_status)}" + return RetCode.DATA_ERROR, msg, [], 0 err_code, err_message, doc_ids_filter, return_empty_metadata = _parse_doc_id_filter_with_metadata(q, dataset_id) if err_code != RetCode.SUCCESS: @@ -867,14 +862,10 @@ def _get_doc_filters_with_request(req, dataset_id: str): msg = f"Invalid filter conditions: {', '.join(invalid_types)} type{'s' if len(invalid_types) > 1 else ''}" return RetCode.DATA_ERROR, msg, {}, 0 - run_status = q.getlist("run") - run_status_text_to_numeric = {"UNSTART": "0", "RUNNING": "1", "CANCEL": "2", "DONE": "3", "FAIL": "4"} - run_status_converted = [run_status_text_to_numeric.get(v, v) for v in run_status] - if run_status_converted: - invalid_status = {s for s in run_status_converted if s not in run_status_text_to_numeric.values()} - if invalid_status: - msg = f"Invalid filter run status conditions: {', '.join(invalid_status)}" - return RetCode.DATA_ERROR, msg, {}, 0 + run_status_converted, invalid_status = _parse_run_status_filter(q) + if invalid_status: + msg = f"Invalid filter run status conditions: {', '.join(invalid_status)}" + return RetCode.DATA_ERROR, msg, {}, 0 docs_filter, total = DocumentService.get_filter_by_kb_id( dataset_id, @@ -885,6 +876,23 @@ def _get_doc_filters_with_request(req, dataset_id: str): ) return RetCode.SUCCESS, "", docs_filter, total + +def _get_query_values(req_args, *names): + values = [] + for name in names: + values.extend(req_args.getlist(name)) + values.extend(req_args.getlist(f"{name}[]")) + return [str(value).strip() for value in values if value is not None and str(value).strip()] + + +def _parse_run_status_filter(req_args): + raw_statuses = _get_query_values(req_args, "run", "run_status") + status_text_to_numeric = {status.name: status.value for status in TaskStatus} + valid_statuses = set(status_text_to_numeric.values()) + converted = [status_text_to_numeric.get(status.upper(), status) for status in raw_statuses] + invalid_statuses = {status for status in converted if status not in valid_statuses} + return converted, invalid_statuses + def _parse_doc_id_filter_with_metadata(req, kb_id): """Parse document ID filter based on metadata conditions from the request. diff --git a/test/testcases/test_web_api/test_document_app/test_list_documents.py b/test/testcases/test_web_api/test_document_app/test_list_documents.py index e4a9579a8a..37dc23d692 100644 --- a/test/testcases/test_web_api/test_document_app/test_list_documents.py +++ b/test/testcases/test_web_api/test_document_app/test_list_documents.py @@ -14,6 +14,7 @@ # limitations under the License. # from concurrent.futures import ThreadPoolExecutor, as_completed +from types import SimpleNamespace import pytest from test_common import list_documents @@ -217,3 +218,66 @@ class TestDocumentsList: assert res["code"] == 0 assert len(res["data"]["docs"]) == 0 + +class _DummyArgs(dict): + def getlist(self, key): + value = self.get(key, []) + if value is None: + return [] + if isinstance(value, list): + return value + return [value] + + +@pytest.mark.p2 +class TestDocumentsListUnit: + def test_run_status_query_param_filters_documents(self, document_app_module, monkeypatch): + captured = {} + + def fake_get_by_kb_id(kb_id, page, page_size, orderby, desc, keywords, run_status, types, suffix, **kwargs): + captured["run_status"] = run_status + return [], 0 + + monkeypatch.setattr(document_app_module.DocumentService, "get_by_kb_id", fake_get_by_kb_id) + + err_code, _, _, _ = document_app_module._get_docs_with_request( + SimpleNamespace(args=_DummyArgs({"run_status": "DONE"})), + "kb-1", + ) + + assert err_code == 0 + assert captured["run_status"] == ["3"] + + def test_empty_run_status_query_param_is_ignored(self, document_app_module, monkeypatch): + captured = {} + + def fake_get_by_kb_id(kb_id, page, page_size, orderby, desc, keywords, run_status, types, suffix, **kwargs): + captured["run_status"] = run_status + return [], 0 + + monkeypatch.setattr(document_app_module.DocumentService, "get_by_kb_id", fake_get_by_kb_id) + + err_code, _, _, _ = document_app_module._get_docs_with_request( + SimpleNamespace(args=_DummyArgs({"run_status": ""})), + "kb-1", + ) + + assert err_code == 0 + assert captured["run_status"] == [] + + def test_run_status_query_param_filters_aggregated_filters(self, document_app_module, monkeypatch): + captured = {} + + def fake_get_filter_by_kb_id(kb_id, keywords, run_status, types, suffix): + captured["run_status"] = run_status + return {}, 0 + + monkeypatch.setattr(document_app_module.DocumentService, "get_filter_by_kb_id", fake_get_filter_by_kb_id) + + err_code, _, _, _ = document_app_module._get_doc_filters_with_request( + SimpleNamespace(args=_DummyArgs({"run_status": "RUNNING"})), + "kb-1", + ) + + assert err_code == 0 + assert captured["run_status"] == ["1"]