Skip to content

Commit 352468a

Browse files
committed
Add missing 'filter_roles' to Search index_list
This parameter was missing from SearchClient, although it was added last year. In terms of the client changes, this follows the patterns we have already established for a query parameter which is a comma-delimited list. The value type or an iterable thereof is acceptable, and our commajoin remarshalling helper ensures proper handling. The parameter is merged in with passthrough query params. The valid values (since it is a string) are declared as a Literal, which gives good typing-time assistance to developers/users but still allows any value at runtime. A new test for this usage is introduced, using a param class to define test parameters in a compact manner.
1 parent 202c07c commit 352468a

3 files changed

Lines changed: 78 additions & 2 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Added
2+
-----
3+
4+
- Added support for ``filter_roles`` as a parameter to ``SearchClient.index_list``. (:pr:`NUMBER`)

src/globus_sdk/services/search/client.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
from __future__ import annotations
22

33
import logging
4+
import sys
45
import typing as t
56
import uuid
67

78
from globus_sdk import client, paging, response
8-
from globus_sdk._internal.remarshal import strseq_listify
9+
from globus_sdk._internal.remarshal import commajoin, strseq_listify
910
from globus_sdk._missing import MISSING, MissingType
1011
from globus_sdk.scopes import SearchScopes
1112

1213
from .data import SearchQueryV1, SearchScrollQuery
1314
from .errors import SearchAPIError
1415
from .response import IndexListResponse
1516

17+
if sys.version_info >= (3, 10):
18+
from typing import TypeAlias
19+
else:
20+
from typing_extensions import TypeAlias
21+
1622
log = logging.getLogger(__name__)
1723

24+
_VALID_ROLE_NAMES_T: TypeAlias = t.Literal["owner", "admin", "writer"]
25+
1826

1927
class SearchClient(client.BaseClient):
2028
r"""
@@ -229,12 +237,18 @@ def get_index(
229237

230238
def index_list(
231239
self,
240+
filter_roles: (
241+
_VALID_ROLE_NAMES_T | t.Iterable[_VALID_ROLE_NAMES_T] | MissingType
242+
) = MISSING,
232243
*,
233244
query_params: dict[str, t.Any] | None = None,
234245
) -> response.IterableResponse:
235246
"""
236247
Get a list of indices on which the caller has permissions.
237248
249+
:param filter_roles: An iterable of roles to use to filter the listing. By
250+
default, all indices where the user has a role are returned.
251+
Valid values are ``owner``, ``admin``, and ``writer``.
238252
:param query_params: additional parameters to pass as query params
239253
240254
.. tab-set::
@@ -260,6 +274,7 @@ def index_list(
260274
:ref: search/reference/index_list/
261275
""" # noqa: E501
262276
log.debug("SearchClient.index_list()")
277+
query_params = {"filter_roles": commajoin(filter_roles), **(query_params or {})}
263278
return IndexListResponse(self.get("/v1/index_list", query_params=query_params))
264279

265280
#

tests/functional/services/search/test_index_list.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
from globus_sdk.testing import load_response
1+
import dataclasses
2+
import urllib.parse
3+
4+
import pytest
5+
6+
from globus_sdk._missing import MISSING
7+
from globus_sdk.testing import get_last_request, load_response
28

39

410
def test_search_index_list(client):
@@ -24,3 +30,54 @@ def test_search_index_list_is_iterable(client):
2430
index_list = list(res)
2531
assert len(index_list) == len(index_ids)
2632
assert [i["id"] for i in index_list] == index_ids
33+
34+
35+
_OMIT = object()
36+
37+
38+
@dataclasses.dataclass
39+
class FilterRoleParam:
40+
name: str
41+
value: object
42+
expect_parsed_param: object = None
43+
44+
@property
45+
def missing(self) -> bool:
46+
return self.value in (_OMIT, MISSING)
47+
48+
@property
49+
def call_kwargs(self) -> dict[str, object]:
50+
return {} if self.value is _OMIT else {"filter_roles": self.value}
51+
52+
def __str__(self) -> str:
53+
return self.name
54+
55+
56+
@pytest.mark.parametrize(
57+
"filter_param",
58+
[
59+
FilterRoleParam("omitted", _OMIT),
60+
FilterRoleParam("missing", MISSING),
61+
FilterRoleParam("ownerstr", "owner", ["owner"]),
62+
FilterRoleParam("adminstr", "admin", ["admin"]),
63+
FilterRoleParam("writerstr", "writer", ["writer"]),
64+
FilterRoleParam("tuple", ("owner", "admin"), ["owner,admin"]),
65+
FilterRoleParam("list", ["admin", "writer"], ["admin,writer"]),
66+
FilterRoleParam("duplicates", ("admin", "admin"), ["admin,admin"]),
67+
# this isn't a real role, but it should be passed through as though it were
68+
# -- this value can be a typing time error but never a runtime error
69+
FilterRoleParam("unknown_str", "ambassador", ["ambassador"]),
70+
],
71+
ids=str,
72+
)
73+
def test_search_index_list_encodes_filter_roles_as_expected(client, filter_param):
74+
load_response(client.index_list).metadata
75+
res = client.index_list(**filter_param.call_kwargs)
76+
assert res.http_status == 200
77+
78+
req = get_last_request()
79+
parsed_qs = urllib.parse.parse_qs(urllib.parse.urlparse(req.url).query)
80+
if filter_param.missing:
81+
assert "filter_roles" not in parsed_qs
82+
else:
83+
assert parsed_qs["filter_roles"] == filter_param.expect_parsed_param

0 commit comments

Comments
 (0)