-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add backend support for Manage Users pagination #8370
Changes from 4 commits
3c5d502
bfb92d5
ee111f6
d3166f1
89e4c6b
c595f42
a6bfa4e
a8eb38a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -226,6 +226,19 @@ public Map<String, UserStatus> getAllUsersWithStatusForOrganization(Organization | |
.collect(Collectors.toMap(u -> u.getProfile().getLogin(), User::getStatus)); | ||
} | ||
|
||
@Override | ||
public Map<String, UserStatus> getPagedUsersWithStatusForOrganization( | ||
Organization org, int pageNumber, int pageSize) { | ||
Group orgDefaultOktaGroup = getDefaultOktaGroup(org); | ||
int afterIndex = pageNumber * pageSize; | ||
List<User> 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<User> getAllUsersForOrg(Organization org) { | ||
PagedList<User> pagedUserList = new PagedList<>(); | ||
List<User> allUsers = new ArrayList<>(); | ||
|
@@ -658,27 +671,39 @@ public Optional<OrganizationRoleClaims> 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<Group> facilityAccessGroup = | ||
groupApi.listGroups(facilityAccessGroupName, null, null, 1, "stats", null, null, null); | ||
private Integer getUsersCountInOktaGroup(String groupName) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pagination needed the total number of users in an organization, so I split this logic out to be shared for facilities and organizations. |
||
List<Group> groupList = | ||
groupApi.listGroups(groupName, null, null, 1, "stats", null, null, null); | ||
|
||
if (facilityAccessGroup.isEmpty()) { | ||
if (groupList.isEmpty()) { | ||
return 0; | ||
} | ||
|
||
try { | ||
LinkedHashMap<String, Object> 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( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,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; | ||
|
@@ -81,6 +84,8 @@ public class ApiUserService { | |
@Autowired private DbOrgRoleClaimsService _dbOrgRoleClaimsService; | ||
|
||
@Autowired private FeatureFlagsConfig _featureFlagsConfig; | ||
public static final int DEFAULT_OKTA_USER_PAGE_SIZE = 10; | ||
public static final int DEFAULT_OKTA_USER_PAGE_OFFSET = 0; | ||
mpbrown marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private void createUserUpdatedAuditLog(Object authorId, Object updatedUserId) { | ||
log.info("User with id={} updated by user with id={}", authorId, updatedUserId); | ||
|
@@ -615,6 +620,51 @@ public List<ApiUser> getUsersInCurrentOrg() { | |
return usersInOrg; | ||
} | ||
|
||
@AuthorizationConfiguration.RequirePermissionManageUsers | ||
public Page<ApiUserWithStatus> getPagedUsersAndStatusInCurrentOrg(int pageNumber, int pageSize) { | ||
Organization org = _orgService.getCurrentOrganization(); | ||
|
||
final Map<String, UserStatus> emailsToStatus = | ||
_oktaRepo.getPagedUsersWithStatusForOrganization(org, pageNumber, pageSize); | ||
List<ApiUser> users = _apiUserRepo.findAllByLoginEmailInOrderByName(emailsToStatus.keySet()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part of the method will be updated in this future ticket to fetch users from the DB instead of having to get them from Okta first. |
||
List<ApiUserWithStatus> userWithStatusList = | ||
users.stream() | ||
.map(u -> new ApiUserWithStatus(u, emailsToStatus.get(u.getLoginEmail()))) | ||
.toList(); | ||
|
||
Integer userCountInOrg = _oktaRepo.getUsersCountInOrganization(org); | ||
PageRequest pageRequest = PageRequest.of(pageNumber, pageSize); | ||
|
||
return new PageImpl<>(userWithStatusList, pageRequest, userCountInOrg); | ||
} | ||
|
||
public Page<ApiUserWithStatus> searchUsersAndStatusInCurrentOrgPaged( | ||
int pageNumber, int pageSize, String searchQuery) { | ||
List<ApiUserWithStatus> allUsers = getUsersAndStatusInCurrentOrg(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See earlier comment |
||
|
||
List<ApiUserWithStatus> filteredUsersList = | ||
allUsers.stream() | ||
.filter( | ||
u -> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we are purposefully excluding suffix based on the existing frontend search in |
||
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 totalResults = filteredUsersList.size(); | ||
int startIndex = pageNumber * pageSize; | ||
int endIndex = Math.min((startIndex + pageSize), filteredUsersList.size()); | ||
|
||
List<ApiUserWithStatus> pageContent = filteredUsersList.subList(startIndex, endIndex); | ||
PageRequest pageRequest = PageRequest.of(pageNumber, pageSize); | ||
|
||
return new PageImpl<>(pageContent, pageRequest, totalResults); | ||
} | ||
|
||
// To be addressed in #8108 | ||
@AuthorizationConfiguration.RequirePermissionManageUsers | ||
public List<ApiUserWithStatus> getUsersAndStatusInCurrentOrg() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Search is being handled by a different service method here only as an intermediate step between now and the rest of the Okta migration. Currently, we have to fetch the entire organization's user list from Okta in order to do this search. Once we switch to getting users from our database, we can have the other method below use the search filter easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to confirm, we aren't using this search functionality on the frontend just yet, right? That's being introduced with your frontend PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, so this new endpoint is not currently being used anywhere in the app until the frontend PR