Skip to content

Commit 6febd22

Browse files
tw4likreymer
andauthored
Add profile /search-values endpoint (#3015)
- Backend implementation for #3010 - Also add 'name' to profiles list filtering --------- Co-authored-by: Ilya Kreymer <ikreymer@gmail.com>
1 parent 8895c3e commit 6febd22

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

backend/btrixcloud/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,6 +2539,13 @@ class ProfileBrowserGetUrlResponse(BaseModel):
25392539
url: AnyHttpUrl
25402540

25412541

2542+
# ============================================================================
2543+
class ProfileSearchValuesResponse(BaseModel):
2544+
"""Response model for profiles search values"""
2545+
2546+
names: List[str]
2547+
2548+
25422549
# ============================================================================
25432550

25442551
### USERS ###

backend/btrixcloud/profiles.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
ProfileBrowserMetadata,
4747
TagsResponse,
4848
ListFilterType,
49+
ProfileSearchValuesResponse,
4950
)
5051
from .utils import dt_now, str_to_date
5152

@@ -444,6 +445,7 @@ async def list_profiles(
444445
userid: Optional[UUID] = None,
445446
tags: Optional[List[str]] = None,
446447
tag_match: Optional[ListFilterType] = ListFilterType.AND,
448+
name: Optional[str] = None,
447449
page_size: int = DEFAULT_PAGE_SIZE,
448450
page: int = 1,
449451
sort_by: str = "modified",
@@ -462,6 +464,8 @@ async def list_profiles(
462464
if tags:
463465
query_type = "$all" if tag_match == ListFilterType.AND else "$in"
464466
match_query["tags"] = {query_type: tags}
467+
if name:
468+
match_query["name"] = name
465469

466470
aggregate: List[Dict[str, Any]] = [{"$match": match_query}]
467471

@@ -651,6 +655,13 @@ async def get_profile_tag_counts(
651655
).to_list()
652656
return tags
653657

658+
async def get_profile_search_values(self, org: Organization):
659+
"""Return profile names for use in search"""
660+
names = await self.profiles.distinct("name", {"oid": org.id})
661+
# Remove empty strings
662+
names = [name for name in names if name]
663+
return {"names": names}
664+
654665
def _run_task(self, func) -> None:
655666
"""add bg tasks to set to avoid premature garbage collection"""
656667
task = asyncio.create_task(func)
@@ -715,6 +726,7 @@ async def list_profiles(
715726
description='Defaults to `"and"` if omitted',
716727
),
717728
] = ListFilterType.AND,
729+
name: Optional[str] = None,
718730
pageSize: int = DEFAULT_PAGE_SIZE,
719731
page: int = 1,
720732
sortBy: str = "modified",
@@ -725,6 +737,7 @@ async def list_profiles(
725737
userid,
726738
tags=tags,
727739
tag_match=tag_match,
740+
name=name,
728741
page_size=pageSize,
729742
page=page,
730743
sort_by=sortBy,
@@ -748,6 +761,15 @@ async def commit_browser_to_new(
748761
async def get_profile_tag_counts(org: Organization = Depends(org_viewer_dep)):
749762
return {"tags": await ops.get_profile_tag_counts(org)}
750763

764+
@router.get(
765+
"/search-values",
766+
response_model=ProfileSearchValuesResponse,
767+
)
768+
async def get_collection_search_values(
769+
org: Organization = Depends(org_viewer_dep),
770+
):
771+
return await ops.get_profile_search_values(org)
772+
751773
@router.patch("/{profileid}", response_model=UpdatedResponse)
752774
async def commit_browser_to_existing(
753775
browser_commit: ProfileUpdate,

backend/test/test_profiles.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,20 @@ def test_list_profiles_filter_by_tag(
205205
assert data["total"] == 2
206206

207207

208+
def test_list_profiles_filter_by_name(admin_auth_headers, default_org_id, profile_2_id):
209+
r = requests.get(
210+
f"{API_PREFIX}/orgs/{default_org_id}/profiles?name={PROFILE_2_NAME}",
211+
headers=admin_auth_headers,
212+
)
213+
assert r.status_code == 200
214+
data = r.json()
215+
assert data["total"] == 1
216+
217+
profile = data["items"][0]
218+
assert profile["id"] == profile_2_id
219+
assert profile["name"] == PROFILE_2_NAME
220+
221+
208222
def test_update_profile_metadata(crawler_auth_headers, default_org_id, profile_id):
209223
# Get original created/modified times
210224
r = requests.get(
@@ -450,6 +464,16 @@ def test_profile_tag_counts(admin_auth_headers, default_org_id):
450464
}
451465

452466

467+
def test_profile_search_values(admin_auth_headers, default_org_id):
468+
r = requests.get(
469+
f"{API_PREFIX}/orgs/{default_org_id}/profiles/search-values",
470+
headers=admin_auth_headers,
471+
)
472+
assert r.status_code == 200
473+
data = r.json()
474+
assert sorted(data["names"]) == sorted([PROFILE_NAME_UPDATED, PROFILE_2_NAME])
475+
476+
453477
def test_delete_profile(admin_auth_headers, default_org_id, profile_2_id):
454478
# Delete second profile
455479
r = requests.delete(

0 commit comments

Comments
 (0)