From f2f88736b6bf16b5dfc4a7d32b37a54d1e086790 Mon Sep 17 00:00:00 2001 From: nicornk Date: Mon, 27 Jul 2026 09:13:28 +0200 Subject: [PATCH 1/2] fix: add page_size and max_results to multipass search Adds page_size parameter to control results per page (default 1000) and max_results to cap total yielded results, preventing unbounded iteration on common-substring queries. Co-Authored-By: Claude Sonnet 4.6 --- .../foundry_dev_tools/clients/multipass.py | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/libs/foundry-dev-tools/src/foundry_dev_tools/clients/multipass.py b/libs/foundry-dev-tools/src/foundry_dev_tools/clients/multipass.py index 82f0991..2fdd5ad 100644 --- a/libs/foundry-dev-tools/src/foundry_dev_tools/clients/multipass.py +++ b/libs/foundry-dev-tools/src/foundry_dev_tools/clients/multipass.py @@ -143,7 +143,12 @@ def api_me_scope(self, **kwargs) -> requests.Response: ) def search( - self, query: str, principal_types: set[api_types.PrincipalTypes] | None = None, **kwargs + self, + query: str, + principal_types: set[api_types.PrincipalTypes] | None = None, + page_size: int = 1000, + max_results: int | None = None, + **kwargs, ) -> Iterator[dict]: """Searches for multipass principals based on a text. @@ -151,21 +156,37 @@ def search( query: the text string to search for principal_types: set of principal types to search in, e.g. "GROUP" Default is GROUP and USER + page_size: number of results per page, defaults to 1000 + max_results: stop after yielding this many results, or None for all. + A search token that is a common substring (e.g. a middle initial + like "M") can match thousands of users, causing unbounded iteration. **kwargs: gets passed to :py:meth:`APIClient.api_request` """ if principal_types is None: principal_types = {"USER", "GROUP"} - json = {"attributeFilters": {}, "principalTypes": list(principal_types), "query": query} + json = { + "attributeFilters": {}, + "principalTypes": list(principal_types), + "query": query, + "pageSize": page_size, + } + total = 0 next_start = None while True: if next_start: json["pageStart"] = next_start response_as_json = self.api_request("POST", "search/v2/search", json=json, **kwargs).json() - yield from response_as_json["values"] - if (next_start := response_as_json["nextPageToken"]) is None: + values = response_as_json["values"] + if max_results is not None: + values = values[: max_results - total] + yield from values + total += len(values) + if (next_start := response_as_json["nextPageToken"]) is None or ( + max_results is not None and total >= max_results + ): break def api_get_groups_of_user(self, **kwargs) -> requests.Response: From 8c656551e11614dea9d943c13e4d3f1439722784 Mon Sep 17 00:00:00 2001 From: nicornk Date: Mon, 27 Jul 2026 09:16:17 +0200 Subject: [PATCH 2/2] feat: release v2.1.25 Co-Authored-By: Claude Sonnet 4.6 --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 10b6f54..cf7fbb9 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog], and this project adheres to [Semantic Versioning]. +## [2.1.25] - 2026-07-27 + +## Fixed +- fix `MultipassClient.search` to support `page_size` and `max_results` parameters, preventing unbounded iteration on broad queries + ## [2.1.24] - 2026-05-20 ## Added