Remove unused API (#14046)

### What problem does this PR solve?

1. Remove unused token related API
2. Fix typo

### Type of change

- [x] Refactoring

---------

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
Jin Hai
2026-04-14 19:32:16 +08:00
committed by GitHub
parent 912fedc9b9
commit 8e9cef3687
7 changed files with 7 additions and 422 deletions

View File

@@ -247,7 +247,7 @@ jobs:
echo "Waiting for service to be available... (last exit code: $?)"
sleep 5
done
source .venv/bin/activate && set -o pipefail; DOC_ENGINE=infinity pytest -s --tb=short --level=${HTTP_API_TEST_LEVEL} test/testcases/test_web_api/test_api_app test/testcases/test_web_api/test_chunk_feedback 2>&1 | tee infinity_web_api_test.log
source .venv/bin/activate && set -o pipefail; DOC_ENGINE=infinity pytest -s --tb=short --level=${HTTP_API_TEST_LEVEL} test/testcases/test_web_api/test_chunk_feedback 2>&1 | tee infinity_web_api_test.log
- name: Run http api tests against Infinity
run: |

View File

@@ -15,73 +15,11 @@
#
from datetime import datetime, timedelta
from quart import request
from api.db.db_models import APIToken
from api.db.services.api_service import APITokenService, API4ConversationService
from api.db.services.api_service import API4ConversationService
from api.db.services.user_service import UserTenantService
from api.utils.api_utils import generate_confirmation_token, get_data_error_result, get_json_result, get_request_json, server_error_response, validate_request
from common.time_utils import current_timestamp, datetime_format
from api.utils.api_utils import get_data_error_result, get_json_result, server_error_response
from api.apps import login_required, current_user
@manager.route('/new_token', methods=['POST']) # noqa: F821
@login_required
async def new_token():
req = await get_request_json()
try:
tenants = UserTenantService.query(user_id=current_user.id)
if not tenants:
return get_data_error_result(message="Tenant not found!")
tenant_id = tenants[0].tenant_id
obj = {"tenant_id": tenant_id, "token": generate_confirmation_token(),
"create_time": current_timestamp(),
"create_date": datetime_format(datetime.now()),
"update_time": None,
"update_date": None
}
if req.get("canvas_id"):
obj["dialog_id"] = req["canvas_id"]
obj["source"] = "agent"
else:
obj["dialog_id"] = req["dialog_id"]
if not APITokenService.save(**obj):
return get_data_error_result(message="Fail to new a dialog!")
return get_json_result(data=obj)
except Exception as e:
return server_error_response(e)
@manager.route('/token_list', methods=['GET']) # noqa: F821
@login_required
def token_list():
try:
tenants = UserTenantService.query(user_id=current_user.id)
if not tenants:
return get_data_error_result(message="Tenant not found!")
id = request.args["dialog_id"] if "dialog_id" in request.args else request.args["canvas_id"]
objs = APITokenService.query(tenant_id=tenants[0].tenant_id, dialog_id=id)
return get_json_result(data=[o.to_dict() for o in objs])
except Exception as e:
return server_error_response(e)
@manager.route('/rm', methods=['POST']) # noqa: F821
@validate_request("tokens", "tenant_id")
@login_required
async def rm():
req = await get_request_json()
try:
for token in req["tokens"]:
APITokenService.filter_delete(
[APIToken.tenant_id == req["tenant_id"], APIToken.token == token])
return get_json_result(data=True)
except Exception as e:
return server_error_response(e)
@manager.route('/stats', methods=['GET']) # noqa: F821
@login_required
def stats():

View File

@@ -1,87 +0,0 @@
#
# Copyright 2025 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 pytest
from test_common import api_new_token, api_rm_token, api_stats, api_token_list, batch_create_chats
from configs import INVALID_API_TOKEN
from libs.auth import RAGFlowWebApiAuth
INVALID_AUTH_CASES = [
(None, 401, "Unauthorized"),
(RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, "Unauthorized"),
]
class TestAuthorization:
@pytest.mark.p2
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
def test_auth_invalid_new_token(self, invalid_auth, expected_code, expected_fragment):
res = api_new_token(invalid_auth, {"dialog_id": "dummy_dialog_id"})
assert res["code"] == expected_code, res
assert expected_fragment in res["message"], res
@pytest.mark.p2
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
def test_auth_invalid_token_list(self, invalid_auth, expected_code, expected_fragment):
res = api_token_list(invalid_auth, {"dialog_id": "dummy_dialog_id"})
assert res["code"] == expected_code, res
assert expected_fragment in res["message"], res
@pytest.mark.p2
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
def test_auth_invalid_rm(self, invalid_auth, expected_code, expected_fragment):
res = api_rm_token(invalid_auth, {"tokens": ["dummy_token"], "tenant_id": "dummy_tenant"})
assert res["code"] == expected_code, res
assert expected_fragment in res["message"], res
@pytest.mark.p2
@pytest.mark.parametrize("invalid_auth, expected_code, expected_fragment", INVALID_AUTH_CASES)
def test_auth_invalid_stats(self, invalid_auth, expected_code, expected_fragment):
res = api_stats(invalid_auth)
assert res["code"] == expected_code, res
assert expected_fragment in res["message"], res
@pytest.mark.usefixtures("clear_chats")
class TestApiTokens:
@pytest.mark.p2
def test_token_lifecycle(self, WebApiAuth):
chat_id = batch_create_chats(WebApiAuth, 1)[0]
create_res = api_new_token(WebApiAuth, {"dialog_id": chat_id})
assert create_res["code"] == 0, create_res
token = create_res["data"]["token"]
tenant_id = create_res["data"]["tenant_id"]
list_res = api_token_list(WebApiAuth, {"dialog_id": chat_id})
assert list_res["code"] == 0, list_res
assert any(item["token"] == token for item in list_res["data"]), list_res
rm_res = api_rm_token(WebApiAuth, {"tokens": [token], "tenant_id": tenant_id})
assert rm_res["code"] == 0, rm_res
assert rm_res["data"] is True, rm_res
@pytest.mark.p2
def test_stats_basic(self, WebApiAuth):
res = api_stats(WebApiAuth)
assert res["code"] == 0, res
for key in ["pv", "uv", "speed", "tokens", "round", "thumb_up"]:
assert key in res["data"], res
@pytest.mark.p3
def test_rm_missing_tokens(self, WebApiAuth):
res = api_rm_token(WebApiAuth, {"tenant_id": "dummy_tenant"})
assert res["code"] == 101, res
assert "required argument are missing" in res["message"], res

View File

@@ -1,247 +0,0 @@
#
# 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
import importlib.util
import sys
from pathlib import Path
from types import ModuleType, SimpleNamespace
import pytest
class _DummyManager:
def route(self, *_args, **_kwargs):
def decorator(func):
return func
return decorator
class _ExprField:
def __init__(self, name):
self.name = name
def __eq__(self, other):
return (self.name, other)
class _DummyAPITokenModel:
tenant_id = _ExprField("tenant_id")
token = _ExprField("token")
def _run(coro):
return asyncio.run(coro)
def _load_api_app(monkeypatch):
repo_root = Path(__file__).resolve().parents[4]
quart_mod = ModuleType("quart")
quart_mod.request = SimpleNamespace(args={})
monkeypatch.setitem(sys.modules, "quart", quart_mod)
apps_mod = ModuleType("api.apps")
apps_mod.__path__ = [str(repo_root / "api" / "apps")]
apps_mod.login_required = lambda fn: fn
apps_mod.current_user = SimpleNamespace(id="user-1")
monkeypatch.setitem(sys.modules, "api.apps", apps_mod)
api_utils_mod = ModuleType("api.utils.api_utils")
async def _get_request_json():
return {}
api_utils_mod.generate_confirmation_token = lambda: "token-123"
api_utils_mod.get_request_json = _get_request_json
api_utils_mod.get_json_result = lambda data=None, message="", code=0: {
"code": code,
"message": message,
"data": data,
}
api_utils_mod.get_data_error_result = lambda message="", code=400, data=None: {
"code": code,
"message": message,
"data": data,
}
api_utils_mod.server_error_response = lambda exc: {
"code": 500,
"message": str(exc),
"data": None,
}
api_utils_mod.validate_request = lambda *_args, **_kwargs: (lambda fn: fn)
monkeypatch.setitem(sys.modules, "api.utils.api_utils", api_utils_mod)
api_service_mod = ModuleType("api.db.services.api_service")
class _StubAPITokenService:
@staticmethod
def save(**_kwargs):
return True
@staticmethod
def query(**_kwargs):
return []
@staticmethod
def filter_delete(_conds):
return True
class _StubAPI4ConversationService:
@staticmethod
def stats(*_args, **_kwargs):
return []
api_service_mod.APITokenService = _StubAPITokenService
api_service_mod.API4ConversationService = _StubAPI4ConversationService
monkeypatch.setitem(sys.modules, "api.db.services.api_service", api_service_mod)
user_service_mod = ModuleType("api.db.services.user_service")
class _StubUserTenantService:
@staticmethod
def query(**_kwargs):
return [SimpleNamespace(tenant_id="tenant-1")]
user_service_mod.UserTenantService = _StubUserTenantService
monkeypatch.setitem(sys.modules, "api.db.services.user_service", user_service_mod)
db_models_mod = ModuleType("api.db.db_models")
db_models_mod.APIToken = _DummyAPITokenModel
monkeypatch.setitem(sys.modules, "api.db.db_models", db_models_mod)
time_utils_mod = ModuleType("common.time_utils")
time_utils_mod.current_timestamp = lambda: 123
time_utils_mod.datetime_format = lambda _dt: "2026-01-01 00:00:00"
monkeypatch.setitem(sys.modules, "common.time_utils", time_utils_mod)
module_path = repo_root / "api" / "apps" / "api_app.py"
spec = importlib.util.spec_from_file_location("test_api_tokens_unit_module", module_path)
module = importlib.util.module_from_spec(spec)
module.manager = _DummyManager()
spec.loader.exec_module(module)
return module
@pytest.mark.p2
def test_new_token_branches_and_error_paths(monkeypatch):
module = _load_api_app(monkeypatch)
async def req_canvas():
return {"canvas_id": "canvas-1"}
monkeypatch.setattr(module, "get_request_json", req_canvas)
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: [])
res = _run(module.new_token())
assert res["message"] == "Tenant not found!"
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: [SimpleNamespace(tenant_id="tenant-1")])
monkeypatch.setattr(module.APITokenService, "save", lambda **_kwargs: True)
res = _run(module.new_token())
assert res["code"] == 0
assert res["data"]["tenant_id"] == "tenant-1"
assert res["data"]["dialog_id"] == "canvas-1"
assert res["data"]["source"] == "agent"
monkeypatch.setattr(module.APITokenService, "save", lambda **_kwargs: False)
res = _run(module.new_token())
assert res["message"] == "Fail to new a dialog!"
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: (_ for _ in ()).throw(RuntimeError("query failed")))
res = _run(module.new_token())
assert res["code"] == 500
assert "query failed" in res["message"]
@pytest.mark.p2
def test_token_list_tenant_guard_and_exception(monkeypatch):
module = _load_api_app(monkeypatch)
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: [])
monkeypatch.setattr(module, "request", SimpleNamespace(args={"dialog_id": "d1"}))
res = module.token_list()
assert res["message"] == "Tenant not found!"
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: [SimpleNamespace(tenant_id="tenant-1")])
monkeypatch.setattr(module, "request", SimpleNamespace(args={}))
res = module.token_list()
assert res["code"] == 500
assert "canvas_id" in res["message"]
@pytest.mark.p2
def test_rm_exception_path(monkeypatch):
module = _load_api_app(monkeypatch)
async def req_rm():
return {"tokens": ["tok-1"], "tenant_id": "tenant-1"}
monkeypatch.setattr(module, "get_request_json", req_rm)
monkeypatch.setattr(
module.APITokenService,
"filter_delete",
lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("delete failed")),
)
res = _run(module.rm())
assert res["code"] == 500
assert "delete failed" in res["message"]
@pytest.mark.p2
def test_stats_aggregation_and_error_paths(monkeypatch):
module = _load_api_app(monkeypatch)
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: [])
monkeypatch.setattr(module, "request", SimpleNamespace(args={}))
res = module.stats()
assert res["message"] == "Tenant not found!"
monkeypatch.setattr(module.UserTenantService, "query", lambda **_kwargs: [SimpleNamespace(tenant_id="tenant-1")])
monkeypatch.setattr(module, "request", SimpleNamespace(args={"canvas_id": "canvas-1"}))
monkeypatch.setattr(
module.API4ConversationService,
"stats",
lambda *_args, **_kwargs: [
{
"dt": "2026-01-01",
"pv": 3,
"uv": 2,
"tokens": 100,
"duration": 9.9,
"round": 1,
"thumb_up": 0,
}
],
)
res = module.stats()
assert res["code"] == 0
assert res["data"]["pv"] == [("2026-01-01", 3)]
assert res["data"]["uv"] == [("2026-01-01", 2)]
assert res["data"]["round"] == [("2026-01-01", 1)]
assert res["data"]["thumb_up"] == [("2026-01-01", 0)]
assert res["data"]["tokens"] == [("2026-01-01", 0.1)]
assert res["data"]["speed"] == [("2026-01-01", 10.0)]
monkeypatch.setattr(
module.API4ConversationService,
"stats",
lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("stats failed")),
)
res = module.stats()
assert res["code"] == 500
assert "stats failed" in res["message"]

View File

@@ -68,25 +68,6 @@ def _log_http_debug(method, url, req_id, payload, status, text, resp_json, elaps
print(f"[HTTP DEBUG] response_text={text}")
print(f"[HTTP DEBUG] response_json={json.dumps(resp_json, default=str) if resp_json is not None else None}")
# API APP
def api_new_token(auth, payload=None, *, headers=HEADERS, data=None):
if payload is None:
payload = {}
res = requests.post(url=f"{HOST_ADDRESS}{API_APP_URL}/new_token", headers=headers, auth=auth, json=payload, data=data)
return res.json()
def api_token_list(auth, params=None, *, headers=HEADERS):
res = requests.get(url=f"{HOST_ADDRESS}{API_APP_URL}/token_list", headers=headers, auth=auth, params=params)
return res.json()
def api_rm_token(auth, payload=None, *, headers=HEADERS, data=None):
res = requests.post(url=f"{HOST_ADDRESS}{API_APP_URL}/rm", headers=headers, auth=auth, json=payload, data=data)
return res.json()
def api_stats(auth, params=None, *, headers=HEADERS):
res = requests.get(url=f"{HOST_ADDRESS}{API_APP_URL}/stats", headers=headers, auth=auth, params=params)
return res.json()

View File

@@ -13,7 +13,7 @@ import { useParams, useSearchParams } from 'react-router';
import { LogTabs } from './dataset-common';
import { IFileLogList, IOverviewTotal } from './interface';
const useFetchOverviewTital = () => {
const useFetchOverviewTotal = () => {
const [searchParams] = useSearchParams();
const { id } = useParams();
const knowledgeBaseId = searchParams.get('id') || id;
@@ -95,4 +95,4 @@ const useFetchFileLogList = () => {
};
};
export { useFetchFileLogList, useFetchOverviewTital };
export { useFetchFileLogList, useFetchOverviewTotal };

View File

@@ -17,7 +17,7 @@ import { useTranslation } from 'react-i18next';
import { RunningStatus } from '../dataset/constant';
import { LogTabs } from './dataset-common';
import { DatasetFilter } from './dataset-filter';
import { useFetchFileLogList, useFetchOverviewTital } from './hook';
import { useFetchFileLogList, useFetchOverviewTotal } from './hook';
import { DocumentLog, IFileLogItem } from './interface';
import FileLogsTable from './overview-table';
@@ -133,7 +133,7 @@ const FileLogsPage: FC = () => {
failed: 0,
},
});
const { data: topData } = useFetchOverviewTital();
const { data: topData } = useFetchOverviewTotal();
const {
pagination: { total: fileTotal },
} = useFetchDocumentList(false);