From d63536bde6c0449375ee3508f833a2ceaeb96523 Mon Sep 17 00:00:00 2001 From: Mike Brown Date: Wed, 15 Jan 2025 16:54:38 -0500 Subject: [PATCH] Add backend support for Manage Users pagination (#8370) * Add backend pagination support for manage users * Add paginated search backend * Add missing override annotation * Update unit tests * Handle zero users in org * Update unit test * Fix sonar issues * Move constants to resolver --- .../api/apiuser/ManageUsersPageWrapper.java | 22 +++ .../api/apiuser/UserResolver.java | 15 ++ .../idp/repository/DemoOktaRepository.java | 30 +++- .../idp/repository/LiveOktaRepository.java | 43 ++++-- .../idp/repository/OktaRepository.java | 13 +- .../simplereport/service/ApiUserService.java | 57 ++++++++ .../service/OrganizationService.java | 2 +- .../repository/LiveOktaRepositoryTest.java | 138 ++++++++++++++++++ .../service/ApiUserServiceTest.java | 44 ++++++ .../service/OrganizationServiceTest.java | 4 +- 10 files changed, 350 insertions(+), 18 deletions(-) create mode 100644 backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/ManageUsersPageWrapper.java diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/ManageUsersPageWrapper.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/ManageUsersPageWrapper.java new file mode 100644 index 0000000000..a47c7669dc --- /dev/null +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/ManageUsersPageWrapper.java @@ -0,0 +1,22 @@ +package gov.cdc.usds.simplereport.api.apiuser; + +import gov.cdc.usds.simplereport.api.model.ApiUserWithStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.domain.Page; + +/** + * When the page content contains zero entries, this could be due to either: - zero search results + * for the query string - OR due to zero users in the entire organization. The value for total users + * in the org helps differentiate this. + */ +@Getter +@Setter +@AllArgsConstructor +public class ManageUsersPageWrapper { + + Page pageContent; + + int totalUsersInOrg; +} diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/UserResolver.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/UserResolver.java index 67edfb34a0..9c9fa38db8 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/UserResolver.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/apiuser/UserResolver.java @@ -16,6 +16,8 @@ /** Resolver for the graphql User type */ @Controller public class UserResolver { + public static final int DEFAULT_OKTA_USER_PAGE_SIZE = 10; + public static final int DEFAULT_OKTA_USER_PAGE_OFFSET = 0; private ApiUserService _userService; @@ -38,6 +40,19 @@ public List usersWithStatus() { return _userService.getUsersAndStatusInCurrentOrg(); } + @QueryMapping + public ManageUsersPageWrapper usersWithStatusPage( + @Argument int pageNumber, @Argument String searchQuery) { + if (pageNumber < 0) { + pageNumber = DEFAULT_OKTA_USER_PAGE_OFFSET; + } + if (!searchQuery.isBlank()) { + return _userService.searchUsersAndStatusInCurrentOrgPaged( + pageNumber, DEFAULT_OKTA_USER_PAGE_SIZE, searchQuery); + } + return _userService.getPagedUsersAndStatusInCurrentOrg(pageNumber, DEFAULT_OKTA_USER_PAGE_SIZE); + } + @QueryMapping public User user(@Argument UUID id, @Argument String email) { if (!StringUtils.isBlank(email)) { diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java index c0acd16cf6..08f5fe2738 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/DemoOktaRepository.java @@ -43,6 +43,8 @@ public class DemoOktaRepository implements OktaRepository { @Value("${simple-report.authorization.environment-name:DEV}") private String environment; + private static final String NON_EXISTANT_ORG_GET_USERS_ERROR = + "Cannot get Okta users from nonexistent organization."; private final OrganizationExtractor organizationExtractor; private final CurrentTenantDataAccessContextHolder tenantDataContextHolder; @@ -257,8 +259,7 @@ public void resendActivationEmail(String username) { // returns ALL users including inactive ones public Set getAllUsersForOrganization(Organization org) { if (!orgUsernamesMap.containsKey(org.getExternalId())) { - throw new IllegalGraphqlArgumentException( - "Cannot get Okta users from nonexistent organization."); + throw new IllegalGraphqlArgumentException(NON_EXISTANT_ORG_GET_USERS_ERROR); } return orgUsernamesMap.get(org.getExternalId()).stream() .collect(Collectors.toUnmodifiableSet()); @@ -266,13 +267,26 @@ public Set getAllUsersForOrganization(Organization org) { public Map getAllUsersWithStatusForOrganization(Organization org) { if (!orgUsernamesMap.containsKey(org.getExternalId())) { - throw new IllegalGraphqlArgumentException( - "Cannot get Okta users from nonexistent organization."); + throw new IllegalGraphqlArgumentException(NON_EXISTANT_ORG_GET_USERS_ERROR); } return orgUsernamesMap.get(org.getExternalId()).stream() .collect(Collectors.toMap(u -> u, u -> getUserStatus(u))); } + @Override + public Map getPagedUsersWithStatusForOrganization( + Organization org, int pageNumber, int pageSize) { + if (!orgUsernamesMap.containsKey(org.getExternalId())) { + throw new IllegalGraphqlArgumentException(NON_EXISTANT_ORG_GET_USERS_ERROR); + } + List allOrgUsernamesList = + orgUsernamesMap.get(org.getExternalId()).stream().sorted().collect(Collectors.toList()); + int startIndex = pageNumber * pageSize; + int endIndex = Math.min((startIndex + pageSize), allOrgUsernamesList.size()); + List pageContent = allOrgUsernamesList.subList(startIndex, endIndex); + return pageContent.stream().collect(Collectors.toMap(u -> u, this::getUserStatus)); + } + // this method doesn't mean much in a demo env public void createOrganization(Organization org) { String externalId = org.getExternalId(); @@ -399,7 +413,8 @@ public void reset() { allUsernames.clear(); } - public Integer getUsersInSingleFacility(Facility facility) { + @Override + public Integer getUsersCountInSingleFacility(Facility facility) { Integer accessCount = 0; for (OrganizationRoleClaims existingClaims : usernameOrgRolesMap.values()) { @@ -417,6 +432,11 @@ public Integer getUsersInSingleFacility(Facility facility) { return accessCount; } + @Override + public Integer getUsersCountInOrganization(Organization org) { + return orgUsernamesMap.get(org.getExternalId()).size(); + } + @Override public String getApplicationStatusForHealthCheck() { return ACTIVE_LITERAL; diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java index 30aca444e9..f0e012fe4b 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepository.java @@ -226,6 +226,19 @@ public Map getAllUsersWithStatusForOrganization(Organization .collect(Collectors.toMap(u -> u.getProfile().getLogin(), User::getStatus)); } + @Override + public Map getPagedUsersWithStatusForOrganization( + Organization org, int pageNumber, int pageSize) { + Group orgDefaultOktaGroup = getDefaultOktaGroup(org); + int afterIndex = pageNumber * pageSize; + List groupUsers = + groupApi.listGroupUsers(orgDefaultOktaGroup.getId(), String.valueOf(afterIndex), pageSize); + return groupUsers.stream() + .collect( + Collectors.toMap( + u -> Objects.requireNonNull(u.getProfile()).getLogin(), User::getStatus)); + } + private List getAllUsersForOrg(Organization org) { PagedList pagedUserList = new PagedList<>(); List allUsers = new ArrayList<>(); @@ -658,27 +671,39 @@ public Optional getOrganizationRoleClaimsForUser(String getUserOrThrowError(username, "Cannot get org external ID for nonexistent user")); } - public Integer getUsersInSingleFacility(Facility facility) { - String facilityAccessGroupName = - generateFacilityGroupName( - facility.getOrganization().getExternalId(), facility.getInternalId()); - - List facilityAccessGroup = - groupApi.listGroups(facilityAccessGroupName, null, null, 1, "stats", null, null, null); + private Integer getUsersCountInOktaGroup(String groupName) { + List groupList = + groupApi.listGroups(groupName, null, null, 1, "stats", null, null, null); - if (facilityAccessGroup.isEmpty()) { + if (groupList.isEmpty()) { return 0; } try { LinkedHashMap stats = - (LinkedHashMap) facilityAccessGroup.get(0).getEmbedded().get("stats"); + (LinkedHashMap) groupList.get(0).getEmbedded().get("stats"); return ((Integer) stats.get("usersCount")); } catch (NullPointerException e) { throw new BadRequestException("Unable to retrieve okta group stats", e); } } + @Override + public Integer getUsersCountInSingleFacility(Facility facility) { + String facilityAccessGroupName = + generateFacilityGroupName( + facility.getOrganization().getExternalId(), facility.getInternalId()); + + return getUsersCountInOktaGroup(facilityAccessGroupName); + } + + @Override + public Integer getUsersCountInOrganization(Organization org) { + String orgDefaultGroupName = + generateRoleGroupName(org.getExternalId(), OrganizationRole.getDefault()); + return getUsersCountInOktaGroup(orgDefaultGroupName); + } + public PartialOktaUser findUser(String username) { User user = getUserOrThrowError( diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java index 4eab17d804..4390e6b796 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/idp/repository/OktaRepository.java @@ -67,6 +67,15 @@ List updateUserPrivilegesAndGroupAccess( Map getAllUsersWithStatusForOrganization(Organization org); + /** + * @param org Organization being queried + * @param pageNumber Starts at page number 0 + * @param pageSize Number of results per page + * @return Map of usernames to the user status in Okta + */ + Map getPagedUsersWithStatusForOrganization( + Organization org, int pageNumber, int pageSize); + void createOrganization(Organization org); void activateOrganization(Organization org); @@ -83,7 +92,9 @@ List updateUserPrivilegesAndGroupAccess( Optional getOrganizationRoleClaimsForUser(String username); - Integer getUsersInSingleFacility(Facility facility); + Integer getUsersCountInSingleFacility(Facility facility); + + Integer getUsersCountInOrganization(Organization org); PartialOktaUser findUser(String username); diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/service/ApiUserService.java b/backend/src/main/java/gov/cdc/usds/simplereport/service/ApiUserService.java index 43db5a26e7..aa9a2f50d4 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/service/ApiUserService.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/service/ApiUserService.java @@ -4,6 +4,7 @@ import gov.cdc.usds.simplereport.api.ApiUserContextHolder; import gov.cdc.usds.simplereport.api.CurrentAccountRequestContextHolder; import gov.cdc.usds.simplereport.api.WebhookContextHolder; +import gov.cdc.usds.simplereport.api.apiuser.ManageUsersPageWrapper; import gov.cdc.usds.simplereport.api.model.ApiUserWithStatus; import gov.cdc.usds.simplereport.api.model.Role; import gov.cdc.usds.simplereport.api.model.errors.ConflictingUserException; @@ -46,6 +47,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.ScopeNotActiveException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -615,6 +619,59 @@ public List getUsersInCurrentOrg() { return usersInOrg; } + @AuthorizationConfiguration.RequirePermissionManageUsers + public ManageUsersPageWrapper getPagedUsersAndStatusInCurrentOrg(int pageNumber, int pageSize) { + Organization org = _orgService.getCurrentOrganization(); + + final Map emailsToStatus = + _oktaRepo.getPagedUsersWithStatusForOrganization(org, pageNumber, pageSize); + List users = _apiUserRepo.findAllByLoginEmailInOrderByName(emailsToStatus.keySet()); + List userWithStatusList = + users.stream() + .map(u -> new ApiUserWithStatus(u, emailsToStatus.get(u.getLoginEmail()))) + .toList(); + + Integer userCountInOrg = _oktaRepo.getUsersCountInOrganization(org); + PageRequest pageRequest = PageRequest.of(pageNumber, pageSize); + Page pageContent = + new PageImpl<>(userWithStatusList, pageRequest, userCountInOrg); + + return new ManageUsersPageWrapper(pageContent, userCountInOrg); + } + + @AuthorizationConfiguration.RequirePermissionManageUsers + public ManageUsersPageWrapper searchUsersAndStatusInCurrentOrgPaged( + int pageNumber, int pageSize, String searchQuery) { + List allUsers = getUsersAndStatusInCurrentOrg(); + + List totalFilteredUsersList = + allUsers.stream() + .filter( + u -> { + String firstName = + u.getFirstName() == null ? "" : String.format("%s ", u.getFirstName()); + String middleName = + u.getMiddleName() == null ? "" : String.format("%s ", u.getMiddleName()); + String fullName = firstName + middleName + u.getLastName(); + return fullName.toLowerCase().contains(searchQuery.toLowerCase()); + }) + .toList(); + + int totalSearchResults = totalFilteredUsersList.size(); + int startIndex = pageNumber * pageSize; + int endIndex = Math.min((startIndex + pageSize), totalFilteredUsersList.size()); + + Organization org = _orgService.getCurrentOrganization(); + Integer userCountInOrg = _oktaRepo.getUsersCountInOrganization(org); + + List filteredSublist = totalFilteredUsersList.subList(startIndex, endIndex); + PageRequest pageRequest = PageRequest.of(pageNumber, pageSize); + Page pageContent = + new PageImpl<>(filteredSublist, pageRequest, totalSearchResults); + + return new ManageUsersPageWrapper(pageContent, userCountInOrg); + } + // To be addressed in #8108 @AuthorizationConfiguration.RequirePermissionManageUsers public List getUsersAndStatusInCurrentOrg() { diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/service/OrganizationService.java b/backend/src/main/java/gov/cdc/usds/simplereport/service/OrganizationService.java index 87408a26f7..c64fdb119b 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/service/OrganizationService.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/service/OrganizationService.java @@ -496,7 +496,7 @@ public FacilityStats getFacilityStats(@Argument UUID facilityId) { usersWithSingleFacilityAccess = dbAuthorizationService.getUsersWithSingleFacilityAccessCount(facility); } else { - usersWithSingleFacilityAccess = this.oktaRepository.getUsersInSingleFacility(facility); + usersWithSingleFacilityAccess = this.oktaRepository.getUsersCountInSingleFacility(facility); } return FacilityStats.builder() .usersSingleAccessCount(usersWithSingleFacilityAccess) diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepositoryTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepositoryTest.java index 083329b403..a72c82cc22 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepositoryTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/idp/repository/LiveOktaRepositoryTest.java @@ -33,6 +33,7 @@ import com.okta.sdk.resource.model.UserStatus; import com.okta.sdk.resource.user.UserBuilder; import gov.cdc.usds.simplereport.api.CurrentTenantDataAccessContextHolder; +import gov.cdc.usds.simplereport.api.model.errors.BadRequestException; import gov.cdc.usds.simplereport.api.model.errors.ConflictingUserException; import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException; import gov.cdc.usds.simplereport.config.AuthorizationProperties; @@ -47,6 +48,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -659,6 +661,142 @@ void getAllUsersWithStatusForOrganization() { assertEquals(Map.of("email@example.com", UserStatus.ACTIVE), actual); } + @Test + void getPagedUsersWithStatusForOrganization() { + var org = new Organization("orgName", "orgType", "1", true); + var groupProfilePrefix = "SR-UNITTEST-TENANT:" + org.getExternalId() + ":NO_ACCESS"; + + var mockGroup = mock(Group.class); + var mockGroupList = List.of(mockGroup); + var mockGroupProfile = mock(GroupProfile.class); + var mockUser = mock(User.class); + PagedList mockUserList = new PagedList<>(List.of(mockUser), "", "", null); + var mockUserProfile = mock(UserProfile.class); + when(groupApi.listGroups( + eq(groupProfilePrefix), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull())) + .thenReturn(mockGroupList); + when(mockGroup.getProfile()).thenReturn(mockGroupProfile); + when(mockGroupProfile.getName()).thenReturn(groupProfilePrefix); + when(mockGroup.getId()).thenReturn("1234"); + when(groupApi.listGroupUsers(eq("1234"), any(), any())).thenReturn(mockUserList); + when(mockUser.getProfile()).thenReturn(mockUserProfile); + when(mockUserProfile.getLogin()).thenReturn("email@example.com"); + when(mockUser.getStatus()).thenReturn(UserStatus.ACTIVE); + + var actual = _repo.getPagedUsersWithStatusForOrganization(org, 0, 10); + assertEquals(Map.of("email@example.com", UserStatus.ACTIVE), actual); + } + + @Test + void getUsersCountInOrganization() { + String orgExternalId = "ce8782ca-18ba-4384-95fc-fbb7be0ef577"; + + var groupProfilePrefix = "SR-UNITTEST-TENANT:" + orgExternalId + ":NO_ACCESS"; + + var mockOrg = mock(Organization.class); + when(mockOrg.getExternalId()).thenReturn(orgExternalId); + + var mockGroup = mock(Group.class); + var mockGroupList = List.of(mockGroup); + when(groupApi.listGroups( + eq(groupProfilePrefix), + isNull(), + isNull(), + eq(1), + eq("stats"), + isNull(), + isNull(), + isNull())) + .thenReturn(mockGroupList); + + var mockEmbeddedMap = mock(Map.class); + when(mockGroup.getEmbedded()).thenReturn(mockEmbeddedMap); + var mockStatsLinkedHashMap = mock(LinkedHashMap.class); + when(mockEmbeddedMap.get("stats")).thenReturn(mockStatsLinkedHashMap); + when(mockStatsLinkedHashMap.get("usersCount")).thenReturn(10); + + var actual = _repo.getUsersCountInOrganization(mockOrg); + assertEquals(10, actual); + } + + @Test + void getUsersCountInSingleFacility() { + String facilityInternalId = "05bc0080-ad53-4b7a-a4b5-d1f86059a304"; + String orgExternalId = "ce8782ca-18ba-4384-95fc-fbb7be0ef577"; + + var facilitySuffix = ":FACILITY_ACCESS:" + facilityInternalId; + var groupProfilePrefix = "SR-UNITTEST-TENANT:" + orgExternalId + facilitySuffix; + + var mockOrg = mock(Organization.class); + var mockFacility = mock(Facility.class); + when(mockFacility.getOrganization()).thenReturn(mockOrg); + when(mockOrg.getExternalId()).thenReturn(orgExternalId); + when(mockFacility.getInternalId()).thenReturn(UUID.fromString(facilityInternalId)); + + var mockGroup = mock(Group.class); + var mockGroupList = List.of(mockGroup); + when(groupApi.listGroups( + eq(groupProfilePrefix), + isNull(), + isNull(), + eq(1), + eq("stats"), + isNull(), + isNull(), + isNull())) + .thenReturn(mockGroupList); + + var mockEmbeddedMap = mock(Map.class); + when(mockGroup.getEmbedded()).thenReturn(mockEmbeddedMap); + var mockStatsLinkedHashMap = mock(LinkedHashMap.class); + when(mockEmbeddedMap.get("stats")).thenReturn(mockStatsLinkedHashMap); + when(mockStatsLinkedHashMap.get("usersCount")).thenReturn(10); + + var actual = _repo.getUsersCountInSingleFacility(mockFacility); + assertEquals(10, actual); + } + + @Test + void + getUsersCountInSingleFacility_throwsBadRequestException_whenUnableToRetrieveOktaGroupStats() { + String facilityInternalId = "05bc0080-ad53-4b7a-a4b5-d1f86059a304"; + String orgExternalId = "ce8782ca-18ba-4384-95fc-fbb7be0ef577"; + + var facilitySuffix = ":FACILITY_ACCESS:" + facilityInternalId; + var groupProfilePrefix = "SR-UNITTEST-TENANT:" + orgExternalId + facilitySuffix; + + var mockOrg = mock(Organization.class); + var mockFacility = mock(Facility.class); + when(mockFacility.getOrganization()).thenReturn(mockOrg); + when(mockOrg.getExternalId()).thenReturn(orgExternalId); + when(mockFacility.getInternalId()).thenReturn(UUID.fromString(facilityInternalId)); + + var mockGroup = mock(Group.class); + var mockGroupList = List.of(mockGroup); + when(groupApi.listGroups( + eq(groupProfilePrefix), + isNull(), + isNull(), + eq(1), + eq("stats"), + isNull(), + isNull(), + isNull())) + .thenReturn(mockGroupList); + + Throwable caught = + assertThrows( + BadRequestException.class, () -> _repo.getUsersCountInSingleFacility(mockFacility)); + assertEquals("Unable to retrieve okta group stats", caught.getMessage()); + } + @Test void updateUserPrivileges() { var username = "fraud@example.com"; diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/service/ApiUserServiceTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/service/ApiUserServiceTest.java index 97990240b1..3d1e93dab9 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/service/ApiUserServiceTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/service/ApiUserServiceTest.java @@ -14,6 +14,7 @@ import static org.mockito.Mockito.when; import com.okta.sdk.resource.model.UserStatus; +import gov.cdc.usds.simplereport.api.apiuser.ManageUsersPageWrapper; import gov.cdc.usds.simplereport.api.model.ApiUserWithStatus; import gov.cdc.usds.simplereport.api.model.Role; import gov.cdc.usds.simplereport.api.model.errors.ConflictingUserException; @@ -51,6 +52,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.data.domain.Page; import org.springframework.security.access.AccessDeniedException; import org.springframework.test.context.TestPropertySource; @@ -149,6 +151,48 @@ void getUsersAndStatusInCurrentOrg_success() { users.get(5), "allfacilities@example.com", "Williams", UserStatus.ACTIVE); } + @Test + @WithSimpleReportOrgAdminUser + void getPagedUsersAndStatusInCurrentOrg_success() { + initSampleData(); + ManageUsersPageWrapper usersPageWrapper = _service.getPagedUsersAndStatusInCurrentOrg(0, 3); + assertEquals(6, usersPageWrapper.getTotalUsersInOrg()); + + Page usersPage = usersPageWrapper.getPageContent(); + List users = usersPage.stream().toList(); + assertEquals(3, users.size()); + + checkApiUserWithStatus(users.get(0), "admin@example.com", "Andrews", UserStatus.ACTIVE); + checkApiUserWithStatus(users.get(1), "bobbity@example.com", "Bobberoo", UserStatus.ACTIVE); + checkApiUserWithStatus( + users.get(2), "allfacilities@example.com", "Williams", UserStatus.ACTIVE); + + ManageUsersPageWrapper users2ndPageWrapper = _service.getPagedUsersAndStatusInCurrentOrg(1, 3); + assertEquals(6, users2ndPageWrapper.getTotalUsersInOrg()); + + Page users2ndPage = users2ndPageWrapper.getPageContent(); + List users2ndList = users2ndPage.stream().toList(); + assertEquals(3, users2ndList.size()); + + checkApiUserWithStatus(users2ndList.get(0), "invalid@example.com", "Irwin", UserStatus.ACTIVE); + checkApiUserWithStatus(users2ndList.get(1), "nobody@example.com", "Nixon", UserStatus.ACTIVE); + checkApiUserWithStatus( + users2ndList.get(2), "notruby@example.com", "Reynolds", UserStatus.ACTIVE); + } + + @Test + @WithSimpleReportOrgAdminUser + void searchUsersAndStatusInCurrentOrgPaged_success() { + initSampleData(); + ManageUsersPageWrapper usersPageWrapper = + _service.searchUsersAndStatusInCurrentOrgPaged(0, 10, "Bob"); + Page usersPage = usersPageWrapper.getPageContent(); + List users = usersPage.stream().toList(); + assertEquals(1, users.size()); + assertEquals(6, usersPageWrapper.getTotalUsersInOrg()); + checkApiUserWithStatus(users.get(0), "bobbity@example.com", "Bobberoo", UserStatus.ACTIVE); + } + @Test @WithSimpleReportOrgAdminUser void getUser_withAdminUser_withOktaMigrationDisabled_success() { diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/service/OrganizationServiceTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/service/OrganizationServiceTest.java index 8562a5b2b5..1e1520258d 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/service/OrganizationServiceTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/service/OrganizationServiceTest.java @@ -460,7 +460,7 @@ void getFacilityStats_withOktaMigrationDisabled_success() { UUID facilityId = UUID.randomUUID(); Facility mockFacility = mock(Facility.class); doReturn(Optional.of(mockFacility)).when(this.facilityRepository).findById(facilityId); - doReturn(2).when(oktaRepository).getUsersInSingleFacility(mockFacility); + doReturn(2).when(oktaRepository).getUsersCountInSingleFacility(mockFacility); doReturn(1).when(personRepository).countByFacilityAndIsDeleted(mockFacility, false); FacilityStats stats = _service.getFacilityStats(facilityId); @@ -480,7 +480,7 @@ void getFacilityStats_withOktaMigrationEnabled_success() { doReturn(2).when(personRepository).countByFacilityAndIsDeleted(mockFacility, false); FacilityStats stats = _service.getFacilityStats(facilityId); - verify(oktaRepository, times(0)).getUsersInSingleFacility(mockFacility); + verify(oktaRepository, times(0)).getUsersCountInSingleFacility(mockFacility); assertEquals(4, stats.getUsersSingleAccessCount()); assertEquals(2, stats.getPatientsSingleAccessCount()); }