Skip to content

Commit

Permalink
Merge pull request #439 from GSA/feature/issue-203/search_user_by_email
Browse files Browse the repository at this point in the history
Allow admin to search users by name or email
  • Loading branch information
felder101 authored Nov 3, 2023
2 parents 951eef0 + b2d4dc8 commit 6ebed49
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 13 deletions.
4 changes: 2 additions & 2 deletions training-front-end/src/components/AdminSearchUser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
async function search() {
noResults.value = false
const url = new URL(`${report_url}`)
url.search = new URLSearchParams({name: searchTerm.value, page_number: currentPage.value + 1})
url.search = new URLSearchParams({searchText: searchTerm.value, page_number: currentPage.value + 1})
try {
const response = await fetch(
Expand Down Expand Up @@ -116,7 +116,7 @@
class="tablet:grid-col-8"
>
<label for="search-field">
Name
Name or Email
</label>
<div
id="gnHint"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe('AdminAgencySelect', async () => {
linkElements[1].trigger('click')

expect(fetchSpy).toBeCalledTimes(2)
expect(fetchSpy.mock.lastCall[0].search).toBe('?name=Steeply&page_number=2')
expect(fetchSpy.mock.lastCall[0].search).toBe('?searchText=Steeply&page_number=2')
})

it('displays no results message', async () => {
Expand Down
6 changes: 3 additions & 3 deletions training/api/api_v1/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,16 @@ def download_report_csv(user=Depends(user_from_form), repo: UserRepository = Dep

@router.get("/users", response_model=UserSearchResult)
def get_users(
name: Annotated[str, Query(min_length=1)],
searchText: Annotated[str, Query(min_length=1)],
page_number: int = 1,
repo: UserRepository = Depends(user_repository),
user=Depends(RequireRole(["Admin"]))
):
'''
Get/users is used to search users for admin portal
currently search only support search by user name, name is required field.
currently search only support search by user name and email address, searchText is required field.
It may have additional search criteira in future, which will require logic update.
page_number param is used to support UI pagination functionality.
It returns UserSearchResult object with a list of users and total_count used for UI pagination
'''
return repo.get_users(name, page_number)
return repo.get_users(searchText, page_number)
13 changes: 7 additions & 6 deletions training/repositories/user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from sqlalchemy import nullsfirst
from sqlalchemy import nullsfirst, or_
from sqlalchemy.orm import Session
from training import models, schemas
from training.schemas import UserQuizCompletionReportData, UserSearchResult
Expand Down Expand Up @@ -66,12 +66,13 @@ def get_user_quiz_completion_report(self, report_user_id: int) -> list[UserQuizC
else:
raise ValueError("Invalid Report User")

def get_users(self, name: str, page_number: int) -> UserSearchResult:
# current UI only support search by user name, and it is required field.
if (name and name.strip() != '' and page_number > 0):
count = self._session.query(models.User).filter(models.User.name.ilike(f"%{name}%")).count()
def get_users(self, searchText: str, page_number: int) -> UserSearchResult:
# current UI only support search by user name and email. The search field it is required field.
if (searchText and searchText.strip() != '' and page_number > 0):
count = self._session.query(models.User).filter(or_(models.User.name.ilike(f"%{searchText}%"), models.User.email.ilike(f"%{searchText}%"))).count()
page_size = 25
offset = (page_number - 1) * page_size
search_results = self._session.query(models.User).filter(models.User.name.ilike(f"%{name}%")).limit(page_size).offset(offset).all()
search_results = self._session.query(models.User).filter(
or_(models.User.name.ilike(f"%{searchText}%"), models.User.email.ilike(f"%{searchText}%"))).limit(page_size).offset(offset).all()
user_search_result = UserSearchResult(users=search_results, total_count=count)
return user_search_result
2 changes: 1 addition & 1 deletion training/tests/test_api_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_get_users(goodJWT, mock_user_repo: UserRepository):
user_search_result = UserSearchResult(users=users, total_count=2)
mock_user_repo.get_users.return_value = user_search_result
response = client.get(
"/api/v1/users?name=test&page_number=1",
"/api/v1/users?searchText=test&page_number=1",
headers={"Authorization": f"Bearer {goodJWT}"}
)
assert response.status_code == status.HTTP_200_OK
Expand Down
10 changes: 10 additions & 0 deletions training/tests/test_user_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,13 @@ def test_get_users(user_repo_with_data: UserRepository, valid_user_ids: List[int
assert result is not None
for item in result.users:
assert search_criteria in item.name


def test_get_users_by_email(user_repo_with_data: UserRepository, valid_user_ids: List[int]):
valid_user_id = valid_user_ids[0]
db_user = user_repo_with_data.find_by_id(valid_user_id)
search_criteria = db_user.email[:-1]
result = user_repo_with_data.get_users(search_criteria, 1)
assert result is not None
for item in result.users:
assert search_criteria in item.email

0 comments on commit 6ebed49

Please sign in to comment.