Skip to content

fix(openapi): preserve falsy query parameter values#6286

Open
VectorPeak wants to merge 4 commits into
google:mainfrom
VectorPeak:fix
Open

fix(openapi): preserve falsy query parameter values#6286
VectorPeak wants to merge 4 commits into
google:mainfrom
VectorPeak:fix

Conversation

@VectorPeak

@VectorPeak VectorPeak commented Jul 3, 2026

Copy link
Copy Markdown

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

2. Or, if no issue exists, describe the change:

Problem / Solution:

What Problem This Solves

RestApiTool._prepare_request_params() previously used a truthiness check when collecting OpenAPI query parameters:

if v:

That treated "absent" and "provided but falsy" as the same thing. In Python, values such as False, 0, "", and None all fail a truthiness check, but they do not carry the same meaning when constructing query parameters.

For generated REST tools, False and 0 can be intentional caller-provided values. Common examples include boolean filters and pagination-style parameters such as:

includeInactive=false
page=0
limit=0

Before this change, those values were removed from request_params["params"] before request execution. That means APIs which distinguish omitted parameters from explicit false or 0 could apply defaults or different filtering/pagination behavior than the caller requested.

Changes

The query-parameter filter now excludes only values that represent absence under the existing behavior:

if v is not None and v != "":

This preserves explicit falsy-but-valid query values such as False and 0, while keeping the previous empty-string omission behavior unchanged.

This PR also includes a one-line local test stability fix in cli_deploy._validate_agent_import() that invalidates importlib caches before importing newly staged temporary agent packages. That was needed to make the repository full unit suite pass consistently in a clean Linux filesystem clone.\n\nThe OpenAPI query-parameter change is intentionally narrow:\n\n- only OpenAPI parameters with param_location == "query" are affected

  • this diff does not touch path, header, cookie, body, auth, default headers, embedded query-string merging, or request execution
  • no request serialization, transport, auth, or parser behavior is changed outside the query-parameter collection branch

Evidence

The regression test added in this PR exercises RestApiTool._prepare_request_params() directly and verifies the request-parameter dictionary before request execution:

  • include_inactive=False is preserved in request_params["params"]
  • page=0 is preserved in request_params["params"]
  • empty_param="" remains omitted from request_params["params"]

Together with the nearby existing query-parameter test, this keeps the prior empty-string omission behavior covered while adding explicit coverage for falsy-but-valid values.

Targeted local validation passed:

uv run --python 3.12 --extra test pytest tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py::TestRestApiTool::test_prepare_request_params_no_credential tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py::TestRestApiTool::test_prepare_request_params_preserves_falsy_query_parameter_values -p no:cacheprovider -q

Result:

2 passed, 4 warnings

Broader focused validation for the same test file also passed:

uv run --with pytest --with pytest-asyncio --with pytest-mock pytest tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py -q

Result:

48 passed, 21 warnings

Possible call chain / impact

An OpenAPI operation is parsed into ApiParameter entries, then a generated RestApiTool receives tool-call kwargs. During execution:

RestApiTool._run_async_impl()
  -> RestApiTool._prepare_request_params(api_params, api_args)
  -> request_params["params"]
  -> httpx.AsyncClient.request(..., params=request_params["params"])

Before this change, explicit query kwargs like:

{"include_inactive": False, "page": 0}

were filtered out before reaching the params dict. After this change, those values remain in request_params["params"], so the generated tool passes the caller-selected falsy values to the same httpx.AsyncClient.request(..., params=...) path used for other query parameters.

This PR does not claim new wire-level serialization behavior; it only changes which explicit query values are admitted into the existing params path.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

Passed locally:

  • uv run --with pytest --with pytest-asyncio --with pytest-mock pytest tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py -q
    • 48 passed, 21 warnings
  • uv run --python 3.12 --extra test pytest tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py::TestRestApiTool::test_prepare_request_params_no_credential tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py::TestRestApiTool::test_prepare_request_params_preserves_falsy_query_parameter_values -p no:cacheprovider -q
    • 2 passed, 4 warnings

Full unit suite validation:

  • uv run --python 3.12 --extra test --with pytest --with pytest-asyncio --with pytest-mock --with pytest-xdist pytest tests/unittests -q --tb=short
    • 7964 passed, 19 skipped, 31 xfailed, 9 xpassed, 1803 warnings, 6 subtests passed
    • Run in a clean WSL/Linux filesystem clone after adding importlib.invalidate_caches() for _validate_agent_import() test stability.

Additional checks:

  • uv run --with ruff==0.15.17 ruff check src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py
    • All checks passed!
  • git diff --check
    • passed

Manual End-to-End (E2E) Tests:

Performed a local manual smoke test that exercises the changed request path through RestApiTool.call() with an injected HTTP client. The smoke verified that:

  • include_inactive=False and page=0 are preserved in the outgoing params passed to client.request(...)
  • empty_param="" is still omitted from outgoing query params
  • _validate_agent_import() can validate consecutive newly-created temporary agent packages after refreshing importlib caches

Command:

uv run --with pytest --with pytest-asyncio --with pytest-mock python -

Result:

manual smoke passed: RestApiTool.call preserves False/0 query params and cli_deploy validates consecutive temp packages

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules. (N/A: no dependent downstream changes.)

Additional context

Before this change, False and 0 were both excluded from request_params["params"]. The new regression test verifies that:

  • include_inactive=False is preserved
  • page=0 is preserved
  • empty_param="" is still omitted

@google-cla

google-cla Bot commented Jul 3, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenAPI query parameters drop explicit false and zero values

1 participant