From 32c541a9edd9a85394148962a6b70faa035b16c4 Mon Sep 17 00:00:00 2001 From: Hao Cheng Date: Tue, 15 Jul 2025 14:19:55 +0800 Subject: [PATCH] fix: generate deterministic operationId for root endpoints without one (#19888) --- api/core/tools/utils/parser.py | 3 +- .../unit_tests/core/tools/utils/__init__.py | 0 .../core/tools/utils/test_parser.py | 56 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 api/tests/unit_tests/core/tools/utils/__init__.py create mode 100644 api/tests/unit_tests/core/tools/utils/test_parser.py diff --git a/api/core/tools/utils/parser.py b/api/core/tools/utils/parser.py index 3f844e8234..a3c84615ca 100644 --- a/api/core/tools/utils/parser.py +++ b/api/core/tools/utils/parser.py @@ -1,5 +1,4 @@ import re -import uuid from json import dumps as json_dumps from json import loads as json_loads from json.decoder import JSONDecodeError @@ -154,7 +153,7 @@ class ApiBasedToolSchemaParser: # remove special characters like / to ensure the operation id is valid ^[a-zA-Z0-9_-]{1,64}$ path = re.sub(r"[^a-zA-Z0-9_-]", "", path) if not path: - path = str(uuid.uuid4()) + path = "" interface["operation"]["operationId"] = f"{path}_{interface['method']}" diff --git a/api/tests/unit_tests/core/tools/utils/__init__.py b/api/tests/unit_tests/core/tools/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/tests/unit_tests/core/tools/utils/test_parser.py b/api/tests/unit_tests/core/tools/utils/test_parser.py new file mode 100644 index 0000000000..8e07293ce0 --- /dev/null +++ b/api/tests/unit_tests/core/tools/utils/test_parser.py @@ -0,0 +1,56 @@ +import pytest +from flask import Flask + +from core.tools.utils.parser import ApiBasedToolSchemaParser + + +@pytest.fixture +def app(): + app = Flask(__name__) + return app + + +def test_parse_openapi_to_tool_bundle_operation_id(app): + openapi = { + "openapi": "3.0.0", + "info": {"title": "Simple API", "version": "1.0.0"}, + "servers": [{"url": "http://localhost:3000"}], + "paths": { + "/": { + "get": { + "summary": "Root endpoint", + "responses": { + "200": { + "description": "Successful response", + } + }, + } + }, + "/api/resources": { + "get": { + "summary": "Non-root endpoint without an operationId", + "responses": { + "200": { + "description": "Successful response", + } + }, + }, + "post": { + "summary": "Non-root endpoint with an operationId", + "operationId": "createResource", + "responses": { + "201": { + "description": "Resource created", + } + }, + }, + }, + }, + } + with app.test_request_context(): + tool_bundles = ApiBasedToolSchemaParser.parse_openapi_to_tool_bundle(openapi) + + assert len(tool_bundles) == 3 + assert tool_bundles[0].operation_id == "_get" + assert tool_bundles[1].operation_id == "apiresources_get" + assert tool_bundles[2].operation_id == "createResource"