Skip to content

Commit 21df9c6

Browse files
committed
feat: adds the ability to set up google api tools with a custom discovery doc url
1 parent e95ff0d commit 21df9c6

4 files changed

Lines changed: 67 additions & 6 deletions

File tree

src/google/adk/tools/google_api_tool/google_api_toolset.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from ...agents.readonly_context import ReadonlyContext
2525
from ...auth.auth_credential import ServiceAccount
2626
from ...auth.auth_schemes import OpenIdConnectWithConfig
27-
from ...tools.base_tool import BaseTool
2827
from ...tools.base_toolset import BaseToolset
2928
from ...tools.base_toolset import ToolPredicate
3029
from ..openapi_tool import OpenAPIToolset
@@ -64,6 +63,7 @@ def __init__(
6463
*,
6564
additional_headers: Optional[Dict[str, str]] = None,
6665
additional_scopes: Optional[List[str]] = None,
66+
discovery_url: Optional[str] = None,
6767
):
6868
super().__init__(tool_filter=tool_filter, tool_name_prefix=tool_name_prefix)
6969
self.api_name = api_name
@@ -73,12 +73,13 @@ def __init__(
7373
self._service_account = service_account
7474
self._additional_headers = additional_headers
7575
self._additional_scopes = additional_scopes
76+
self._discovery_url = discovery_url
7677
self._openapi_toolset = self._load_toolset_with_oidc_auth()
7778

7879
@override
7980
async def get_tools(
8081
self, readonly_context: Optional[ReadonlyContext] = None
81-
) -> list[BaseTool]:
82+
) -> List[GoogleApiTool]:
8283
"""Get all tools in the toolset."""
8384
return [
8485
GoogleApiTool(
@@ -97,7 +98,7 @@ def set_tool_filter(self, tool_filter: Union[ToolPredicate, List[str]]):
9798

9899
def _load_toolset_with_oidc_auth(self) -> OpenAPIToolset:
99100
spec_dict = GoogleApiToOpenApiConverter(
100-
self.api_name, self.api_version
101+
self.api_name, self.api_version, discovery_url=self._discovery_url
101102
).convert()
102103
discovery_scopes = list(
103104
spec_dict['components']['securitySchemes']['oauth2']['flows'][

src/google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,19 @@
3232
class GoogleApiToOpenApiConverter:
3333
"""Converts Google API Discovery documents to OpenAPI v3 format."""
3434

35-
def __init__(self, api_name: str, api_version: str):
35+
def __init__(
36+
self, api_name: str, api_version: str, discovery_url: str | None = None
37+
):
3638
"""Initialize the converter with the API name and version.
3739
3840
Args:
3941
api_name: The name of the Google API (e.g., "calendar")
4042
api_version: The version of the API (e.g., "v3")
43+
discovery_url: Optional custom discovery document URL.
4144
"""
4245
self._api_name = api_name
4346
self._api_version = api_version
47+
self._discovery_url = discovery_url
4448
self._google_api_resource = None
4549
self._google_api_spec = None
4650
self._openapi_spec = {
@@ -60,7 +64,14 @@ def fetch_google_api_spec(self) -> None:
6064
self._api_version,
6165
)
6266
# Build a resource object for the specified API
63-
self._google_api_resource = build(self._api_name, self._api_version)
67+
if self._discovery_url:
68+
self._google_api_resource = build(
69+
self._api_name,
70+
self._api_version,
71+
discoveryServiceUrl=self._discovery_url,
72+
)
73+
else:
74+
self._google_api_resource = build(self._api_name, self._api_version)
6475

6576
# Access the underlying API discovery document
6677
self._google_api_spec = self._google_api_resource._rootDesc

tests/unittests/tools/google_api_tool/test_google_api_toolset.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def test_init(
146146
assert tool_set._additional_headers == additional_headers
147147

148148
mock_converter_class.assert_called_once_with(
149-
TEST_API_NAME, TEST_API_VERSION
149+
TEST_API_NAME, TEST_API_VERSION, discovery_url=None
150150
)
151151
mock_converter_instance.convert.assert_called_once()
152152
spec_dict = mock_converter_instance.convert.return_value
@@ -187,6 +187,34 @@ def test_init_with_additional_scopes(
187187
assert isinstance(kwargs["auth_scheme"], OpenIdConnectWithConfig)
188188
assert kwargs["auth_scheme"].scopes == [DEFAULT_SCOPE] + extra_scopes
189189

190+
@mock.patch(
191+
"google.adk.tools.google_api_tool.google_api_toolset.OpenAPIToolset"
192+
)
193+
@mock.patch(
194+
"google.adk.tools.google_api_tool.google_api_toolset.GoogleApiToOpenApiConverter"
195+
)
196+
def test_init_with_discovery_url(
197+
self,
198+
mock_converter_class,
199+
mock_openapi_toolset_class,
200+
mock_converter_instance,
201+
mock_openapi_toolset_instance,
202+
):
203+
"""Test GoogleApiToolset initialization with custom discovery URL."""
204+
mock_converter_class.return_value = mock_converter_instance
205+
mock_openapi_toolset_class.return_value = mock_openapi_toolset_instance
206+
207+
discovery_url = "https://example.com/discovery"
208+
tool_set = GoogleApiToolset(
209+
api_name=TEST_API_NAME,
210+
api_version=TEST_API_VERSION,
211+
discovery_url=discovery_url,
212+
)
213+
214+
mock_converter_class.assert_called_once_with(
215+
TEST_API_NAME, TEST_API_VERSION, discovery_url=discovery_url
216+
)
217+
190218
@mock.patch(
191219
"google.adk.tools.google_api_tool.google_api_toolset.GoogleApiTool"
192220
)

tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,27 @@ def test_fetch_google_api_spec(
261261
# Verify the results
262262
assert converter_with_patched_build._google_api_spec == calendar_api_spec
263263

264+
def test_fetch_google_api_spec_with_discovery_url(
265+
self, monkeypatch, mock_api_resource, calendar_api_spec
266+
):
267+
"""Test fetching Google API specification with custom discovery URL."""
268+
mock_build = MagicMock(return_value=mock_api_resource)
269+
monkeypatch.setattr(
270+
"google.adk.tools.google_api_tool.googleapi_to_openapi_converter.build",
271+
mock_build,
272+
)
273+
274+
discovery_url = "https://example.com/discovery"
275+
converter = GoogleApiToOpenApiConverter(
276+
"calendar", "v3", discovery_url=discovery_url
277+
)
278+
converter.fetch_google_api_spec()
279+
280+
assert converter._google_api_spec == calendar_api_spec
281+
mock_build.assert_called_once_with(
282+
"calendar", "v3", discoveryServiceUrl=discovery_url
283+
)
284+
264285
def test_fetch_google_api_spec_error(self, monkeypatch, converter):
265286
"""Test error handling when fetching Google API specification."""
266287
# Create a mock that raises an error

0 commit comments

Comments
 (0)