mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-06-29 23:41:12 +08:00
Refactor: API connectors (#14228)
### What problem does this PR solve? Refactor /api/v1/connectors to be more RESTful. ### Type of change - [x] Refactoring
This commit is contained in:
@@ -35,15 +35,30 @@ from rag.utils.redis_conn import REDIS_CONN
|
||||
from api.apps import login_required, current_user
|
||||
from box_sdk_gen import BoxOAuth, OAuthConfig, GetAuthorizeUrlOptions
|
||||
|
||||
|
||||
@manager.route("/set", methods=["POST"]) # noqa: F821
|
||||
@manager.route("/connectors/<connector_id>", methods=["PATCH"]) # noqa: F821
|
||||
@login_required
|
||||
async def set_connector():
|
||||
async def update_connector(connector_id):
|
||||
req = await get_request_json()
|
||||
if req.get("id"):
|
||||
e, conn = ConnectorService.get_by_id(connector_id)
|
||||
if not e:
|
||||
return get_data_error_result(message="Can't find this Connector!")
|
||||
|
||||
if req:
|
||||
conn = {fld: req[fld] for fld in ["prune_freq", "refresh_freq", "config", "timeout_secs"] if fld in req}
|
||||
ConnectorService.update_by_id(req["id"], conn)
|
||||
else:
|
||||
conn["id"] = connector_id
|
||||
ConnectorService.update_by_id(connector_id, conn)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
e, conn = ConnectorService.get_by_id(connector_id)
|
||||
|
||||
return get_json_result(data=conn.to_dict())
|
||||
|
||||
|
||||
@manager.route("/connectors", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
async def create_connector():
|
||||
req = await get_request_json()
|
||||
if req:
|
||||
req["id"] = get_uuid()
|
||||
conn = {
|
||||
"id": req["id"],
|
||||
@@ -65,13 +80,13 @@ async def set_connector():
|
||||
return get_json_result(data=conn.to_dict())
|
||||
|
||||
|
||||
@manager.route("/list", methods=["GET"]) # noqa: F821
|
||||
@manager.route("/connectors", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
def list_connector():
|
||||
return get_json_result(data=ConnectorService.list(current_user.id))
|
||||
|
||||
|
||||
@manager.route("/<connector_id>", methods=["GET"]) # noqa: F821
|
||||
@manager.route("/connectors/<connector_id>", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
def get_connector(connector_id):
|
||||
e, conn = ConnectorService.get_by_id(connector_id)
|
||||
@@ -80,7 +95,7 @@ def get_connector(connector_id):
|
||||
return get_json_result(data=conn.to_dict())
|
||||
|
||||
|
||||
@manager.route("/<connector_id>/logs", methods=["GET"]) # noqa: F821
|
||||
@manager.route("/connectors/<connector_id>/logs", methods=["GET"]) # noqa: F821
|
||||
@login_required
|
||||
def list_logs(connector_id):
|
||||
req = request.args.to_dict(flat=True)
|
||||
@@ -88,7 +103,7 @@ def list_logs(connector_id):
|
||||
return get_json_result(data={"total": total, "logs": arr})
|
||||
|
||||
|
||||
@manager.route("/<connector_id>/resume", methods=["PUT"]) # noqa: F821
|
||||
@manager.route("/connectors/<connector_id>/resume", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
async def resume(connector_id):
|
||||
req = await get_request_json()
|
||||
@@ -99,7 +114,7 @@ async def resume(connector_id):
|
||||
return get_json_result(data=True)
|
||||
|
||||
|
||||
@manager.route("/<connector_id>/rebuild", methods=["PUT"]) # noqa: F821
|
||||
@manager.route("/connectors/<connector_id>/rebuild", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
@validate_request("kb_id")
|
||||
async def rebuild(connector_id):
|
||||
@@ -110,7 +125,7 @@ async def rebuild(connector_id):
|
||||
return get_json_result(data=True)
|
||||
|
||||
|
||||
@manager.route("/<connector_id>/rm", methods=["POST"]) # noqa: F821
|
||||
@manager.route("/connectors/<connector_id>", methods=["DELETE"]) # noqa: F821
|
||||
@login_required
|
||||
def rm_connector(connector_id):
|
||||
ConnectorService.resume(connector_id, TaskStatus.CANCEL)
|
||||
@@ -185,7 +200,7 @@ async def _render_web_oauth_popup(flow_id: str, success: bool, message: str, sou
|
||||
return response
|
||||
|
||||
|
||||
@manager.route("/google/oauth/web/start", methods=["POST"]) # noqa: F821
|
||||
@manager.route("/connectors/google/oauth/web/start", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
@validate_request("credentials")
|
||||
async def start_google_web_oauth():
|
||||
@@ -265,7 +280,7 @@ async def start_google_web_oauth():
|
||||
)
|
||||
|
||||
|
||||
@manager.route("/gmail/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||
@manager.route("/connectors/gmail/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||
async def google_gmail_web_oauth_callback():
|
||||
state_id = request.args.get("state")
|
||||
error = request.args.get("error")
|
||||
@@ -316,7 +331,7 @@ async def google_gmail_web_oauth_callback():
|
||||
return await _render_web_oauth_popup(state_id, True, "Authorization completed successfully.", source)
|
||||
|
||||
|
||||
@manager.route("/google-drive/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||
@manager.route("/connectors/google-drive/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||
async def google_drive_web_oauth_callback():
|
||||
state_id = request.args.get("state")
|
||||
error = request.args.get("error")
|
||||
@@ -366,7 +381,7 @@ async def google_drive_web_oauth_callback():
|
||||
|
||||
return await _render_web_oauth_popup(state_id, True, "Authorization completed successfully.", source)
|
||||
|
||||
@manager.route("/google/oauth/web/result", methods=["POST"]) # noqa: F821
|
||||
@manager.route("/connectors/google/oauth/web/result", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
@validate_request("flow_id")
|
||||
async def poll_google_web_result():
|
||||
@@ -386,7 +401,7 @@ async def poll_google_web_result():
|
||||
REDIS_CONN.delete(_web_result_cache_key(flow_id, source))
|
||||
return get_json_result(data={"credentials": result.get("credentials")})
|
||||
|
||||
@manager.route("/box/oauth/web/start", methods=["POST"]) # noqa: F821
|
||||
@manager.route("/connectors/box/oauth/web/start", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
async def start_box_web_oauth():
|
||||
req = await get_request_json()
|
||||
@@ -429,7 +444,7 @@ async def start_box_web_oauth():
|
||||
"expires_in": WEB_FLOW_TTL_SECS,}
|
||||
)
|
||||
|
||||
@manager.route("/box/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||
@manager.route("/connectors/box/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||
async def box_web_oauth_callback():
|
||||
flow_id = request.args.get("state")
|
||||
if not flow_id:
|
||||
@@ -471,7 +486,7 @@ async def box_web_oauth_callback():
|
||||
|
||||
return await _render_web_oauth_popup(flow_id, True, "Authorization completed successfully.", "box")
|
||||
|
||||
@manager.route("/box/oauth/web/result", methods=["POST"]) # noqa: F821
|
||||
@manager.route("/connectors/box/oauth/web/result", methods=["POST"]) # noqa: F821
|
||||
@login_required
|
||||
@validate_request("flow_id")
|
||||
async def poll_box_web_result():
|
||||
@@ -44,7 +44,7 @@ You need to configure the OAuth Consent Screen because it is the step where you
|
||||
2. Select **Web Application** as **Application type** for the created project:
|
||||

|
||||
3. Enter a client name.
|
||||
4. Add `http://localhost:9380/v1/connector/google-drive/oauth/web/callback` as **Authorised redirect URIs**:
|
||||
4. Add `http://localhost:9380/api/v1/connectors/google-drive/oauth/web/callback` as **Authorised redirect URIs**:
|
||||
5. Add **Authorised JavaScript origins**:
|
||||
- If deploying RAGFlow from Docker, use `http://localhost:80`:
|
||||

|
||||
|
||||
@@ -20,7 +20,7 @@ import requests
|
||||
|
||||
from configs import HOST_ADDRESS, VERSION
|
||||
|
||||
CONNECTOR_BASE_URL = f"{HOST_ADDRESS}/{VERSION}/connector"
|
||||
CONNECTOR_BASE_URL = f"{HOST_ADDRESS}/api/{VERSION}/connectors"
|
||||
LLM_API_KEY_URL = f"{HOST_ADDRESS}/{VERSION}/llm/set_api_key"
|
||||
LANGFUSE_API_KEY_URL = f"{HOST_ADDRESS}/{VERSION}/langfuse/api_key"
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ def _load_connector_app(monkeypatch):
|
||||
box_mod.GetAuthorizeUrlOptions = _GetAuthorizeUrlOptions
|
||||
monkeypatch.setitem(sys.modules, "box_sdk_gen", box_mod)
|
||||
|
||||
module_path = repo_root / "api" / "apps" / "connector_app.py"
|
||||
module_path = repo_root / "api" / "apps" / "restful_apis" / "connector_api.py"
|
||||
spec = importlib.util.spec_from_file_location("test_connector_routes_unit", module_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
module.manager = _DummyManager()
|
||||
@@ -363,8 +363,8 @@ def test_connector_basic_routes_and_task_controls(monkeypatch):
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({"id": "conn-1", "refresh_freq": 7, "config": {"x": 1}}),
|
||||
)
|
||||
res = _run(module.set_connector())
|
||||
assert update_calls == [("conn-1", {"refresh_freq": 7, "config": {"x": 1}})]
|
||||
res = _run(module.update_connector("conn-1"))
|
||||
assert update_calls == [("conn-1", {'id': 'conn-1', "refresh_freq": 7, "config": {"x": 1}})]
|
||||
assert res["data"]["id"] == "conn-1"
|
||||
|
||||
monkeypatch.setattr(
|
||||
@@ -372,7 +372,7 @@ def test_connector_basic_routes_and_task_controls(monkeypatch):
|
||||
"get_request_json",
|
||||
lambda: _AwaitableValue({"name": "new", "source": "gmail", "config": {"y": 2}}),
|
||||
)
|
||||
res = _run(module.set_connector())
|
||||
res = _run(module.create_connector())
|
||||
assert save_calls[-1]["id"] == "generated-id"
|
||||
assert save_calls[-1]["tenant_id"] == "tenant-1"
|
||||
assert save_calls[-1]["input_type"] == module.InputType.POLL
|
||||
|
||||
@@ -144,7 +144,7 @@ const SourceDetailPage = () => {
|
||||
];
|
||||
}, [detail, runSchedule]);
|
||||
|
||||
const { addLoading, handleAddOk } = useAddDataSource();
|
||||
const { addLoading, handleAddOk } = useAddDataSource({isEdit:true});
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
formRef?.current?.submit();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useSetModalState } from '@/hooks/common-hooks';
|
||||
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
|
||||
import dataSourceService, {
|
||||
dataSourceRebuild,
|
||||
dataSourceResume,
|
||||
dataSourceResume, dataSourceUpdate,
|
||||
deleteDataSource,
|
||||
featchDataSourceDetail,
|
||||
getDataSourceLogs,
|
||||
@@ -68,7 +68,7 @@ export const useListDataSource = () => {
|
||||
return { list, categorizedList: updatedDataSourceTemplates, isFetching };
|
||||
};
|
||||
|
||||
export const useAddDataSource = () => {
|
||||
export const useAddDataSource = ({isEdit=false}:{isEdit?:boolean} ) => {
|
||||
const [addSource, setAddSource] = useState<IDataSorceInfo | undefined>(
|
||||
undefined,
|
||||
);
|
||||
@@ -90,7 +90,9 @@ export const useAddDataSource = () => {
|
||||
const handleAddOk = useCallback(
|
||||
async (data: any) => {
|
||||
setAddLoading(true);
|
||||
const { data: res } = await dataSourceService.dataSourceSet(data);
|
||||
const { data: res } = isEdit
|
||||
? await dataSourceUpdate(data.id, data)
|
||||
: await dataSourceService.dataSourceSet(data);
|
||||
console.log('🚀 ~ handleAddOk ~ code:', res.code);
|
||||
if (res.code === 0) {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source'] });
|
||||
|
||||
@@ -79,7 +79,7 @@ const DataSource = () => {
|
||||
handleAddOk,
|
||||
hideAddingModal,
|
||||
showAddingModal,
|
||||
} = useAddDataSource();
|
||||
} = useAddDataSource({});
|
||||
|
||||
return (
|
||||
<ProfileSettingWrapperCard
|
||||
|
||||
@@ -19,13 +19,17 @@ const dataSourceService = registerServer<keyof typeof methods>(
|
||||
);
|
||||
|
||||
export const deleteDataSource = (id: string) =>
|
||||
request.post(api.dataSourceDel(id));
|
||||
request.delete(api.dataSourceDel(id));
|
||||
export const dataSourceResume = (id: string, data: { resume: boolean }) => {
|
||||
return request.put(api.dataSourceResume(id), { data });
|
||||
return request.post(api.dataSourceResume(id), { data });
|
||||
};
|
||||
|
||||
export const dataSourceRebuild = (id: string, data: { kb_id: string }) => {
|
||||
return request.put(api.dataSourceRebuild(id), { data });
|
||||
return request.post(api.dataSourceRebuild(id), { data });
|
||||
};
|
||||
|
||||
export const dataSourceUpdate = (id: string, data: { kb_id: string }) => {
|
||||
return request.patch(api.dataSourceUpdate(id), { data });
|
||||
};
|
||||
|
||||
export const getDataSourceLogs = (id: string, params?: any) =>
|
||||
|
||||
@@ -35,19 +35,20 @@ export default {
|
||||
deleteFactory: `${webAPI}/llm/delete_factory`,
|
||||
|
||||
// data source
|
||||
dataSourceSet: `${webAPI}/connector/set`,
|
||||
dataSourceList: `${webAPI}/connector/list`,
|
||||
dataSourceDel: (id: string) => `${webAPI}/connector/${id}/rm`,
|
||||
dataSourceResume: (id: string) => `${webAPI}/connector/${id}/resume`,
|
||||
dataSourceRebuild: (id: string) => `${webAPI}/connector/${id}/rebuild`,
|
||||
dataSourceLogs: (id: string) => `${webAPI}/connector/${id}/logs`,
|
||||
dataSourceDetail: (id: string) => `${webAPI}/connector/${id}`,
|
||||
dataSourceUpdate: (id:string) => `${restAPIv1}/connectors/${id}`,
|
||||
dataSourceSet: `${restAPIv1}/connectors`,
|
||||
dataSourceList: `${restAPIv1}/connectors`,
|
||||
dataSourceDel: (id: string) => `${restAPIv1}/connectors/${id}`,
|
||||
dataSourceResume: (id: string) => `${restAPIv1}/connectors/${id}/resume`,
|
||||
dataSourceRebuild: (id: string) => `${restAPIv1}/connectors/${id}/rebuild`,
|
||||
dataSourceLogs: (id: string) => `${restAPIv1}/connectors/${id}/logs`,
|
||||
dataSourceDetail: (id: string) => `${restAPIv1}/connectors/${id}`,
|
||||
googleWebAuthStart: (type: 'google-drive' | 'gmail') =>
|
||||
`${webAPI}/connector/google/oauth/web/start?type=${type}`,
|
||||
`${restAPIv1}/connectors/google/oauth/web/start?type=${type}`,
|
||||
googleWebAuthResult: (type: 'google-drive' | 'gmail') =>
|
||||
`${webAPI}/connector/google/oauth/web/result?type=${type}`,
|
||||
boxWebAuthStart: () => `${webAPI}/connector/box/oauth/web/start`,
|
||||
boxWebAuthResult: () => `${webAPI}/connector/box/oauth/web/result`,
|
||||
`${restAPIv1}/connectors/google/oauth/web/result?type=${type}`,
|
||||
boxWebAuthStart: () => `${restAPIv1}/connectors/box/oauth/web/start`,
|
||||
boxWebAuthResult: () => `${restAPIv1}/connectors/box/oauth/web/result`,
|
||||
|
||||
// plugin
|
||||
llmTools: `${webAPI}/plugin/llm_tools`,
|
||||
|
||||
Reference in New Issue
Block a user