From ed6d362b1868b0083fec7d9d6ee47116c310c021 Mon Sep 17 00:00:00 2001 From: Thomasr Date: Tue, 5 Nov 2024 03:38:27 -0500 Subject: [PATCH 1/2] Modify counting logic based on org role --- .../usermanagement/GroupApiServiceImpl.java | 31 ++++++++++--------- .../api/usermanagement/GroupController.java | 2 +- .../api/usermanagement/view/GroupView.java | 10 +++--- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java index c54c3f526..715ad4cb1 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java @@ -186,7 +186,7 @@ public Mono> getGroups() { return sessionUserService.getVisitorOrgMemberCache() .flatMap(orgMember -> { String orgId = orgMember.getOrgId(); - Mono orgAdminCountMono = orgMemberService.getAllOrgAdmins(orgId).map(List::size); + Mono> orgAdminsMono = orgMemberService.getAllOrgAdmins(orgId); if (orgMember.isAdmin() || orgMember.isSuperAdmin()) { MemberRole memberRole; if(orgMember.isAdmin()) { @@ -196,44 +196,45 @@ public Mono> getGroups() { } return groupService.getByOrgId(orgId) .sort() - .flatMapSequential(group -> groupMemberService.getAllGroupAdmin(group.getId()) - .zipWith(groupMemberService.getGroupMembers(group.getId(), 0, -1)) - .zipWith(orgAdminCountMono, TupleUtils::merge) + .flatMapSequential(group -> groupMemberService.getGroupMembers(group.getId(), 0, -1) + .zipWith(orgAdminsMono) .flatMap(tuple -> { - var adminMembers = tuple.getT1(); - var users = tuple.getT2(); - var orgAdminCount = tuple.getT3(); + var users = tuple.getT1(); + var orgAdmins = tuple.getT2(); + var adminMembers = orgAdmins.stream().filter(orgAdmin -> users.stream().anyMatch(member -> member.getUserId().equals(orgAdmin.getUserId()))).toList(); if(group.isAllUsersGroup()) { - return GroupView.from(group, memberRole.getValue(), orgAdminCount, users.size()); + return GroupView.from(group, memberRole.getValue(), orgAdmins.size(), users.size(), users.stream().map(GroupMember::getUserId).toList()); } else { - return GroupView.from(group, memberRole.getValue(), adminMembers.size(), users.size()); + return GroupView.from(group, memberRole.getValue(), adminMembers.size(), users.size(), users.stream().map(GroupMember::getUserId).toList()); } }) ) .collectList(); } return groupMemberService.getUserGroupMembersInOrg(orgId, orgMember.getUserId()) - .zipWith(orgAdminCountMono) + .zipWith(orgAdminsMono) .flatMap(tuple -> { List groupMembers = tuple.getT1(); - int orgAdminCount = tuple.getT2(); + List orgAdmins = tuple.getT2(); List groupIds = collectList(groupMembers, GroupMember::getGroupId); Map groupMemberMap = collectMap(groupMembers, GroupMember::getGroupId, it -> it); return groupService.getByIds(groupIds) .sort() .flatMapSequential(group -> { - var adminMembers = groupMembers.stream().filter(groupMember -> groupMember.getGroupId().equals(group.getId()) && groupMember.getRole() == MemberRole.ADMIN).toList(); var allMembers = groupMembers.stream().filter(groupMember -> groupMember.getGroupId().equals(group.getId())).toList(); + var adminMembers = orgAdmins.stream().filter(orgAdmin -> allMembers.stream().anyMatch(member -> member.getUserId().equals(orgAdmin.getUserId()))).toList(); if(group.isAllUsersGroup()) { return GroupView.from(group, groupMemberMap.get(group.getId()).getRole().getValue(), - orgAdminCount, - allMembers.size()); + orgAdmins.size(), + allMembers.size(), + allMembers.stream().map(GroupMember::getUserId).toList()); } else { return GroupView.from(group, groupMemberMap.get(group.getId()).getRole().getValue(), adminMembers.size(), - allMembers.size()); + allMembers.size(), + allMembers.stream().map(GroupMember::getUserId).toList()); } }) .collectList(); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java index 65e38befa..9582e7bd2 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java @@ -51,7 +51,7 @@ public class GroupController implements GroupEndpoints public Mono> create(@Valid @RequestBody CreateGroupRequest newGroup) { return groupApiService.create(newGroup) .delayUntil(group -> businessEventPublisher.publishGroupCreateEvent(group)) - .flatMap(group -> GroupView.from(group, MemberRole.ADMIN.getValue(), 1, 1)) + .flatMap(group -> GroupView.from(group, MemberRole.ADMIN.getValue(), 1, 1, List.of())) .map(ResponseView::success); } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupView.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupView.java index 18cd19c06..0f261fd54 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupView.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupView.java @@ -1,6 +1,8 @@ package org.lowcoder.api.usermanagement.view; +import java.util.List; import java.util.Locale; +import java.util.Map; import org.lowcoder.domain.group.model.Group; import org.lowcoder.sdk.util.LocaleUtils; @@ -23,10 +25,9 @@ public class GroupView { private String dynamicRule; private boolean isSyncGroup; private boolean isSyncDelete; - private int adminUserCount; - private int userCount; + private Map stats; - public static Mono from(Group group, String memberRole, int adminCount, int userCount) { + public static Mono from(Group group, String memberRole, int adminCount, int userCount, List users) { return Mono.deferContextual(contextView -> { Locale locale = LocaleUtils.getLocale(contextView); GroupView groupView = GroupView.builder() @@ -40,8 +41,7 @@ public static Mono from(Group group, String memberRole, int adminCoun .dynamicRule(group.getDynamicRule()) .isSyncGroup(group.isSyncGroup()) .isSyncDelete(group.isSyncDeleted()) - .adminUserCount(adminCount) - .userCount(userCount) + .stats(Map.of("adminUserCount", adminCount, "userCount", userCount, "users", users)) .build(); return Mono.just(groupView); }); From 9ca6647149a60a89c7ecd74d77c234e7e57a7438 Mon Sep 17 00:00:00 2001 From: Thomasr Date: Thu, 7 Nov 2024 11:52:45 -0500 Subject: [PATCH 2/2] Add statistic fields to group list api --- .../usermanagement/GroupApiServiceImpl.java | 4 +- .../api/usermanagement/GroupController.java | 49 +++++++++++++++---- .../api/usermanagement/GroupEndpoints.java | 9 +--- .../view/GroupListResponseView.java | 22 +++++++++ 4 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupListResponseView.java diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java index 715ad4cb1..abf581586 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java @@ -199,7 +199,7 @@ public Mono> getGroups() { .flatMapSequential(group -> groupMemberService.getGroupMembers(group.getId(), 0, -1) .zipWith(orgAdminsMono) .flatMap(tuple -> { - var users = tuple.getT1(); + var users = tuple.getT1().stream().filter(user -> user.getRole() != MemberRole.SUPER_ADMIN).toList(); var orgAdmins = tuple.getT2(); var adminMembers = orgAdmins.stream().filter(orgAdmin -> users.stream().anyMatch(member -> member.getUserId().equals(orgAdmin.getUserId()))).toList(); if(group.isAllUsersGroup()) { @@ -221,7 +221,7 @@ public Mono> getGroups() { return groupService.getByIds(groupIds) .sort() .flatMapSequential(group -> { - var allMembers = groupMembers.stream().filter(groupMember -> groupMember.getGroupId().equals(group.getId())).toList(); + var allMembers = groupMembers.stream().filter(groupMember -> groupMember.getGroupId().equals(group.getId()) && groupMember.getRole() != MemberRole.SUPER_ADMIN).toList(); var adminMembers = orgAdmins.stream().filter(orgAdmin -> allMembers.stream().anyMatch(member -> member.getUserId().equals(orgAdmin.getUserId()))).toList(); if(group.isAllUsersGroup()) { return GroupView.from(group, diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java index 9582e7bd2..66eef1a40 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java @@ -9,17 +9,15 @@ import org.apache.commons.lang3.StringUtils; import org.lowcoder.api.framework.view.ResponseView; import org.lowcoder.api.home.SessionUserService; -import org.lowcoder.api.usermanagement.view.AddMemberRequest; -import org.lowcoder.api.usermanagement.view.CreateGroupRequest; -import org.lowcoder.api.usermanagement.view.GroupMemberAggregateView; -import org.lowcoder.api.usermanagement.view.GroupView; -import org.lowcoder.api.usermanagement.view.UpdateGroupRequest; -import org.lowcoder.api.usermanagement.view.UpdateRoleRequest; +import org.lowcoder.api.usermanagement.view.*; import org.lowcoder.api.util.BusinessEventPublisher; import org.lowcoder.api.util.GidService; +import org.lowcoder.domain.group.model.GroupMember; import org.lowcoder.domain.group.service.GroupMemberService; import org.lowcoder.domain.group.service.GroupService; import org.lowcoder.domain.organization.model.MemberRole; +import org.lowcoder.domain.organization.model.OrgMember; +import org.lowcoder.domain.organization.service.OrgMemberService; import org.lowcoder.sdk.exception.BizError; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; @@ -46,6 +44,8 @@ public class GroupController implements GroupEndpoints private GroupService groupService; @Autowired private GidService gidService; + @Autowired + private OrgMemberService orgMemberService; @Override public Mono> create(@Valid @RequestBody CreateGroupRequest newGroup) { @@ -75,9 +75,40 @@ public Mono> delete(@PathVariable String groupId) { } @Override - public Mono>> getOrgGroups() { - return groupApiService.getGroups() - .map(ResponseView::success); + public Mono>> getOrgGroups() { + return groupApiService.getGroups().flatMap(groupList -> { + if(groupList.isEmpty()) return Mono.just(new GroupListResponseView<>(ResponseView.SUCCESS, + "", List.of(), 0, 0, 0, 0)); + return sessionUserService.getVisitorOrgMemberCache() + .map(OrgMember::getOrgId) + .flatMap(orgId -> orgMemberService.getOrganizationMembers(orgId) + .collectList() + .zipWith(groupService.getDevGroup(orgId).flatMap(devGroup -> groupMemberService.getGroupMembers(devGroup.getId(), 0, -1))) + .map(tuple -> { + List orgMembers = tuple.getT1(); + List devMembers = tuple.getT2(); + int totalAdmins = orgMembers.stream().filter(OrgMember::isAdmin).toList().size(); + int totalAdminsAndDevelopers = orgMembers.stream() + .filter(orgMember -> orgMember.isAdmin() || + devMembers.stream().anyMatch(devMember -> devMember.getUserId().equals(orgMember.getUserId()))).toList().size(); + int totalDevelopersOnly = orgMembers.stream() + .filter(orgMember -> !orgMember.isAdmin() && !orgMember.isSuperAdmin() && + devMembers.stream().anyMatch(devMember -> devMember.getUserId().equals(orgMember.getUserId()))).toList().size(); + int totalOtherMembers = orgMembers.stream() + .filter(orgMember -> !orgMember.isAdmin() && !orgMember.isSuperAdmin() && + devMembers.stream().noneMatch(devMember -> devMember.getUserId().equals(orgMember.getUserId()))).toList().size(); + + return new GroupListResponseView<>(ResponseView.SUCCESS, + "", + groupList, + totalAdmins, + totalAdminsAndDevelopers, + totalDevelopersOnly, + totalOtherMembers); + }) + ); + } + ); } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java index c1d5fd5e7..983a0bed1 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java @@ -5,12 +5,7 @@ import jakarta.validation.Valid; import org.lowcoder.api.framework.view.ResponseView; -import org.lowcoder.api.usermanagement.view.AddMemberRequest; -import org.lowcoder.api.usermanagement.view.CreateGroupRequest; -import org.lowcoder.api.usermanagement.view.GroupMemberAggregateView; -import org.lowcoder.api.usermanagement.view.GroupView; -import org.lowcoder.api.usermanagement.view.UpdateGroupRequest; -import org.lowcoder.api.usermanagement.view.UpdateRoleRequest; +import org.lowcoder.api.usermanagement.view.*; import org.lowcoder.infra.constant.NewUrl; import org.lowcoder.infra.constant.Url; import org.springframework.web.bind.annotation.DeleteMapping; @@ -68,7 +63,7 @@ public Mono> update(@PathVariable String groupId, description = "Retrieve a list of User Groups within Lowcoder, providing an overview of available groups, based on the access rights of the currently impersonated User." ) @GetMapping("/list") - public Mono>> getOrgGroups(); + public Mono>> getOrgGroups(); @Operation( tags = TAG_GROUP_MEMBERS, diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupListResponseView.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupListResponseView.java new file mode 100644 index 000000000..7b228d960 --- /dev/null +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/view/GroupListResponseView.java @@ -0,0 +1,22 @@ +package org.lowcoder.api.usermanagement.view; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import org.lowcoder.api.framework.view.ResponseView; +import org.lowcoder.sdk.exception.BizError; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@Getter +public class GroupListResponseView extends ResponseView { + private final int totalAdminsAndDevelopers; + private final int totalDevelopersOnly; + private final int totalAdmins; + private final int totalOtherMembers; + public GroupListResponseView(int code, String message, T data, int totalAdmins, int totalAdminsAndDevelopers, int totalDevelopersOnly, int totalOtherMembers) { + super(code, message, data); + this.totalAdmins = totalAdmins; + this.totalDevelopersOnly = totalDevelopersOnly; + this.totalAdminsAndDevelopers = totalAdminsAndDevelopers; + this.totalOtherMembers = totalOtherMembers; + } +}