Skip to content

Commit 5f829c1

Browse files
committed
fix(tools): add timeouts to HTTP requests in API client helpers
The requests.get/post calls in the API Hub and Application Integration tool clients omitted timeout=, so python-requests would wait forever on a stalled endpoint, hanging the tool invocation (and the agent). This also brought the clients in line with load_web_page.py, which already sets a default request timeout. Add a module-level _REQUEST_TIMEOUT_SECONDS constant (30s, matching load_web_page.py) to each client module and pass timeout= to every request call site. Extend the existing client tests to assert the mocked request receives timeout. Signed-off-by: agharsallah <17379925+agharsallah@users.noreply.github.com>
1 parent e6df097 commit 5f829c1

6 files changed

Lines changed: 35 additions & 6 deletions

File tree

src/google/adk/tools/apihub_tool/clients/apihub_client.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
from google.oauth2 import service_account
3333
import requests
3434

35+
# Default timeout in seconds for HTTP requests.
36+
_REQUEST_TIMEOUT_SECONDS = 30
37+
3538

3639
class BaseAPIHubClient(ABC):
3740
"""Base class for API Hub clients."""
@@ -133,7 +136,9 @@ def list_apis(self, project: str, location: str) -> List[Dict[str, Any]]:
133136
"accept": "application/json, text/plain, */*",
134137
"Authorization": f"Bearer {self._get_access_token()}",
135138
}
136-
response = requests.get(url, headers=headers)
139+
response = requests.get(
140+
url, headers=headers, timeout=_REQUEST_TIMEOUT_SECONDS
141+
)
137142
response.raise_for_status()
138143
apis = response.json().get("apis", [])
139144
return apis
@@ -153,7 +158,9 @@ def get_api(self, api_resource_name: str) -> Dict[str, Any]:
153158
"accept": "application/json, text/plain, */*",
154159
"Authorization": f"Bearer {self._get_access_token()}",
155160
}
156-
response = requests.get(url, headers=headers)
161+
response = requests.get(
162+
url, headers=headers, timeout=_REQUEST_TIMEOUT_SECONDS
163+
)
157164
response.raise_for_status()
158165
apis = response.json()
159166
return apis
@@ -173,7 +180,9 @@ def get_api_version(self, api_version_name: str) -> Dict[str, Any]:
173180
"accept": "application/json, text/plain, */*",
174181
"Authorization": f"Bearer {self._get_access_token()}",
175182
}
176-
response = requests.get(url, headers=headers)
183+
response = requests.get(
184+
url, headers=headers, timeout=_REQUEST_TIMEOUT_SECONDS
185+
)
177186
response.raise_for_status()
178187
return response.json()
179188

@@ -192,7 +201,9 @@ def _fetch_spec(self, api_spec_resource_name: str) -> str:
192201
"accept": "application/json, text/plain, */*",
193202
"Authorization": f"Bearer {self._get_access_token()}",
194203
}
195-
response = requests.get(url, headers=headers)
204+
response = requests.get(
205+
url, headers=headers, timeout=_REQUEST_TIMEOUT_SECONDS
206+
)
196207
response.raise_for_status()
197208
content_base64 = response.json().get("contents", "")
198209
if content_base64:

src/google/adk/tools/application_integration_tool/clients/connections_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
"integrations.mtls.googleapis.com"
3838
)
3939

40+
# Default timeout in seconds for HTTP requests.
41+
_REQUEST_TIMEOUT_SECONDS = 30
42+
4043

4144
class ConnectionsClient:
4245
"""Utility class for interacting with Google Cloud Connectors API."""
@@ -886,7 +889,9 @@ def _execute_api_call(self, url):
886889
"Authorization": f"Bearer {self._get_access_token()}",
887890
}
888891

889-
response = requests.get(url, headers=headers)
892+
response = requests.get(
893+
url, headers=headers, timeout=_REQUEST_TIMEOUT_SECONDS
894+
)
890895
response.raise_for_status()
891896
return response
892897

src/google/adk/tools/application_integration_tool/clients/integration_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
"{location}-integrations.mtls.googleapis.com"
3636
)
3737

38+
# Default timeout in seconds for HTTP requests.
39+
_REQUEST_TIMEOUT_SECONDS = 30
40+
3841

3942
class IntegrationClient:
4043
"""A client for interacting with Google Cloud Application Integration.
@@ -118,7 +121,9 @@ def get_openapi_spec_for_integration(self):
118121
],
119122
"fileFormat": "JSON",
120123
}
121-
response = requests.post(url, headers=headers, json=data)
124+
response = requests.post(
125+
url, headers=headers, json=data, timeout=_REQUEST_TIMEOUT_SECONDS
126+
)
122127
response.raise_for_status()
123128
spec = response.json().get("openApiSpec", {})
124129
return json.loads(spec)

tests/unittests/tools/apihub_tool/clients/test_apihub_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import base64
1616
import json
17+
from unittest.mock import ANY
1718
from unittest.mock import MagicMock
1819
from unittest.mock import patch
1920

@@ -74,6 +75,7 @@ def test_list_apis(self, mock_get, client):
7475
"accept": "application/json, text/plain, */*",
7576
"Authorization": "Bearer mocked_token",
7677
},
78+
timeout=ANY,
7779
)
7880

7981
@patch("requests.get")
@@ -105,6 +107,7 @@ def test_get_api(self, mock_get, client):
105107
"accept": "application/json, text/plain, */*",
106108
"Authorization": "Bearer mocked_token",
107109
},
110+
timeout=ANY,
108111
)
109112

110113
@patch("requests.get")
@@ -127,6 +130,7 @@ def test_get_api_version(self, mock_get, client):
127130
"accept": "application/json, text/plain, */*",
128131
"Authorization": "Bearer mocked_token",
129132
},
133+
timeout=ANY,
130134
)
131135

132136
@patch("requests.get")
@@ -151,6 +155,7 @@ def test_get_spec_content(self, mock_get, client):
151155
"accept": "application/json, text/plain, */*",
152156
"Authorization": "Bearer mocked_token",
153157
},
158+
timeout=ANY,
154159
)
155160

156161
@patch("requests.get")

tests/unittests/tools/application_integration_tool/clients/test_connections_client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def test_execute_api_call_success(
110110
"Content-Type": "application/json",
111111
"Authorization": f"Bearer {mock_credentials.token}",
112112
},
113+
timeout=mock.ANY,
113114
)
114115

115116
def test_execute_api_call_credential_error(

tests/unittests/tools/application_integration_tool/clients/test_integration_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def test_get_openapi_spec_for_integration_success(
162162
}],
163163
"fileFormat": "JSON",
164164
},
165+
timeout=mock.ANY,
165166
)
166167

167168
def test_get_openapi_spec_for_integration_success_mtls(
@@ -218,6 +219,7 @@ def test_get_openapi_spec_for_integration_success_mtls(
218219
}],
219220
"fileFormat": "JSON",
220221
},
222+
timeout=mock.ANY,
221223
)
222224

223225
def test_get_openapi_spec_for_integration_credential_error(

0 commit comments

Comments
 (0)