1

refactor vorerst fertig, bugsuche

This commit is contained in:
Christoph
2020-04-14 17:13:29 +02:00
parent dbb60f30a7
commit cd445f12e8
34 changed files with 272 additions and 185 deletions

View File

@ -1,15 +1,10 @@
CREATE TABLE event CREATE TABLE event
( (
event_id INT PRIMARY KEY AUTO_INCREMENT, event_id INT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(36) NOT NULL, group_id VARCHAR(36) NOT NULL,
user_id VARCHAR(50) NOT NULL, group_version INT NOT NULL,
event_type VARCHAR(32) NOT NULL, exec_id VARCHAR(50) NOT NULL,
event_payload JSON target_id VARCHAR(50),
); event_type VARCHAR(32) NOT NULL,
event_payload VARCHAR(2500) NOT NULL
CREATE TABLE invite
(
invite_id INT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(36) NOT NULL,
invite_link VARCHAR(36) NOT NULL
); );

View File

@ -38,7 +38,7 @@ public class AddMemberEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.ADDMEMBER.toString(); return EventType.ADDMEMBER.toString();
} }
} }

View File

@ -33,7 +33,7 @@ public class CreateGroupEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.CREATEGROUP.toString(); return EventType.CREATEGROUP.toString();
} }

View File

@ -23,7 +23,7 @@ public class DestroyGroupEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.DESTROYGROUP.toString(); return EventType.DESTROYGROUP.toString();
} }
} }

View File

@ -52,10 +52,10 @@ public abstract class Event {
public void init(long version) { public void init(long version) {
if (this.version != 0) { if (this.version != 0) {
throw new BadArgumentException("Event wurde schon initialisiert. (" + getType() + ")"); throw new BadArgumentException("Event wurde schon initialisiert. (" + type() + ")");
} }
log.trace("Event wurde initialisiert. (" + getType() + "," + version + ")"); log.trace("Event wurde initialisiert. (" + type() + "," + version + ")");
this.version = version; this.version = version;
} }
@ -88,5 +88,5 @@ public abstract class Event {
protected abstract void applyEvent(Group group) throws EventException; protected abstract void applyEvent(Group group) throws EventException;
@JsonIgnore @JsonIgnore
public abstract String getType(); public abstract String type();
} }

View File

@ -27,7 +27,7 @@ public class KickMemberEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.KICKMEMBER.toString(); return EventType.KICKMEMBER.toString();
} }
} }

View File

@ -34,7 +34,7 @@ public class SetDescriptionEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.SETDESCRIPTION.toString(); return EventType.SETDESCRIPTION.toString();
} }
} }

View File

@ -31,7 +31,7 @@ public class SetInviteLinkEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.SETLINK.toString(); return EventType.SETLINK.toString();
} }
} }

View File

@ -32,7 +32,7 @@ public class SetLimitEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.SETLIMIT.toString(); return EventType.SETLIMIT.toString();
} }
} }

View File

@ -31,7 +31,7 @@ public class SetParentEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.SETPARENT.toString(); return EventType.SETPARENT.toString();
} }
} }

View File

@ -34,7 +34,7 @@ public class SetTitleEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.SETTITLE.toString(); return EventType.SETTITLE.toString();
} }

View File

@ -30,7 +30,7 @@ public class SetTypeEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.SETTYPE.toString(); return EventType.SETTYPE.toString();
} }
} }

View File

@ -33,7 +33,7 @@ public class UpdateRoleEvent extends Event {
} }
@Override @Override
public String getType() { public String type() {
return EventType.UPDATEROLE.toString(); return EventType.UPDATEROLE.toString();
} }

View File

@ -32,4 +32,10 @@ public final class CommonHelper {
public static boolean uuidIsEmpty(UUID uuid) { public static boolean uuidIsEmpty(UUID uuid) {
return "00000000-0000-0000-0000-000000000000".equals(uuid.toString()); return "00000000-0000-0000-0000-000000000000".equals(uuid.toString());
} }
public static List<UUID> stringsToUUID(List<String> groupids) {
return groupids.stream()
.map(UUID::fromString)
.collect(Collectors.toList());
}
} }

View File

@ -2,7 +2,6 @@ package mops.gruppen2.domain.model.group;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.BadArgumentException; import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.GroupFullException;
@ -11,6 +10,7 @@ import mops.gruppen2.domain.exception.LastAdminException;
import mops.gruppen2.domain.exception.NoAccessException; import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.exception.UserAlreadyExistsException; import mops.gruppen2.domain.exception.UserAlreadyExistsException;
import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.helper.ValidationHelper; import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.group.wrapper.Body; import mops.gruppen2.domain.model.group.wrapper.Body;
import mops.gruppen2.domain.model.group.wrapper.Description; import mops.gruppen2.domain.model.group.wrapper.Description;
@ -36,12 +36,10 @@ import java.util.stream.Collectors;
*/ */
@Log4j2 @Log4j2
@EqualsAndHashCode(onlyExplicitlyIncluded = true) @EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
public class Group { public class Group {
// Metainformationen // Metainformationen
@EqualsAndHashCode.Include @EqualsAndHashCode.Include
@ToString.Include
private UUID groupid; private UUID groupid;
@Getter @Getter
@ -53,7 +51,6 @@ public class Group {
private Link link = Link.RANDOM(); private Link link = Link.RANDOM();
@ToString.Include
private GroupMeta meta = GroupMeta.EMPTY(); private GroupMeta meta = GroupMeta.EMPTY();
private GroupOptions options = GroupOptions.DEFAULT(); private GroupOptions options = GroupOptions.DEFAULT();
@ -62,9 +59,9 @@ public class Group {
//private LocalDateTime age; //private LocalDateTime age;
// Inhalt // Inhalt
private Title title; private Title title = Title.EMPTY();
private Description description; private Description description = Description.EMPTY();
private Body body; private Body body;
@ -123,6 +120,12 @@ public class Group {
memberships.put(target, memberships.get(target).setRole(role)); memberships.put(target, memberships.get(target).setRole(role));
} }
public boolean memberHasRole(String target, Role role) {
ValidationHelper.throwIfNoMember(this, target);
return memberships.get(target).getRole() == role;
}
public boolean isMember(String target) { public boolean isMember(String target) {
return memberships.containsKey(target); return memberships.containsKey(target);
} }
@ -148,7 +151,7 @@ public class Group {
} }
public UUID getParent() { public UUID getParent() {
return parent.getGroupid(); return parent.getValue();
} }
public long getLimit() { public long getLimit() {
@ -191,6 +194,10 @@ public class Group {
return size() == 0; return size() == 0;
} }
public boolean exists() {
return groupid != null && !CommonHelper.uuidIsEmpty(groupid);
}
public boolean isPublic() { public boolean isPublic() {
return type == Type.PUBLIC; return type == Type.PUBLIC;
} }
@ -272,22 +279,35 @@ public class Group {
public void destroy(String userid) throws NoAccessException { public void destroy(String userid) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, userid); if (!isEmpty()) {
ValidationHelper.throwIfNoAdmin(this, userid);
}
groupid = null; groupid = null;
parent = null;
type = null; type = null;
title = null; parent = null;
description = null;
limit = null; limit = null;
memberships = null;
link = null; link = null;
meta = null; meta = null;
options = null; options = null;
title = null;
description = null;
body = null; body = null;
memberships = null;
} }
public String format() { public String format() {
return title + " " + description; return title + " " + description;
} }
@Override
public String toString() {
return "group("
+ (groupid == null ? "groupid: null" : groupid.toString())
+ ", "
+ (parent == null ? "parent: null" : parent.toString())
+ ", "
+ (meta == null ? "meta: null" : meta.toString())
+ ")";
}
} }

View File

@ -26,7 +26,7 @@ public class User {
@JsonProperty("familyname") @JsonProperty("familyname")
String familyname; String familyname;
@JsonProperty("mail") @JsonProperty("email")
String email; String email;
public User(KeycloakAuthenticationToken token) { public User(KeycloakAuthenticationToken token) {

View File

@ -1,5 +1,6 @@
package mops.gruppen2.domain.model.group.wrapper; package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value; import lombok.Value;
@ -18,4 +19,9 @@ public class Description {
public String toString() { public String toString() {
return value; return value;
} }
@JsonIgnore
public static Description EMPTY() {
return new Description("EMPTY");
}
} }

View File

@ -2,6 +2,7 @@ package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.ToString;
import lombok.Value; import lombok.Value;
import mops.gruppen2.domain.helper.CommonHelper; import mops.gruppen2.domain.helper.CommonHelper;
@ -12,15 +13,16 @@ import java.beans.ConstructorProperties;
import java.util.UUID; import java.util.UUID;
@Value @Value
@ToString
public class Parent { public class Parent {
@NotNull @NotNull
@JsonProperty("id") @JsonProperty("id")
UUID groupid; UUID value;
@ConstructorProperties("id") @ConstructorProperties("id")
public Parent(@NotBlank @Size(min = 36, max = 36) String parentid) { public Parent(@NotBlank @Size(min = 36, max = 36) String parentid) {
groupid = UUID.fromString(parentid); value = UUID.fromString(parentid);
} }
public static Parent EMPTY() { public static Parent EMPTY() {
@ -29,6 +31,6 @@ public class Parent {
@JsonIgnore @JsonIgnore
public boolean isEmpty() { public boolean isEmpty() {
return CommonHelper.uuidIsEmpty(groupid); return CommonHelper.uuidIsEmpty(value);
} }
} }

View File

@ -1,5 +1,6 @@
package mops.gruppen2.domain.model.group.wrapper; package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value; import lombok.Value;
@ -18,4 +19,9 @@ public class Title {
public String toString() { public String toString() {
return value; return value;
} }
@JsonIgnore
public static Title EMPTY() {
return new Title("EMPTY");
}
} }

View File

@ -8,14 +8,18 @@ import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.EventType; import mops.gruppen2.domain.event.EventType;
import mops.gruppen2.domain.event.SetTypeEvent;
import mops.gruppen2.domain.exception.BadPayloadException; import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.domain.exception.InvalidInviteException; import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.helper.JsonHelper; import mops.gruppen2.domain.helper.JsonHelper;
import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO; import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -24,6 +28,7 @@ import java.util.stream.Collectors;
import static mops.gruppen2.domain.event.EventType.CREATEGROUP; import static mops.gruppen2.domain.event.EventType.CREATEGROUP;
import static mops.gruppen2.domain.event.EventType.DESTROYGROUP; import static mops.gruppen2.domain.event.EventType.DESTROYGROUP;
import static mops.gruppen2.domain.event.EventType.SETLINK; import static mops.gruppen2.domain.event.EventType.SETLINK;
import static mops.gruppen2.domain.event.EventType.SETTYPE;
import static mops.gruppen2.domain.helper.CommonHelper.eventTypesToString; import static mops.gruppen2.domain.helper.CommonHelper.eventTypesToString;
import static mops.gruppen2.domain.helper.CommonHelper.uuidsToString; import static mops.gruppen2.domain.helper.CommonHelper.uuidsToString;
@ -93,7 +98,7 @@ public class EventStoreService {
event.getVersion(), event.getVersion(),
event.getExec(), event.getExec(),
event.getTarget(), event.getTarget(),
event.getType(), event.type(),
payload); payload);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
log.error("Event ({}) konnte nicht serialisiert werden!", event, e); log.error("Event ({}) konnte nicht serialisiert werden!", event, e);
@ -155,13 +160,12 @@ public class EventStoreService {
* *
* @return Liste von neuen und alten Events * @return Liste von neuen und alten Events
*/ */
List<Event> findChangedGroupEvents(long status) { List<UUID> findChangedGroups(long status) {
List<String> changedGroupIds = eventStore.findGroupIdsWhereEventIdGreaterThanStatus(status); List<String> changedGroupIds = eventStore.findGroupIdsWhereEventIdGreaterThanStatus(status);
List<EventDTO> groupEventDTOS = eventStore.findEventDTOsByGroup(changedGroupIds);
log.debug("Seit Event {} haben sich {} Gruppen geändert!", status, changedGroupIds.size()); log.debug("Seit Event {} haben sich {} Gruppen geändert!", status, changedGroupIds.size());
return getEventsFromDTOs(groupEventDTOS); return CommonHelper.stringsToUUID(changedGroupIds);
} }
/** /**
@ -179,6 +183,30 @@ public class EventStoreService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public List<UUID> findPublicGroupIds() {
List<UUID> groups = findExistingGroupIds();
List<Event> typeEvents = findLatestEventsFromGroupsByType(SETTYPE);
typeEvents.removeIf(event -> ((SetTypeEvent) event).getType() == Type.PRIVATE);
typeEvents.removeIf(event -> !groups.contains(event.getGroupid()));
return typeEvents.stream()
.map(Event::getGroupid)
.collect(Collectors.toList());
}
public List<UUID> findLectureGroupIds() {
List<UUID> groups = findExistingGroupIds();
List<Event> typeEvents = findLatestEventsFromGroupsByType(SETTYPE);
typeEvents.removeIf(event -> ((SetTypeEvent) event).getType() != Type.LECTURE);
typeEvents.removeIf(event -> !groups.contains(event.getGroupid()));
return typeEvents.stream()
.map(Event::getGroupid)
.collect(Collectors.toList());
}
/** /**
* Liefert Gruppen-Ids von existierenden (ungelöschten) Gruppen, in welchen der User teilnimmt. * Liefert Gruppen-Ids von existierenden (ungelöschten) Gruppen, in welchen der User teilnimmt.
* *
@ -239,16 +267,16 @@ public class EventStoreService {
} }
List<Event> findEventsByType(String... types) { List<Event> findEventsByType(String... types) {
return getEventsFromDTOs(eventStore.findEventDTOsByType(types)); return getEventsFromDTOs(eventStore.findEventDTOsByType(Arrays.asList(types)));
} }
List<Event> findEventsByType(String type) { List<Event> findEventsByType(String type) {
return getEventsFromDTOs(eventStore.findEventDTOsByType(type)); return getEventsFromDTOs(eventStore.findEventDTOsByType(Collections.singletonList(type)));
} }
List<Event> findEventsByGroupAndType(List<UUID> groupIds, String... types) { List<Event> findEventsByGroupAndType(List<UUID> groupIds, String... types) {
return getEventsFromDTOs(eventStore.findEventDTOsByGroupAndType(uuidsToString(groupIds), return getEventsFromDTOs(eventStore.findEventDTOsByGroupAndType(uuidsToString(groupIds),
types)); Arrays.asList(types)));
} }
/** /**
@ -259,7 +287,7 @@ public class EventStoreService {
* @return Eine Liste von einem Add- oder DeleteUserEvent pro Gruppe * @return Eine Liste von einem Add- oder DeleteUserEvent pro Gruppe
*/ */
private List<Event> findLatestEventsFromGroupsByUser(String userid) { private List<Event> findLatestEventsFromGroupsByUser(String userid) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByUser(userid)); return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupTarget(userid));
} }
@ -271,6 +299,6 @@ public class EventStoreService {
* @return Eine Liste von einem Event pro Gruppe * @return Eine Liste von einem Event pro Gruppe
*/ */
private List<Event> findLatestEventsFromGroupsByType(EventType... types) { private List<Event> findLatestEventsFromGroupsByType(EventType... types) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(eventTypesToString(types))); return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(Arrays.asList(eventTypesToString(types))));
} }
} }

View File

@ -2,6 +2,7 @@ package mops.gruppen2.domain.service;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.event.AddMemberEvent; import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.DestroyGroupEvent; import mops.gruppen2.domain.event.DestroyGroupEvent;
@ -36,6 +37,7 @@ import java.util.UUID;
* Es werden übergebene Gruppen bearbeitet und dementsprechend Events erzeugt und gespeichert. * Es werden übergebene Gruppen bearbeitet und dementsprechend Events erzeugt und gespeichert.
*/ */
@Log4j2 @Log4j2
@TraceMethodCalls
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class GroupService { public class GroupService {
@ -166,11 +168,11 @@ public class GroupService {
* Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an. * Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist. * Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist.
*/ */
public void deleteUser(Group group, String exec, String target) { public void kickMember(Group group, String exec, String target) {
applyAndSave(group, new KickMemberEvent(group, exec, target)); applyAndSave(group, new KickMemberEvent(group, exec, target));
if (ValidationHelper.checkIfGroupEmpty(group)) { if (ValidationHelper.checkIfGroupEmpty(group)) {
deleteGroup(group, target); deleteGroup(group, exec);
} }
} }
@ -179,6 +181,10 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist. * Prüft, ob der Nutzer Admin ist.
*/ */
public void deleteGroup(Group group, String exec) { public void deleteGroup(Group group, String exec) {
if (!group.exists()) {
return;
}
applyAndSave(group, new DestroyGroupEvent(group, exec)); applyAndSave(group, new DestroyGroupEvent(group, exec));
} }
@ -188,6 +194,10 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
public void setTitle(Group group, String exec, Title title) { public void setTitle(Group group, String exec, Title title) {
if (group.getTitle().equals(title.getValue())) {
return;
}
applyAndSave(group, new SetTitleEvent(group, exec, title)); applyAndSave(group, new SetTitleEvent(group, exec, title));
} }
@ -197,6 +207,10 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
public void setDescription(Group group, String exec, Description description) { public void setDescription(Group group, String exec, Description description) {
if (group.getDescription().equals(description.getValue())) {
return;
}
applyAndSave(group, new SetDescriptionEvent(group, exec, description)); applyAndSave(group, new SetDescriptionEvent(group, exec, description));
} }
@ -206,6 +220,10 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
private void updateRole(Group group, String exec, String target, Role role) { private void updateRole(Group group, String exec, String target, Role role) {
if (group.memberHasRole(target, role)) {
return;
}
applyAndSave(group, new UpdateRoleEvent(group, exec, target, role)); applyAndSave(group, new UpdateRoleEvent(group, exec, target, role));
} }
@ -215,18 +233,34 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
public void setLimit(Group group, String exec, Limit userLimit) { public void setLimit(Group group, String exec, Limit userLimit) {
if (userLimit.getValue() == group.getLimit()) {
return;
}
applyAndSave(group, new SetLimitEvent(group, exec, userLimit)); applyAndSave(group, new SetLimitEvent(group, exec, userLimit));
} }
public void setParent(Group group, String exec, Parent parent) { public void setParent(Group group, String exec, Parent parent) {
if (parent.getValue() == group.getParent()) {
return;
}
applyAndSave(group, new SetParentEvent(group, exec, parent)); applyAndSave(group, new SetParentEvent(group, exec, parent));
} }
public void setLink(Group group, String exec, Link link) { public void setLink(Group group, String exec, Link link) {
if (group.getLink().equals(link.getValue())) {
return;
}
applyAndSave(group, new SetInviteLinkEvent(group, exec, link)); applyAndSave(group, new SetInviteLinkEvent(group, exec, link));
} }
private void setType(Group group, String exec, Type type) { private void setType(Group group, String exec, Type type) {
if (group.getType() == type) {
return;
}
applyAndSave(group, new SetTypeEvent(group, exec, type)); applyAndSave(group, new SetTypeEvent(group, exec, type));
} }

View File

@ -7,7 +7,6 @@ import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupNotFoundException; import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.domain.helper.CommonHelper; import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.Type;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -17,13 +16,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import static mops.gruppen2.domain.event.EventType.CREATEGROUP;
import static mops.gruppen2.domain.event.EventType.SETDESCRIPTION;
import static mops.gruppen2.domain.event.EventType.SETLIMIT;
import static mops.gruppen2.domain.event.EventType.SETTITLE;
import static mops.gruppen2.domain.helper.CommonHelper.eventTypesToString;
/** /**
* Liefert verschiedene Projektionen auf Gruppen. * Liefert verschiedene Projektionen auf Gruppen.
@ -40,23 +32,6 @@ public class ProjectionService {
// ################################## STATISCHE PROJEKTIONEN ################################# // ################################## STATISCHE PROJEKTIONEN #################################
/**
* Konstruiert Gruppen aus einer Liste von Events.
*
* @param events Liste an Events
*
* @return Liste an Projizierten Gruppen
*
* @throws EventException Projektionsfehler
*/
static List<Group> projectGroups(List<Event> events) throws EventException {
Map<UUID, Group> groupMap = new HashMap<>();
events.forEach(event -> event.apply(getOrCreateGroup(groupMap, event.getGroupid())));
return new ArrayList<>(groupMap.values());
}
/** /**
* Projiziert Events, geht aber davon aus, dass alle zu derselben Gruppe gehören. * Projiziert Events, geht aber davon aus, dass alle zu derselben Gruppe gehören.
* *
@ -66,7 +41,7 @@ public class ProjectionService {
* *
* @throws EventException Projektionsfehler, z.B. falls Events von verschiedenen Gruppen übergeben werden * @throws EventException Projektionsfehler, z.B. falls Events von verschiedenen Gruppen übergeben werden
*/ */
static Group projectSingleGroup(List<Event> events) throws EventException { private static Group projectGroupByEvents(List<Event> events) throws EventException {
if (events.isEmpty()) { if (events.isEmpty()) {
throw new GroupNotFoundException(ProjectionService.class.toString()); throw new GroupNotFoundException(ProjectionService.class.toString());
} }
@ -78,6 +53,23 @@ public class ProjectionService {
return group; return group;
} }
/**
* Konstruiert Gruppen aus einer Liste von Events.
*
* @param events Liste an Events
*
* @return Liste an Projizierten Gruppen
*
* @throws EventException Projektionsfehler
*/
private static List<Group> projectGroupsByEvents(List<Event> events) throws EventException {
Map<UUID, Group> groupMap = new HashMap<>();
events.forEach(event -> event.apply(getOrCreateGroup(groupMap, event.getGroupid())));
return new ArrayList<>(groupMap.values());
}
/** /**
* Gibt die Gruppe mit der richtigen Id aus der übergebenen Map wieder, existiert diese nicht * Gibt die Gruppe mit der richtigen Id aus der übergebenen Map wieder, existiert diese nicht
* wird die Gruppe erstellt und der Map hizugefügt. * wird die Gruppe erstellt und der Map hizugefügt.
@ -99,6 +91,41 @@ public class ProjectionService {
// ############################### PROJEKTIONEN MIT DATENBANK ################################ // ############################### PROJEKTIONEN MIT DATENBANK ################################
/**
* Gibt die Gruppe zurück, die zu der übergebenen Id passt.
* Enthält alle verfügbaren Informationen, also auch User (langsam).
* Gibt eine leere Gruppe zurück, falls die Id leer ist.
*
* @param groupId Die Id der gesuchten Gruppe
*
* @return Die gesuchte Gruppe
*
* @throws GroupNotFoundException Wenn die Gruppe nicht gefunden wird
*/
public Group projectGroupById(UUID groupId) throws GroupNotFoundException {
try {
List<Event> events = eventStoreService.findGroupEvents(groupId);
return projectGroupByEvents(events);
} catch (Exception e) {
log.error("Gruppe {} wurde nicht gefunden!", groupId.toString(), e);
throw new GroupNotFoundException(groupId + ": " + ProjectionService.class);
}
}
public Group projectParent(UUID parent) {
if (CommonHelper.uuidIsEmpty(parent)) {
return new Group();
}
return projectGroupById(parent);
}
public List<Group> projectGroupsByIds(List<UUID> groupids) {
List<Event> events = eventStoreService.findGroupEvents(groupids);
return projectGroupsByEvents(events);
}
/** /**
* Projiziert Gruppen, welche sich seit einer übergebenen eventId geändert haben. * Projiziert Gruppen, welche sich seit einer übergebenen eventId geändert haben.
* Die Gruppen werden dabei vollständig konstruiert. * Die Gruppen werden dabei vollständig konstruiert.
@ -107,10 +134,10 @@ public class ProjectionService {
* *
* @return Liste an Gruppen * @return Liste an Gruppen
*/ */
public List<Group> projectNewGroups(long status) { public List<Group> projectChangedGroups(long status) {
List<Event> events = eventStoreService.findChangedGroupEvents(status); List<UUID> changedids = eventStoreService.findChangedGroups(status);
return projectGroups(events); return projectGroupsByIds(changedids);
} }
/** /**
@ -124,23 +151,13 @@ public class ProjectionService {
*/ */
@Cacheable("groups") @Cacheable("groups")
public List<Group> projectPublicGroups() throws EventException { public List<Group> projectPublicGroups() throws EventException {
List<UUID> groupIds = eventStoreService.findExistingGroupIds(); List<UUID> groupIds = eventStoreService.findPublicGroupIds();
if (groupIds.isEmpty()) { if (groupIds.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds, return projectGroupsByIds(groupIds);
eventTypesToString(CREATEGROUP,
SETDESCRIPTION,
SETTITLE,
SETLIMIT));
List<Group> groups = projectGroups(events);
return groups.stream()
.filter(group -> group.getType() != Type.PRIVATE)
.collect(Collectors.toList());
} }
/** /**
@ -151,21 +168,13 @@ public class ProjectionService {
*/ */
@Cacheable("groups") @Cacheable("groups")
public List<Group> projectLectures() { public List<Group> projectLectures() {
List<UUID> groupIds = eventStoreService.findExistingGroupIds(); List<UUID> groupIds = eventStoreService.findLectureGroupIds();
if (groupIds.isEmpty()) { if (groupIds.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds, return projectGroupsByIds(groupIds);
eventTypesToString(CREATEGROUP,
SETTITLE));
List<Group> lectures = projectGroups(events);
return lectures.stream()
.filter(group -> group.getType() == Type.LECTURE)
.collect(Collectors.toList());
} }
/** /**
@ -184,41 +193,7 @@ public class ProjectionService {
return Collections.emptyList(); return Collections.emptyList();
} }
List<Event> groupEvents = eventStoreService.findEventsByGroupAndType(groupIds, return projectGroupsByIds(groupIds);
eventTypesToString(CREATEGROUP,
SETTITLE,
SETDESCRIPTION));
return projectGroups(groupEvents);
}
/**
* Gibt die Gruppe zurück, die zu der übergebenen Id passt.
* Enthält alle verfügbaren Informationen, also auch User (langsam).
* Gibt eine leere Gruppe zurück, falls die Id leer ist.
*
* @param groupId Die Id der gesuchten Gruppe
*
* @return Die gesuchte Gruppe
*
* @throws GroupNotFoundException Wenn die Gruppe nicht gefunden wird
*/
public Group projectSingleGroup(UUID groupId) throws GroupNotFoundException {
try {
List<Event> events = eventStoreService.findGroupEvents(groupId);
return projectSingleGroup(events);
} catch (Exception e) {
log.error("Gruppe {} wurde nicht gefunden!", groupId.toString(), e);
throw new GroupNotFoundException(groupId + ": " + ProjectionService.class);
}
}
public Group projectParent(UUID parent) {
if (CommonHelper.uuidIsEmpty(parent)) {
return new Group();
}
return projectSingleGroup(parent);
} }
/** /**
@ -234,7 +209,7 @@ public class ProjectionService {
} }
public Group projectGroupByLink(String link) { public Group projectGroupByLink(String link) {
return projectSingleGroup(eventStoreService.findGroupByLink(link)); return projectGroupById(eventStoreService.findGroupByLink(link));
} }
} }

View File

@ -32,9 +32,11 @@ public class SearchService {
* @throws EventException Projektionsfehler * @throws EventException Projektionsfehler
*/ */
@Cacheable("groups") @Cacheable("groups")
public List<Group> searchPublicGroups(String search, String userid) { public List<Group> searchPublicGroups(String search, String principal) {
List<Group> groups = projectionService.projectPublicGroups(); List<Group> groups = projectionService.projectPublicGroups();
projectionService.removeUserGroups(groups, userid); System.out.println(groups);
projectionService.removeUserGroups(groups, principal);
System.out.println(groups);
SortHelper.sortByGroupType(groups); SortHelper.sortByGroupType(groups);
if (search.isEmpty()) { if (search.isEmpty()) {

View File

@ -13,10 +13,10 @@ public interface EventRepository extends CrudRepository<EventDTO, Long> {
// ####################################### GROUP IDs ######################################### // ####################################### GROUP IDs #########################################
@Query("SELECT DISTINCT group_id FROM event" /*@Query("SELECT DISTINCT group_id FROM event"
+ " WHERE user_id = :userId AND event_type = :type") + " WHERE user_id = :userId AND event_type = :type")
List<String> findGroupIdsByUserAndType(@Param("userId") String userId, List<String> findGroupIdsByUserAndType(@Param("userId") String userId,
@Param("type") String type); @Param("type") String type);*/
@Query("SELECT DISTINCT group_id FROM event" @Query("SELECT DISTINCT group_id FROM event"
+ " WHERE event_id > :status") + " WHERE event_id > :status")
@ -28,33 +28,33 @@ public interface EventRepository extends CrudRepository<EventDTO, Long> {
+ " WHERE group_id IN (:groupIds) ") + " WHERE group_id IN (:groupIds) ")
List<EventDTO> findEventDTOsByGroup(@Param("groupIds") List<String> groupIds); List<EventDTO> findEventDTOsByGroup(@Param("groupIds") List<String> groupIds);
@Query("SELECT * FROM event" /*@Query("SELECT * FROM event"
+ " WHERE group_id IN (:userIds) ") + " WHERE group_id IN (:userIds) ")
List<EventDTO> findEventDTOsByUser(@Param("groupIds") String... userIds); List<EventDTO> findEventDTOsByUser(@Param("groupIds") String... userIds);*/
@Query("SELECT * FROM event" @Query("SELECT * FROM event"
+ " WHERE event_type IN (:types)") + " WHERE event_type IN (:types)")
List<EventDTO> findEventDTOsByType(@Param("types") String... types); List<EventDTO> findEventDTOsByType(@Param("types") List<String> types);
@Query("SELECT * FROM event" @Query("SELECT * FROM event"
+ " WHERE event_type IN (:types) AND group_id IN (:groupIds)") + " WHERE event_type IN (:types) AND group_id IN (:groupIds)")
List<EventDTO> findEventDTOsByGroupAndType(@Param("groupIds") List<String> groupIds, List<EventDTO> findEventDTOsByGroupAndType(@Param("groupIds") List<String> groupIds,
@Param("types") String... types); @Param("types") List<String> types);
@Query("SELECT * FROM event" /*@Query("SELECT * FROM event"
+ " WHERE event_type IN (:types) AND user_id = :userId") + " WHERE event_type IN (:types) AND user_id = :userId")
List<EventDTO> findEventDTOsByUserAndType(@Param("userId") String userId, List<EventDTO> findEventDTOsByUserAndType(@Param("userId") String userId,
@Param("types") String... types); @Param("types") String... types);*/
// ################################ LATEST EVENT DTOs ######################################## // ################################ LATEST EVENT DTOs ########################################
@Query("WITH ranked_events AS (" @Query("WITH ranked_events AS ("
+ "SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY event_id DESC) AS rn" + "SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY event_id DESC) AS rn"
+ " FROM event" + " FROM event"
+ " WHERE user_id = :userId AND event_type IN ('ADDMEMBER', 'KICKMEMBER')" + " WHERE target_id = :userId AND event_type IN ('ADDMEMBER', 'KICKMEMBER')"
+ ")" + ")"
+ "SELECT * FROM ranked_events WHERE rn = 1;") + "SELECT * FROM ranked_events WHERE rn = 1;")
List<EventDTO> findLatestEventDTOsPartitionedByGroupByUser(@Param("userId") String userId); List<EventDTO> findLatestEventDTOsPartitionedByGroupTarget(@Param("userId") String target);
@Query("WITH ranked_events AS (" @Query("WITH ranked_events AS ("
+ "SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY event_id DESC) AS rn" + "SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY event_id DESC) AS rn"
@ -62,10 +62,12 @@ public interface EventRepository extends CrudRepository<EventDTO, Long> {
+ " WHERE event_type IN (:types)" + " WHERE event_type IN (:types)"
+ ")" + ")"
+ "SELECT * FROM ranked_events WHERE rn = 1;") + "SELECT * FROM ranked_events WHERE rn = 1;")
List<EventDTO> findLatestEventDTOsPartitionedByGroupByType(@Param("types") String... types); List<EventDTO> findLatestEventDTOsPartitionedByGroupByType(@Param("types") List<String> types);
// ######################################### COUNT ########################################### // ######################################### COUNT ###########################################
@Query("SELECT MAX(event_id) FROM event") @Query("SELECT MAX(event_id) FROM event")
Long findMaxEventId(); Long findMaxEventId();
} }

View File

@ -16,8 +16,8 @@ public class EventDTO {
String group_id; String group_id;
long group_version; long group_version;
String exec_user_id; String exec_id;
String target_user_id; String target_id;
String event_type; String event_type;
String event_payload; String event_payload;

View File

@ -47,7 +47,7 @@ public class APIController {
@PathVariable("id") long eventId) { @PathVariable("id") long eventId) {
return APIHelper.wrap(eventStoreService.findMaxEventId(), return APIHelper.wrap(eventStoreService.findMaxEventId(),
projectionService.projectNewGroups(eventId)); projectionService.projectChangedGroups(eventId));
} }
/** /**
@ -71,7 +71,7 @@ public class APIController {
public Group getApiGroup(@ApiParam("Gruppen-Id der gefordeten Gruppe") public Group getApiGroup(@ApiParam("Gruppen-Id der gefordeten Gruppe")
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
return projectionService.projectSingleGroup(UUID.fromString(groupId)); return projectionService.projectGroupById(UUID.fromString(groupId));
} }
} }

View File

@ -46,7 +46,7 @@ public class GroupDetailsController {
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
// Parent Badge // Parent Badge
Group parent = projectionService.projectParent(group.getParent()); Group parent = projectionService.projectParent(group.getParent());
@ -69,7 +69,7 @@ public class GroupDetailsController {
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
if (ValidationHelper.checkIfMember(group, principal)) { if (ValidationHelper.checkIfMember(group, principal)) {
return "redirect:/gruppen2/details/" + groupId; return "redirect:/gruppen2/details/" + groupId;
@ -87,9 +87,9 @@ public class GroupDetailsController {
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.deleteUser(group, principal, principal); groupService.kickMember(group, principal, principal);
return "redirect:/gruppen2"; return "redirect:/gruppen2";
} }
@ -102,7 +102,7 @@ public class GroupDetailsController {
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
// Invite Link // Invite Link
String actualURL = request.getRequestURL().toString(); String actualURL = request.getRequestURL().toString();
@ -126,7 +126,7 @@ public class GroupDetailsController {
@Valid Description description) { @Valid Description description) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.setTitle(group, principal, title); groupService.setTitle(group, principal, title);
groupService.setDescription(group, principal, description); groupService.setDescription(group, principal, description);
@ -141,7 +141,7 @@ public class GroupDetailsController {
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@Valid Limit limit) { @Valid Limit limit) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.setLimit(group, principal, limit); groupService.setLimit(group, principal, limit);
@ -156,7 +156,7 @@ public class GroupDetailsController {
@RequestParam(value = "file", required = false) MultipartFile file) { @RequestParam(value = "file", required = false) MultipartFile file) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file)); groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file));
@ -171,7 +171,7 @@ public class GroupDetailsController {
@PathVariable("userid") String target) { @PathVariable("userid") String target) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
ValidationHelper.throwIfNoAdmin(group, principal); ValidationHelper.throwIfNoAdmin(group, principal);
@ -193,13 +193,13 @@ public class GroupDetailsController {
@PathVariable("userid") String target) { @PathVariable("userid") String target) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectGroupById(UUID.fromString(groupId));
ValidationHelper.throwIfNoAdmin(group, principal); ValidationHelper.throwIfNoAdmin(group, principal);
// Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen) // Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen)
if (!principal.equals(target)) { if (!principal.equals(target)) {
groupService.deleteUser(group, principal, target); groupService.kickMember(group, principal, target);
} }
return "redirect:/gruppen2/details/" + groupId + "/edit"; return "redirect:/gruppen2/details/" + groupId + "/edit";
@ -212,7 +212,7 @@ public class GroupDetailsController {
@PathVariable("id") String groupid) { @PathVariable("id") String groupid) {
String principal = token.getName(); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupid)); Group group = projectionService.projectGroupById(UUID.fromString(groupid));
groupService.deleteGroup(group, principal); groupService.deleteGroup(group, principal);

View File

@ -2,11 +2,11 @@ DROP TABLE IF EXISTS event;
CREATE TABLE event CREATE TABLE event
( (
event_id INT PRIMARY KEY AUTO_INCREMENT, event_id INT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(36) NOT NULL, group_id VARCHAR(36) NOT NULL,
group_version INT NOT NULL, group_version INT NOT NULL,
exec_user_id VARCHAR(50) NOT NULL, exec_id VARCHAR(50) NOT NULL,
target_user_id VARCHAR(50), target_id VARCHAR(50),
event_type VARCHAR(32) NOT NULL, event_type VARCHAR(32) NOT NULL,
event_payload VARCHAR(2500) NOT NULL event_payload VARCHAR(2500) NOT NULL
); );

View File

@ -44,7 +44,7 @@
</div> </div>
<!--Bearbeiten-Button--> <!--Bearbeiten-Button-->
<div class="mb-2" th:if="${group.isAdmin(user.getId())}"> <div class="mb-2" th:if="${group.isAdmin(principal.getId())}">
<form method="get" <form method="get"
th:action="@{/gruppen2/details/{id}/edit(id=${group.getId()})}"> th:action="@{/gruppen2/details/{id}/edit(id=${group.getId()})}">
<button class="btn btn-secondary btn-block">Gruppe verwalten</button> <button class="btn btn-secondary btn-block">Gruppe verwalten</button>
@ -56,7 +56,7 @@
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item d-flex justify-content-between" <li class="list-group-item d-flex justify-content-between"
th:each="member : ${group.getMembers()}"> th:each="member : ${group.getMembers()}">
<span th:text="${member}"></span> <span th:text="${member.format()}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span> <span th:replace="~{fragments/groups :: userbadges}"></span>
</li> </li>
</ul> </ul>

View File

@ -98,7 +98,7 @@
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item d-flex justify-content-between" th:each="member : ${group.getMembers()}"> <li class="list-group-item d-flex justify-content-between" th:each="member : ${group.getMembers()}">
<div> <div>
<span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span> <span th:text="${member.format()}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span> <span th:replace="~{fragments/groups :: userbadges}"></span>
</div> </div>

View File

@ -1,7 +1,12 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<!--suppress ALL -->
<html lang="de" xmlns:th="http://www.thymeleaf.org"> <html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--/*@thymesVar id="parent" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="group" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="member" type="mops.gruppen2.domain.model.group.User"*/-->
<!--/*@thymesVar id="account" type="mops.gruppen2.domain.Account"*/-->
<!--/*@thymesVar id="lectures" type="java.util.List<mops.gruppen2.domain.model.group.Group>"*/-->
<!--Meta--> <!--Meta-->
<th:block th:fragment="meta"> <th:block th:fragment="meta">
<!--Gruppentitel--> <!--Gruppentitel-->
@ -14,6 +19,7 @@
th:value="${group?.getTitle()}" required> th:value="${group?.getTitle()}" required>
</div> </div>
<!--Gruppenbeschreibung--> <!--Gruppenbeschreibung-->
<div class="input-group" <div class="input-group"
title="Eine kurze Gruppenbeschreibung zwischen 4 und 512 Zeichen. Die Beschreibung ist öffentlich."> title="Eine kurze Gruppenbeschreibung zwischen 4 und 512 Zeichen. Die Beschreibung ist öffentlich.">

View File

@ -1,6 +1,10 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="de" xmlns:th="http://www.thymeleaf.org"> <html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--/*@thymesVar id="parent" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="group" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="member" type="mops.gruppen2.domain.model.group.User"*/-->
<!--Grouptype Badges--> <!--Grouptype Badges-->
<th:block th:fragment="badges"> <th:block th:fragment="badges">
<span class="badge badge-pill private" <span class="badge badge-pill private"
@ -14,7 +18,7 @@
th:if='${group.isLecture()}'>Veranstaltung</span> th:if='${group.isLecture()}'>Veranstaltung</span>
<span class="badge badge-pill parent" <span class="badge badge-pill parent"
th:if="${parent != null && parent?.getTitle() != null && parent?.getTitle() != ''}" th:if="${parent?.exists()}"
th:title="${'Die Gruppe gehört zur Veranstaltung ' + parent.getTitle() + '.'}" th:title="${'Die Gruppe gehört zur Veranstaltung ' + parent.getTitle() + '.'}"
th:text="${parent.getTitle()}">Parent</span> th:text="${parent.getTitle()}">Parent</span>
@ -27,7 +31,7 @@
<!--User Badges--> <!--User Badges-->
<th:block th:fragment="userbadges"> <th:block th:fragment="userbadges">
<span class="badge badge-success align-self-start ml-2" <span class="badge badge-success align-self-start ml-2"
th:if="${group.isAdmin(member.getUserid())}">Admin</span> th:if="${group.isAdmin(member.getId())}">Admin</span>
</th:block> </th:block>
<th:block th:fragment="groupcontent"> <th:block th:fragment="groupcontent">
@ -56,7 +60,7 @@
</div> </div>
<div class="row"> <div class="row">
<form method="post" th:action="@{/gruppen2/details/{id}/join(id = ${group.getGroupId()})}" <form method="post" th:action="@{/gruppen2/details/{id}/join(id = ${group.getId()})}"
th:unless="${group.isFull()}"> th:unless="${group.isFull()}">
<button class="btn btn-success" type="submit">Gruppe beitreten.</button> <button class="btn btn-success" type="submit">Gruppe beitreten.</button>
</form> </form>

View File

@ -21,6 +21,7 @@ class ControllerServiceTest {
Account account; Account account;
Account account2; Account account2;
Account account3; Account account3;
GroupEmpty
@Autowired @Autowired
EventStoreService eventStoreService; EventStoreService eventStoreService;
@Autowired @Autowired

View File

@ -70,7 +70,7 @@ class GroupServiceTest {
void rightClassForSuccessfulGroup() { void rightClassForSuccessfulGroup() {
List<Event> eventList = completePrivateGroup(1); List<Event> eventList = completePrivateGroup(1);
List<Group> groups = ProjectionService.projectGroups(eventList); List<Group> groups = ProjectionService.projectGroupsByEvents(eventList);
assertThat(groups.get(0)).isInstanceOf(Group.class); assertThat(groups.get(0)).isInstanceOf(Group.class);
} }
@ -79,7 +79,7 @@ class GroupServiceTest {
void projectEventList_SingleGroup() { void projectEventList_SingleGroup() {
List<Event> eventList = completePrivateGroup(5); List<Event> eventList = completePrivateGroup(5);
List<Group> groups = ProjectionService.projectGroups(eventList); List<Group> groups = ProjectionService.projectGroupsByEvents(eventList);
assertThat(groups).hasSize(1); assertThat(groups).hasSize(1);
assertThat(groups.get(0).getMemberships()).hasSize(5); assertThat(groups.get(0).getMemberships()).hasSize(5);
@ -92,7 +92,7 @@ class GroupServiceTest {
List<Event> eventList = completePrivateGroups(10, 2); List<Event> eventList = completePrivateGroups(10, 2);
eventList.addAll(completePublicGroups(10, 5)); eventList.addAll(completePublicGroups(10, 5));
List<Group> groups = ProjectionService.projectGroups(eventList); List<Group> groups = ProjectionService.projectGroupsByEvents(eventList);
assertThat(groups).hasSize(20); assertThat(groups).hasSize(20);
assertThat(groups.stream().map(group -> group.getMemberships().size()).reduce(Integer::sum).get()).isEqualTo(70); assertThat(groups.stream().map(group -> group.getMemberships().size()).reduce(Integer::sum).get()).isEqualTo(70);