Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,15 @@ def _prepare_request_params(
# Move query params embedded in the path into query_params, since httpx
# replaces (rather than merges) the URL query string when `params` is set.
parsed_url = urlparse(url)
if parsed_url.query or parsed_url.fragment:
for key, values in parse_qs(parsed_url.query).items():
query_params.setdefault(key, values[0] if len(values) == 1 else values)
url = urlunparse(parsed_url._replace(query="", fragment=""))
for part in (parsed_url.query, parsed_url.fragment):
if part:
for key, values in parse_qs(part).items():
query_params.setdefault(
key,
values[0] if len(values) == 1 else values
)
# URL without query and fragment
url = urlunparse(parsed_url._replace(query="", fragment=""))

# Construct body
body_kwargs: Dict[str, Any] = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,47 @@ def test_prepare_request_params_plain_url_unchanged(
request_params = tool._prepare_request_params([], {})

assert request_params["url"] == "https://example.com/test"
def test_prepare_request_params_fragment_params_become_query_params(
self, sample_auth_credential, sample_auth_scheme
):
# When the ApplicationIntegrationToolset builds an endpoint URL, it sometimes
# puts params in the fragment (e.g. #triggerId=my_trigger). Without this fix
# those params were silently dropped and the API returned a 400 error.
# See: https://github.com/google/adk-python/issues/4598
integration_endpoint = OperationEndpoint(
base_url="https://integrations.googleapis.com",
path=(
"/v2/projects/demo/locations/us-central1"
"/integrations/MyFlow:execute"
"?triggerId=api_trigger/MyFlow"
"#httpMethod=POST"
),
method="POST",
)
op = Operation(operationId="run_integration")
tool = RestApiTool(
name="run_integration",
description="Runs a Google Cloud integration flow",
endpoint=integration_endpoint,
operation=op,
auth_credential=sample_auth_credential,
auth_scheme=sample_auth_scheme,
)

result = tool._prepare_request_params([], {})

# Both the query string and fragment params should land in query params
assert result["params"]["triggerId"] == "api_trigger/MyFlow"
assert result["params"]["httpMethod"] == "POST"

# The final URL should be clean — no leftover ? or #
assert "?" not in result["url"]
assert "#" not in result["url"]
assert result["url"] == (
"https://integrations.googleapis.com"
"/v2/projects/demo/locations/us-central1"
"/integrations/MyFlow:execute"
)


def test_snake_to_lower_camel():
Expand Down