Skip to content

Commit d5d808a

Browse files
committed
switch to effectiveGroupUsers
1 parent f74a7ab commit d5d808a

File tree

6 files changed

+55
-43
lines changed

6 files changed

+55
-43
lines changed

backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import org.cryptomator.hub.entities.Group;
66
import org.cryptomator.hub.entities.User;
77

8+
import jakarta.inject.Inject;
9+
810
abstract sealed class AuthorityDto permits UserDto, GroupDto, MemberDto {
911

1012
public enum Type {
@@ -30,10 +32,13 @@ protected AuthorityDto(String id, Type type, String name, String pictureUrl) {
3032
this.pictureUrl = pictureUrl;
3133
}
3234

35+
@Inject
36+
static User.Repository userRepo; // Inject User Repository für die neue Berechnung
37+
3338
static AuthorityDto fromEntity(Authority a) {
3439
return switch (a) {
3540
case User u -> UserDto.justPublicInfo(u);
36-
case Group g -> new GroupDto(g.getId(), g.getName(), (int) g.getMemberCount());
41+
case Group g -> new GroupDto(g.getId(), g.getName(), (int) userRepo.getEffectiveGroupUsers(g.getId()).count());
3742
default -> throw new IllegalStateException("authority is not of type user or group");
3843
};
3944
}

backend/src/main/java/org/cryptomator/hub/api/AuthorityResource.java

+25-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import jakarta.ws.rs.QueryParam;
1010
import jakarta.ws.rs.core.MediaType;
1111
import org.cryptomator.hub.entities.Authority;
12+
import org.cryptomator.hub.entities.Group;
13+
import org.cryptomator.hub.entities.User;
1214
import org.eclipse.microprofile.openapi.annotations.Operation;
1315
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
1416
import org.jboss.resteasy.reactive.NoCache;
@@ -21,6 +23,9 @@ public class AuthorityResource {
2123

2224
@Inject
2325
Authority.Repository authorityRepo;
26+
27+
@Inject
28+
User.Repository userRepo; // UserRepo wird hier für die Gruppen-Mitgliederanzahl benötigt
2429

2530
@GET
2631
@Path("/search")
@@ -31,7 +36,7 @@ public class AuthorityResource {
3136
public List<AuthorityDto> search(@QueryParam("query") @NotBlank String query) {
3237
List<Authority> authorities = authorityRepo.byName(query).toList();
3338
return authorities.stream()
34-
.map(AuthorityDto::fromEntity)
39+
.map(this::convertToDto) // Neue Methode für die Konvertierung mit Member-Anzahl
3540
.toList();
3641
}
3742

@@ -43,7 +48,24 @@ public List<AuthorityDto> search(@QueryParam("query") @NotBlank String query) {
4348
@Operation(summary = "lists all authorities matching the given ids", description = "lists for each id in the list its corresponding authority. Ignores all id's where an authority cannot be found")
4449
@APIResponse(responseCode = "200")
4550
public List<AuthorityDto> getSome(@QueryParam("ids") List<String> authorityIds) {
46-
return authorityRepo.findAllInList(authorityIds).map(AuthorityDto::fromEntity).toList();
51+
return authorityRepo.findAllInList(authorityIds)
52+
.map(this::convertToDto) // Neue Methode wird hier genutzt
53+
.toList();
54+
}
55+
56+
/**
57+
* Konvertiert eine Authority-Entity in das passende DTO,
58+
* inklusive der Gruppen-Mitgliederanzahl für Gruppen.
59+
*/
60+
private AuthorityDto convertToDto(Authority a) {
61+
if (a instanceof User u) {
62+
return UserDto.justPublicInfo(u);
63+
} else if (a instanceof Group g) {
64+
int memberCount = (int) userRepo.getEffectiveGroupUsers(g.getId()).count();
65+
return new GroupDto(g.getId(), g.getName(), memberCount);
66+
} else {
67+
throw new IllegalStateException("authority is not of type user or group");
68+
}
4769
}
4870

49-
}
71+
}

backend/src/main/java/org/cryptomator/hub/api/GroupsResource.java

+1-14
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,7 @@ public class GroupsResource {
3333
@Operation(summary = "list all groups")
3434
public List<GroupDto> getAll() {
3535
List<Group> groups = groupRepo.findAll().list();
36-
return groups.stream().map(group -> {
37-
long memberCount = groupRepo.countMembers(group.getId());
38-
return new GroupDto(group.getId(), group.getName(), (int) memberCount);
39-
}).toList();
40-
}
41-
42-
@GET
43-
@Path("/{groupId}/memberCount")
44-
@RolesAllowed("user")
45-
@Produces(MediaType.APPLICATION_JSON)
46-
@Operation(summary = "Get member count of a group")
47-
public Response getMemberCount(@PathParam("groupId") String groupId) {
48-
long count = userRepo.getEffectiveGroupUsers(groupId).count();
49-
return Response.ok(Map.of("count", count)).build();
36+
return groups.stream().map(group -> GroupDto.fromEntity(group, groupRepo.countMembers(group.getId()))).toList();
5037
}
5138

5239
@GET

backend/src/main/java/org/cryptomator/hub/entities/Group.java

+10-11
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22

33
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
44
import jakarta.enterprise.context.ApplicationScoped;
5-
import jakarta.persistence.CascadeType;
6-
import jakarta.persistence.DiscriminatorValue;
7-
import jakarta.persistence.Entity;
8-
import jakarta.persistence.JoinColumn;
9-
import jakarta.persistence.JoinTable;
10-
import jakarta.persistence.ManyToMany;
11-
import jakarta.persistence.Table;
5+
import jakarta.inject.Inject;
6+
import jakarta.persistence.*;
127

138
import java.util.HashSet;
149
import java.util.Set;
@@ -18,13 +13,16 @@
1813
@DiscriminatorValue("GROUP")
1914
public class Group extends Authority {
2015

21-
@ManyToMany(cascade = {CascadeType.REMOVE})
16+
@ManyToMany(cascade = {CascadeType.REMOVE}, fetch = FetchType.LAZY)
2217
@JoinTable(name = "group_membership",
2318
joinColumns = @JoinColumn(name = "group_id", referencedColumnName = "id"),
2419
inverseJoinColumns = @JoinColumn(name = "member_id", referencedColumnName = "id")
2520
)
2621
private Set<Authority> members = new HashSet<>();
2722

23+
@Inject
24+
transient Repository groupRepo;
25+
2826
public Set<Authority> getMembers() {
2927
return members;
3028
}
@@ -33,16 +31,17 @@ public void setMembers(Set<Authority> members) {
3331
this.members = members;
3432
}
3533

34+
3635
public int getMemberCount() {
37-
return members != null ? members.size() : 0;
36+
return groupRepo.countMembers(this.getId()); // Verwende das injectete Repository
3837
}
3938

4039
@ApplicationScoped
4140
public static class Repository implements PanacheRepositoryBase<Group, String> {
4241

43-
public long countMembers(String groupId) {
42+
public int countMembers(String groupId) {
4443
return getEntityManager()
45-
.createQuery("SELECT COUNT(m) FROM Group g JOIN g.members m WHERE g.id = :groupId", Long.class)
44+
.createQuery("SELECT SIZE(g.members) FROM Group g WHERE g.id = :groupId", Integer.class)
4645
.setParameter("groupId", groupId)
4746
.getSingleResult();
4847
}

frontend/src/common/backend.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ export type GroupDto = {
8989

9090
class GroupService {
9191
public async getMemberCount(groupId: string): Promise<number> {
92-
return axiosAuth.get<{ count: number }>(`/groups/${groupId}/memberCount`)
93-
.then(response => response.data.count)
94-
.catch(error => rethrowAndConvertIfExpected(error, 404));
92+
return axiosAuth.get<UserDto[]>(`/groups/${groupId}/effective-members`)
93+
.then(response => response.data.length)
94+
.catch(() => 0);
9595
}
9696
}
9797

frontend/src/components/VaultDetails.vue

+10-11
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,7 @@ async function fetchOwnerData() {
308308
for (const member of fetchedMembers) {
309309
if (member.type === "GROUP") {
310310
try {
311-
const res = await backend.groups.getMemberCount(member.id);
312-
const count = typeof res === "number" ? res : (res as { count: number })?.count ?? 0;
311+
const count = await backend.groups.getMemberCount(member.id);
313312
members.value.set(member.id, { ...member, memberCount: count });
314313
} catch (error) {
315314
members.value.set(member.id, { ...member, memberCount: 0 });
@@ -339,15 +338,16 @@ async function fetchOwnerData() {
339338
}
340339
341340
const groupMemberCounts = computed(() => {
342-
const counts = new Map<string, number>();
343-
members.value.forEach((member) => {
344-
if (member.type === 'GROUP') {
345-
counts.set(member.id, member.memberCount ?? 0);
346-
}
347-
});
348-
return counts;
341+
const counts = new Map<string, number>();
342+
members.value.forEach((member) => {
343+
if (member.type === 'GROUP' && 'memberCount' in member) {
344+
counts.set(member.id, member.memberCount);
345+
}
346+
});
347+
return counts;
349348
});
350349
350+
351351
async function loadVaultKeys(vaultKeyJwe: string): Promise<VaultKeys> {
352352
const userKeys = await userdata.decryptUserKeysWithBrowser();
353353
return VaultKeys.decryptWithUserKey(vaultKeyJwe, userKeys.ecdhKeyPair.privateKey);
@@ -515,8 +515,7 @@ async function searchAuthority(query: string): Promise<(AuthorityDto & { memberC
515515
filtered.map(async authority => {
516516
if (authority.type === "GROUP") {
517517
try {
518-
const res = await backend.groups.getMemberCount(authority.id);
519-
const count = typeof res === "number" ? res : (res as { count: number })?.count ?? 0;
518+
const count = await backend.groups.getMemberCount(authority.id);
520519
return { ...authority, memberCount: count };
521520
} catch (error) {
522521
return { ...authority, memberCount: 0 };

0 commit comments

Comments
 (0)