Skip to content
Merged
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
7 changes: 7 additions & 0 deletions backend/btrixcloud/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,13 @@ class ProfileBrowserGetUrlResponse(BaseModel):
url: AnyHttpUrl


# ============================================================================
class ProfileSearchValuesResponse(BaseModel):
"""Response model for profiles search values"""

names: List[str]


# ============================================================================

### USERS ###
Expand Down
22 changes: 22 additions & 0 deletions backend/btrixcloud/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
ProfileBrowserMetadata,
TagsResponse,
ListFilterType,
ProfileSearchValuesResponse,
)
from .utils import dt_now, str_to_date

Expand Down Expand Up @@ -444,6 +445,7 @@ async def list_profiles(
userid: Optional[UUID] = None,
tags: Optional[List[str]] = None,
tag_match: Optional[ListFilterType] = ListFilterType.AND,
name: Optional[str] = None,
page_size: int = DEFAULT_PAGE_SIZE,
page: int = 1,
sort_by: str = "modified",
Expand All @@ -462,6 +464,8 @@ async def list_profiles(
if tags:
query_type = "$all" if tag_match == ListFilterType.AND else "$in"
match_query["tags"] = {query_type: tags}
if name:
match_query["name"] = name

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

Expand Down Expand Up @@ -651,6 +655,13 @@ async def get_profile_tag_counts(
).to_list()
return tags

async def get_profile_search_values(self, org: Organization):
"""Return profile names for use in search"""
names = await self.profiles.distinct("name", {"oid": org.id})
# Remove empty strings
names = [name for name in names if name]
return {"names": names}

def _run_task(self, func) -> None:
"""add bg tasks to set to avoid premature garbage collection"""
task = asyncio.create_task(func)
Expand Down Expand Up @@ -715,6 +726,7 @@ async def list_profiles(
description='Defaults to `"and"` if omitted',
),
] = ListFilterType.AND,
name: Optional[str] = None,
pageSize: int = DEFAULT_PAGE_SIZE,
page: int = 1,
sortBy: str = "modified",
Expand All @@ -725,6 +737,7 @@ async def list_profiles(
userid,
tags=tags,
tag_match=tag_match,
name=name,
page_size=pageSize,
page=page,
sort_by=sortBy,
Expand All @@ -748,6 +761,15 @@ async def commit_browser_to_new(
async def get_profile_tag_counts(org: Organization = Depends(org_viewer_dep)):
return {"tags": await ops.get_profile_tag_counts(org)}

@router.get(
"/search-values",
response_model=ProfileSearchValuesResponse,
)
async def get_collection_search_values(
org: Organization = Depends(org_viewer_dep),
):
return await ops.get_profile_search_values(org)

@router.patch("/{profileid}", response_model=UpdatedResponse)
async def commit_browser_to_existing(
browser_commit: ProfileUpdate,
Expand Down
24 changes: 24 additions & 0 deletions backend/test/test_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,20 @@ def test_list_profiles_filter_by_tag(
assert data["total"] == 2


def test_list_profiles_filter_by_name(admin_auth_headers, default_org_id, profile_2_id):
r = requests.get(
f"{API_PREFIX}/orgs/{default_org_id}/profiles?name={PROFILE_2_NAME}",
headers=admin_auth_headers,
)
assert r.status_code == 200
data = r.json()
assert data["total"] == 1

profile = data["items"][0]
assert profile["id"] == profile_2_id
assert profile["name"] == PROFILE_2_NAME


def test_update_profile_metadata(crawler_auth_headers, default_org_id, profile_id):
# Get original created/modified times
r = requests.get(
Expand Down Expand Up @@ -450,6 +464,16 @@ def test_profile_tag_counts(admin_auth_headers, default_org_id):
}


def test_profile_search_values(admin_auth_headers, default_org_id):
r = requests.get(
f"{API_PREFIX}/orgs/{default_org_id}/profiles/search-values",
headers=admin_auth_headers,
)
assert r.status_code == 200
data = r.json()
assert sorted(data["names"]) == sorted([PROFILE_NAME_UPDATED, PROFILE_2_NAME])


def test_delete_profile(admin_auth_headers, default_org_id, profile_2_id):
# Delete second profile
r = requests.delete(
Expand Down
Loading