Skip to content

Commit 7051a76

Browse files
dragonpooludomikula
authored andcommitted
Get users who do not belong to this group among the members of the organization.
Removed "/{orgId}/{searchMemberName}/{searchGroupId}/members" endpoint.
1 parent 24cfa60 commit 7051a76

File tree

11 files changed

+121
-111
lines changed

11 files changed

+121
-111
lines changed

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/repository/UserRepository.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package org.lowcoder.domain.user.repository;
22

33
import java.util.Collection;
4+
import java.util.List;
45

56
import org.lowcoder.domain.user.model.User;
7+
import org.springframework.data.domain.Pageable;
68
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
79
import org.springframework.stereotype.Repository;
810

911
import reactor.core.publisher.Flux;
1012
import reactor.core.publisher.Mono;
13+
import org.springframework.data.mongodb.repository.Query;
1114

1215
@Repository
1316
public interface UserRepository extends ReactiveMongoRepository<User, String> {
@@ -23,4 +26,10 @@ public interface UserRepository extends ReactiveMongoRepository<User, String> {
2326

2427
//email1 and email2 should be equal
2528
Flux<User> findByEmailOrConnections_Email(String email1, String email2);
29+
30+
@Query("{ '_id': { $in: ?0 }, 'state': ?1, 'isEnabled': ?2, $or: [ { 'name': { $regex: ?3, $options: 'i' } }, { '_id': { $regex: ?3, $options: 'i' } } ] }")
31+
Flux<User> findUsersByIdsAndSearchNameForPagination(Collection<String> ids, String state, boolean isEnabled, String searchRegex, Pageable pageable);
32+
33+
@Query(value = "{ '_id': { $in: ?0 }, 'state': ?1, 'isEnabled': ?2, $or: [ { 'name': { $regex: ?3, $options: 'i' } }, { '_id': { $regex: ?3, $options: 'i' } } ] }", count = true)
34+
Mono<Long> countUsersByIdsAndSearchName(Collection<String> ids, String state, boolean isEnabled, String searchRegex);
2635
}

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
import java.util.Collection;
44
import java.util.Map;
55

6-
import org.lowcoder.domain.user.model.AuthUser;
7-
import org.lowcoder.domain.user.model.Connection;
8-
import org.lowcoder.domain.user.model.User;
9-
import org.lowcoder.domain.user.model.UserDetail;
6+
import org.lowcoder.domain.user.model.*;
107
import org.lowcoder.infra.annotation.NonEmptyMono;
118
import org.lowcoder.infra.mongo.MongoUpsertHelper.PartialResourceWithId;
9+
import org.springframework.data.domain.Pageable;
1210
import org.springframework.http.codec.multipart.Part;
1311
import org.springframework.web.server.ServerWebExchange;
1412

@@ -68,5 +66,7 @@ public interface UserService {
6866

6967
Flux<User> findBySourceAndIds(String connectionSource, Collection<String> connectionSourceUuids);
7068

71-
}
69+
Flux<User> findUsersByIdsAndSearchNameForPagination(Collection<String> ids, String state, boolean isEnabled, String searchRegex, Pageable pageable);
7270

71+
Mono<Long> countUsersByIdsAndSearchName(Collection<String> ids, String state, boolean isEnabled, String searchRegex);
72+
}

server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserServiceImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.lowcoder.sdk.util.HashUtils;
3737
import org.lowcoder.sdk.util.LocaleUtils;
3838
import org.springframework.dao.DuplicateKeyException;
39+
import org.springframework.data.domain.Pageable;
3940
import org.springframework.http.codec.multipart.Part;
4041
import org.springframework.stereotype.Service;
4142
import org.springframework.web.server.ServerWebExchange;
@@ -473,4 +474,13 @@ public Flux<User> findBySourceAndIds(String connectionSource, Collection<String>
473474
return repository.findByConnections_SourceAndConnections_RawIdIn(connectionSource, connectionSourceUuids);
474475
}
475476

477+
@Override
478+
public Flux<User> findUsersByIdsAndSearchNameForPagination(Collection<String> ids, String state, boolean isEnabled, String searchRegex, Pageable pageable) {
479+
return repository.findUsersByIdsAndSearchNameForPagination(ids, state, isEnabled, searchRegex, pageable);
480+
}
481+
482+
@Override
483+
public Mono<Long> countUsersByIdsAndSearchName(Collection<String> ids, String state, boolean isEnabled, String searchRegex) {
484+
return repository.countUsersByIdsAndSearchName(ids, state, isEnabled, searchRegex);
485+
}
476486
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.lowcoder.api.usermanagement;
22

33
import org.lowcoder.api.usermanagement.view.*;
4+
import org.lowcoder.api.usermanagement.view.OrgMemberListView;
45
import org.lowcoder.domain.group.model.Group;
56
import reactor.core.publisher.Mono;
67

@@ -24,4 +25,6 @@ public interface GroupApiService {
2425
Mono<Boolean> update(String groupId, UpdateGroupRequest updateGroupRequest);
2526

2627
Mono<Boolean> removeUser(String groupId, String userId);
28+
29+
Mono<OrgMemberListView> getPotentialGroupMembers(String groupId, String searchName, Integer pageNum, Integer pageSize);
2730
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupApiServiceImpl.java

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,31 @@
99
import static org.lowcoder.sdk.util.StreamUtils.collectList;
1010
import static org.lowcoder.sdk.util.StreamUtils.collectMap;
1111

12-
import java.util.List;
13-
import java.util.Map;
14-
import java.util.Objects;
12+
import java.util.*;
13+
import java.util.regex.Pattern;
1514
import java.util.stream.Collectors;
1615

1716
import com.github.f4b6a3.uuid.UuidCreator;
1817
import lombok.RequiredArgsConstructor;
1918
import org.apache.commons.lang3.tuple.Pair;
2019
import org.lowcoder.api.bizthreshold.AbstractBizThresholdChecker;
2120
import org.lowcoder.api.home.SessionUserService;
22-
import org.lowcoder.api.usermanagement.view.CreateGroupRequest;
23-
import org.lowcoder.api.usermanagement.view.GroupMemberAggregateView;
24-
import org.lowcoder.api.usermanagement.view.GroupMemberView;
25-
import org.lowcoder.api.usermanagement.view.GroupView;
26-
import org.lowcoder.api.usermanagement.view.UpdateGroupRequest;
27-
import org.lowcoder.api.usermanagement.view.UpdateRoleRequest;
21+
import org.lowcoder.api.usermanagement.view.*;
2822
import org.lowcoder.domain.group.model.Group;
2923
import org.lowcoder.domain.group.model.GroupMember;
24+
import org.lowcoder.domain.user.model.UserState;
25+
import org.lowcoder.api.usermanagement.view.OrgMemberListView;
3026
import org.lowcoder.domain.group.service.GroupMemberService;
3127
import org.lowcoder.domain.group.service.GroupService;
3228
import org.lowcoder.domain.organization.model.MemberRole;
3329
import org.lowcoder.domain.organization.model.OrgMember;
3430
import org.lowcoder.domain.organization.service.OrgMemberService;
35-
import org.lowcoder.domain.organization.service.OrganizationService;
3631
import org.lowcoder.domain.user.model.User;
3732
import org.lowcoder.domain.user.service.UserService;
3833
import org.lowcoder.infra.util.TupleUtils;
3934
import org.lowcoder.sdk.exception.BizError;
35+
import org.springframework.data.domain.Pageable;
36+
import org.springframework.data.domain.PageRequest;
4037
import org.springframework.stereotype.Service;
4138

4239
import reactor.core.publisher.Flux;
@@ -53,7 +50,6 @@ public class GroupApiServiceImpl implements GroupApiService {
5350
private final UserService userService;
5451
private final GroupService groupService;
5552
private final AbstractBizThresholdChecker bizThresholdChecker;
56-
private final OrganizationService organizationService;
5753
private final OrgMemberService orgMemberService;
5854

5955
@Override
@@ -311,4 +307,63 @@ public Mono<Boolean> removeUser(String groupId, String userId) {
311307
return groupMemberService.removeMember(groupId, userId);
312308
});
313309
}
310+
311+
@Override
312+
public Mono<OrgMemberListView> getPotentialGroupMembers(String groupId, String searchName, Integer pageNum, Integer pageSize) {
313+
return groupService.getById(groupId)
314+
.flatMap(group -> {
315+
String orgId = group.getOrganizationId();
316+
Mono<List<OrgMember>> orgMemberUserIdsMono = orgMemberService.getOrganizationMembers(orgId).collectList();
317+
Mono<List<GroupMember>> groupMemberUserIdsMono = groupMemberService.getGroupMembers(groupId);
318+
319+
return Mono.zip(orgMemberUserIdsMono, groupMemberUserIdsMono)
320+
.flatMap(tuple -> {
321+
List<OrgMember> orgMembers = tuple.getT1();
322+
List<GroupMember> groupMembers = tuple.getT2();
323+
324+
Set<String> groupMemberUserIds = groupMembers.stream()
325+
.map(GroupMember::getUserId)
326+
.collect(Collectors.toSet());
327+
328+
Collection<String> potentialUserIds = orgMembers.stream()
329+
.map(OrgMember::getUserId)
330+
.filter(uid -> !groupMemberUserIds.contains(uid))
331+
.collect(Collectors.toList());
332+
333+
if (potentialUserIds.isEmpty()) {
334+
return Mono.just(OrgMemberListView.builder()
335+
.members(List.of())
336+
.total(0)
337+
.pageNum(pageNum)
338+
.pageSize(pageSize)
339+
.build());
340+
}
341+
342+
Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
343+
String searchRegex = searchName != null && !searchName.isBlank() ? ".*" + Pattern.quote(searchName) + ".*" : ".*";
344+
345+
return userService.findUsersByIdsAndSearchNameForPagination(
346+
potentialUserIds, String.valueOf(UserState.ACTIVATED), true, searchRegex, pageable)
347+
.collectList()
348+
.zipWith(userService.countUsersByIdsAndSearchName(
349+
potentialUserIds, String.valueOf(UserState.ACTIVATED), true, searchRegex))
350+
.map(tupleUser -> {
351+
List<User> users = tupleUser.getT1();
352+
long total = tupleUser.getT2();
353+
List<OrgMemberListView.OrgMemberView> memberViews = users.stream()
354+
.map(u -> OrgMemberListView.OrgMemberView.builder()
355+
.userId(u.getId())
356+
.name(u.getName())
357+
.build())
358+
.collect(Collectors.toList());
359+
return OrgMemberListView.builder()
360+
.members(memberViews)
361+
.total((int) total)
362+
.pageNum(pageNum)
363+
.pageSize(pageSize)
364+
.build();
365+
});
366+
});
367+
});
368+
}
314369
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupController.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
import org.lowcoder.domain.organization.service.OrgMemberService;
2121
import org.lowcoder.sdk.exception.BizError;
2222
import org.springframework.beans.factory.annotation.Autowired;
23-
import org.springframework.web.bind.annotation.PathVariable;
24-
import org.springframework.web.bind.annotation.RequestBody;
25-
import org.springframework.web.bind.annotation.RequestParam;
26-
import org.springframework.web.bind.annotation.RestController;
23+
import org.springframework.web.bind.annotation.*;
24+
import org.lowcoder.api.usermanagement.view.OrgMemberListView;
2725

2826
import reactor.core.publisher.Mono;
2927
import reactor.util.function.Tuple2;
@@ -180,4 +178,15 @@ public Mono<ResponseView<Boolean>> removeUser(@PathVariable String groupId,
180178
.map(Tuple2::getT2)
181179
.map(ResponseView::success));
182180
}
181+
182+
@Override
183+
public Mono<ResponseView<OrgMemberListView>> searchPotentialGroupMembers(
184+
@PathVariable String groupId,
185+
@RequestParam(required = false) String searchName,
186+
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
187+
@RequestParam(required = false, defaultValue = "1000") Integer pageSize) {
188+
return gidService.convertGroupIdToObjectId(groupId).flatMap(id ->
189+
groupApiService.getPotentialGroupMembers(id, searchName, pageNum, pageSize)
190+
.map(ResponseView::success));
191+
}
183192
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/GroupEndpoints.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,19 @@ public Mono<ResponseView<Boolean>> updateRoleForMember(@RequestBody UpdateRoleRe
115115
@DeleteMapping("/{groupId}/remove")
116116
public Mono<ResponseView<Boolean>> removeUser(@PathVariable String groupId,
117117
@RequestParam String userId);
118+
119+
@Operation(
120+
tags = TAG_GROUP_MEMBERS,
121+
operationId = "searchPotentialGroupMembers",
122+
summary = "Search Potential Group Members",
123+
description = "Retrieve a list of users who are not currently members of the specified group within an organization."
124+
)
125+
126+
@GetMapping("/{groupId}/potential-members")
127+
public Mono<ResponseView<OrgMemberListView>> searchPotentialGroupMembers(
128+
@PathVariable String groupId,
129+
@RequestParam(required = false) String searchName,
130+
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
131+
@RequestParam(required = false, defaultValue = "1000") Integer pageSize
132+
);
118133
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrgApiService.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,5 @@ public interface OrgApiService {
5353
Mono<ConfigView> getOrganizationConfigs(String orgId);
5454

5555
Mono<Long> getApiUsageCount(String orgId, Boolean lastMonthOnly);
56-
57-
Mono<OrgMemberListView> getOrganizationMembersForSearch(String orgId, String searchMemberName, String searchGroupId, Integer pageNum, Integer pageSize);
5856
}
5957

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrgApiServiceImpl.java

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -90,78 +90,6 @@ public Mono<OrgMemberListView> getOrganizationMembers(String orgId, int page, in
9090
.then(getOrgMemberListView(orgId, page, count));
9191
}
9292

93-
// Update getOrgMemberListViewForSearch to filter by group membership
94-
private Mono<OrgMemberListView> getOrgMemberListViewForSearch(String orgId, String searchMemberName, String searchGroupId, Integer page, Integer pageSize) {
95-
return orgMemberService.getOrganizationMembers(orgId)
96-
.collectList()
97-
.flatMap(orgMembers -> {
98-
List<String> userIds = orgMembers.stream()
99-
.map(OrgMember::getUserId)
100-
.collect(Collectors.toList());
101-
Mono<Map<String, User>> users = userService.getByIds(userIds);
102-
103-
// If searchGroupId is provided, fetch group members
104-
Mono<Set<String>> groupUserIdsMono = StringUtils.isBlank(searchGroupId)
105-
? Mono.just(Collections.emptySet())
106-
: groupMemberService.getGroupMembers(searchGroupId)
107-
.map(list -> list.stream()
108-
.map(GroupMember::getUserId)
109-
.collect(Collectors.toSet()));
110-
111-
return Mono.zip(users, groupUserIdsMono)
112-
.map(tuple -> {
113-
Map<String, User> userMap = tuple.getT1();
114-
Set<String> groupUserIds = tuple.getT2();
115-
116-
var list = orgMembers.stream()
117-
.map(orgMember -> {
118-
User user = userMap.get(orgMember.getUserId());
119-
if (user == null) {
120-
log.warn("user {} not exist and will be removed from the result.", orgMember.getUserId());
121-
return null;
122-
}
123-
return buildOrgMemberView(user, orgMember);
124-
})
125-
.filter(Objects::nonNull)
126-
.filter(orgMemberView -> {
127-
// Filter by name
128-
boolean matchesName = StringUtils.isBlank(searchMemberName) ||
129-
StringUtils.containsIgnoreCase(orgMemberView.getName(), searchMemberName);
130-
131-
// Filter by group
132-
boolean matchesGroup = StringUtils.isBlank(searchGroupId) ||
133-
groupUserIds.contains(orgMemberView.getUserId());
134-
135-
return matchesName && matchesGroup;
136-
})
137-
.collect(Collectors.toList());
138-
var pageTotal = list.size();
139-
list = list.subList((page - 1) * pageSize, pageSize == 0 ? pageTotal : Math.min(page * pageSize, pageTotal));
140-
return Pair.of(list, pageTotal);
141-
});
142-
})
143-
.zipWith(sessionUserService.getVisitorOrgMemberCache())
144-
.map(tuple -> {
145-
List<OrgMemberView> memberViews = tuple.getT1().getLeft();
146-
var pageTotal = tuple.getT1().getRight();
147-
OrgMember orgMember = tuple.getT2();
148-
return OrgMemberListView.builder()
149-
.members(memberViews)
150-
.total(pageTotal)
151-
.pageNum(page)
152-
.pageSize(pageSize)
153-
.visitorRole(orgMember.getRole().getValue())
154-
.build();
155-
});
156-
}
157-
@Override
158-
public Mono<OrgMemberListView> getOrganizationMembersForSearch(String orgId, String searchMemberName, String searchGroupId, Integer page, Integer pageSize) {
159-
return sessionUserService.getVisitorId()
160-
.flatMap(visitorId -> orgMemberService.getOrgMember(orgId, visitorId))
161-
.switchIfEmpty(deferredError(BizError.NOT_AUTHORIZED, "NOT_AUTHORIZED"))
162-
.then(getOrgMemberListViewForSearch(orgId, searchMemberName, searchGroupId, page, pageSize));
163-
}
164-
16593
private Mono<OrgMemberListView> getOrgMemberListView(String orgId, int page, int count) {
16694
return orgMemberService.getOrganizationMembers(orgId)
16795
.collectList()

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationController.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,6 @@ public Mono<ResponseView<OrgMemberListView>> getOrgMembers(@PathVariable String
118118
orgApiService.getOrganizationMembers(id, pageNum, pageSize)
119119
.map(ResponseView::success));
120120
}
121-
@Override
122-
public Mono<ResponseView<OrgMemberListView>> getOrgMembersForSearch(@PathVariable String orgId,
123-
@PathVariable String searchMemberName,
124-
@PathVariable String searchGroupId,
125-
@RequestParam(required = false, defaultValue = "1") int pageNum,
126-
@RequestParam(required = false, defaultValue = "1000") int pageSize) {
127-
return gidService.convertOrganizationIdToObjectId(orgId).flatMap(id ->
128-
orgApiService.getOrganizationMembersForSearch(id, searchMemberName, searchGroupId, pageNum, pageSize)
129-
.map(ResponseView::success));
130-
}
131121

132122
@Override
133123
public Mono<ResponseView<Boolean>> updateRoleForMember(@RequestBody UpdateRoleRequest updateRoleRequest,

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationEndpoints.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,6 @@ public Mono<ResponseView<OrgMemberListView>> getOrgMembers(@PathVariable String
9898
@RequestParam(required = false, defaultValue = "1") int pageNum,
9999
@RequestParam(required = false, defaultValue = "1000") int pageSize);
100100

101-
@GetMapping("/{orgId}/{searchMemberName}/{searchGroupId}/members")
102-
public Mono<ResponseView<OrgMemberListView>> getOrgMembersForSearch(@PathVariable String orgId,
103-
@PathVariable String searchMemberName,
104-
@PathVariable String searchGroupId,
105-
@RequestParam(required = false, defaultValue = "1") int pageNum,
106-
@RequestParam(required = false, defaultValue = "1000") int pageSize);
107-
108101
@Operation(
109102
tags = TAG_ORGANIZATION_MEMBERS,
110103
operationId = "updateOrganizationMemberRole",

0 commit comments

Comments
 (0)