Skip to content

Commit 096a7d2

Browse files
dragonpooludomikula
authored andcommitted
Feat: add API to list groups and users without permissions, with search and pagination support
1 parent 27ea453 commit 096a7d2

File tree

4 files changed

+120
-5
lines changed

4 files changed

+120
-5
lines changed

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import reactor.core.publisher.Flux;
1616
import reactor.core.publisher.Mono;
1717

18+
import java.util.List;
1819
import java.util.Set;
1920

2021
public interface ApplicationApiService {
@@ -66,4 +67,6 @@ Mono<Boolean> grantPermission(String applicationId,
6667
Mono<Boolean> setApplicationAsAgencyProfile(String applicationId, boolean agencyProfile);
6768

6869
Mono<Application> updateSlug(String applicationId, String slug);
70+
71+
Mono<List<Object>> getGroupsOrMembersWithoutPermissions(String appId);
6972
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiServiceImpl.java

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,22 @@
2222
import org.lowcoder.api.home.UserHomeApiService;
2323
import org.lowcoder.api.permission.PermissionHelper;
2424
import org.lowcoder.api.permission.view.PermissionItemView;
25+
import org.lowcoder.api.usermanagement.GroupApiService;
26+
import org.lowcoder.api.usermanagement.OrgApiService;
2527
import org.lowcoder.api.usermanagement.OrgDevChecker;
28+
import org.lowcoder.api.usermanagement.view.GroupView;
29+
import org.lowcoder.api.usermanagement.view.OrgMemberListView;
2630
import org.lowcoder.domain.application.model.*;
2731
import org.lowcoder.domain.application.service.ApplicationHistorySnapshotService;
2832
import org.lowcoder.domain.application.service.ApplicationRecordService;
2933
import org.lowcoder.domain.application.service.ApplicationService;
3034
import org.lowcoder.domain.datasource.model.Datasource;
3135
import org.lowcoder.domain.datasource.service.DatasourceService;
3236
import org.lowcoder.domain.folder.service.FolderElementRelationService;
37+
import org.lowcoder.domain.group.model.Group;
38+
import org.lowcoder.domain.group.model.GroupMember;
3339
import org.lowcoder.domain.interaction.UserApplicationInteractionService;
40+
import org.lowcoder.domain.organization.model.OrgMember;
3441
import org.lowcoder.domain.organization.model.Organization;
3542
import org.lowcoder.domain.organization.service.OrgMemberService;
3643
import org.lowcoder.domain.organization.service.OrganizationService;
@@ -54,13 +61,11 @@
5461
import reactor.core.publisher.Flux;
5562
import reactor.core.publisher.Mono;
5663
import reactor.core.scheduler.Schedulers;
64+
import reactor.util.function.Tuple2;
5765

5866
import java.time.Duration;
5967
import java.time.Instant;
60-
import java.util.HashSet;
61-
import java.util.List;
62-
import java.util.Map;
63-
import java.util.Set;
68+
import java.util.*;
6469
import java.util.stream.Collectors;
6570

6671
import static org.lowcoder.domain.application.model.ApplicationStatus.NORMAL;
@@ -99,6 +104,8 @@ public class ApplicationApiServiceImpl implements ApplicationApiService {
99104
private final ApplicationHistorySnapshotService applicationHistorySnapshotService;
100105
private final ApplicationRecordService applicationRecordService;
101106
private final FolderElementRelationService folderElementRelationService;
107+
private final GroupApiService groupApiService;
108+
private final OrgApiService orgApiService;
102109

103110
@Override
104111
public Mono<ApplicationView> create(CreateApplicationRequest createApplicationRequest) {
@@ -743,4 +750,62 @@ private Mono<Void> checkDatasourcePermissions(Application application) {
743750
});
744751
});
745752
}
753+
754+
@Override
755+
public Mono<List<Object>> getGroupsOrMembersWithoutPermissions(String appId) {
756+
return applicationService.findById(appId)
757+
.flatMap(application -> {
758+
String orgId = application.getOrganizationId();
759+
Mono<List<ResourcePermission>> applicationPermissions = resourcePermissionService.getByApplicationId(application.getId()).cache();
760+
761+
Mono<List<PermissionItemView>> groupPermissionPairsMono = applicationPermissions
762+
.flatMap(permissionHelper::getGroupPermissions);
763+
764+
Mono<List<PermissionItemView>> userPermissionPairsMono = applicationPermissions
765+
.flatMap(permissionHelper::getUserPermissions);
766+
Mono<OrgMemberListView> orgMemberListViewMono = orgApiService.getOrganizationMembers(orgId, 1, 0);
767+
Mono<List<GroupView>> groupsViewMono = groupApiService.getGroups();
768+
769+
return Mono.zip(groupPermissionPairsMono, userPermissionPairsMono, orgMemberListViewMono, groupsViewMono)
770+
.map(tuple -> {
771+
List<PermissionItemView> groupPermissionPairs = tuple.getT1();
772+
List<PermissionItemView> userPermissionPairs = tuple.getT2();
773+
OrgMemberListView orgMemberListViews = tuple.getT3();
774+
List<GroupView> groupListViews = tuple.getT4();
775+
776+
Set<String> groupIdsWithPerm = groupPermissionPairs.stream()
777+
.map(PermissionItemView::getId)
778+
.collect(Collectors.toSet());
779+
780+
List<Map<String, Object>> filteredGroups = groupListViews.stream()
781+
.filter(group -> !groupIdsWithPerm.contains(group.getGroupId()))
782+
.map(group -> {
783+
Map<String, Object> map = new HashMap<>();
784+
map.put("type", "Group");
785+
map.put("data", group);
786+
return map;
787+
})
788+
.toList();
789+
790+
Set<String> userIdsWithPerm = userPermissionPairs.stream()
791+
.map(PermissionItemView::getId)
792+
.collect(Collectors.toSet());
793+
794+
List<Map<String, Object>> filteredMembers = orgMemberListViews.getMembers().stream()
795+
.filter(member -> !userIdsWithPerm.contains(member.getUserId()))
796+
.map(member -> {
797+
Map<String, Object> map = new HashMap<>();
798+
map.put("type", "User");
799+
map.put("data", member);
800+
return map;
801+
})
802+
.toList();
803+
804+
List<Object> result = new ArrayList<>();
805+
result.addAll(filteredGroups);
806+
result.addAll(filteredMembers);
807+
return result;
808+
});
809+
});
810+
}
746811
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import static org.lowcoder.plugin.api.event.LowcoderEvent.EventType.*;
2828
import static org.lowcoder.sdk.exception.BizError.INVALID_PARAMETER;
2929
import static org.lowcoder.sdk.util.ExceptionUtils.ofError;
30+
import reactor.core.publisher.Flux;
31+
import java.util.Map;
3032

3133
@RequiredArgsConstructor
3234
@RestController
@@ -298,5 +300,36 @@ public Mono<ResponseView<Boolean>> setApplicationAsAgencyProfile(@PathVariable S
298300
.map(ResponseView::success));
299301
}
300302

301-
303+
@Override
304+
public Mono<ResponseView<List<Object>>> getGroupsOrMembersWithoutPermissions(
305+
@PathVariable String applicationId,
306+
@RequestParam(required = false) String search,
307+
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
308+
@RequestParam(required = false, defaultValue = "1000") Integer pageSize) {
309+
310+
return gidService.convertLibraryQueryIdToObjectId(applicationId).flatMap(appId -> {
311+
var flx = applicationApiService.getGroupsOrMembersWithoutPermissions(appId)
312+
.flatMapMany(Flux::fromIterable)
313+
.filter(item -> {
314+
if (search == null || search.isBlank()) return true;
315+
if (!(item instanceof Map map)) return false;
316+
Object type = map.get("type");
317+
Object data = map.get("data");
318+
if ("Group".equals(type) && data instanceof org.lowcoder.api.usermanagement.view.GroupView group) {
319+
return group.getGroupName() != null && group.getGroupName().toLowerCase().contains(search.toLowerCase());
320+
}
321+
if ("User".equals(type) && data instanceof org.lowcoder.api.usermanagement.view.OrgMemberListView.OrgMemberView user) {
322+
return user.getName() != null && user.getName().toLowerCase().contains(search.toLowerCase());
323+
}
324+
return false;
325+
})
326+
.cache();
327+
var countMono = flx.count();
328+
var flux1 = flx.skip((long) (pageNum - 1) * pageSize);
329+
if (pageSize > 0) flux1 = flux1.take(pageSize);
330+
return flux1.collectList()
331+
.zipWith(countMono)
332+
.map(tuple -> PageResponseView.success(tuple.getT1(), pageNum, pageSize, Math.toIntExact(tuple.getT2())));
333+
});
334+
}
302335
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,20 @@ public Mono<ResponseView<Boolean>> grantPermission(
245245
@GetMapping("/{applicationId}/permissions")
246246
public Mono<ResponseView<ApplicationPermissionView>> getApplicationPermissions(@PathVariable String applicationId);
247247

248+
@Operation(
249+
tags = ApplicationEndpoints.TAG_APPLICATION_PERMISSIONS,
250+
operationId = "listGroupsOrMembersWithoutPermissions",
251+
summary = "Get groups or members without permissions",
252+
description = "Retrieve the groups or members of a specific Lowcoder Application identified by its ID that do not have permissions."
253+
)
254+
@GetMapping("/{applicationId}/get_groups_or_members_without_permissions")
255+
public Mono<ResponseView<List<Object>>> getGroupsOrMembersWithoutPermissions(
256+
@PathVariable String applicationId,
257+
@RequestParam(required = false) String search,
258+
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
259+
@RequestParam(required = false, defaultValue = "1000") Integer pageSize
260+
);
261+
248262
@Operation(
249263
tags = TAG_APPLICATION_MANAGEMENT,
250264
operationId = "setApplicationAsPublic",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy