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
(
event_id INT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(36) NOT NULL,
user_id VARCHAR(50) NOT NULL,
event_type VARCHAR(32) NOT NULL,
event_payload JSON
);
CREATE TABLE invite
(
invite_id INT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(36) NOT NULL,
invite_link VARCHAR(36) NOT NULL
group_id VARCHAR(36) NOT NULL,
group_version INT NOT NULL,
exec_id VARCHAR(50) NOT NULL,
target_id VARCHAR(50),
event_type VARCHAR(32) NOT NULL,
event_payload VARCHAR(2500) NOT NULL
);

View File

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

View File

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

View File

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

View File

@ -52,10 +52,10 @@ public abstract class Event {
public void init(long version) {
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;
}
@ -88,5 +88,5 @@ public abstract class Event {
protected abstract void applyEvent(Group group) throws EventException;
@JsonIgnore
public abstract String getType();
public abstract String type();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,4 +32,10 @@ public final class CommonHelper {
public static boolean uuidIsEmpty(UUID uuid) {
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.Getter;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.BadArgumentException;
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.UserAlreadyExistsException;
import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.group.wrapper.Body;
import mops.gruppen2.domain.model.group.wrapper.Description;
@ -36,12 +36,10 @@ import java.util.stream.Collectors;
*/
@Log4j2
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString(onlyExplicitlyIncluded = true)
public class Group {
// Metainformationen
@EqualsAndHashCode.Include
@ToString.Include
private UUID groupid;
@Getter
@ -53,7 +51,6 @@ public class Group {
private Link link = Link.RANDOM();
@ToString.Include
private GroupMeta meta = GroupMeta.EMPTY();
private GroupOptions options = GroupOptions.DEFAULT();
@ -62,9 +59,9 @@ public class Group {
//private LocalDateTime age;
// Inhalt
private Title title;
private Title title = Title.EMPTY();
private Description description;
private Description description = Description.EMPTY();
private Body body;
@ -123,6 +120,12 @@ public class Group {
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) {
return memberships.containsKey(target);
}
@ -148,7 +151,7 @@ public class Group {
}
public UUID getParent() {
return parent.getGroupid();
return parent.getValue();
}
public long getLimit() {
@ -191,6 +194,10 @@ public class Group {
return size() == 0;
}
public boolean exists() {
return groupid != null && !CommonHelper.uuidIsEmpty(groupid);
}
public boolean isPublic() {
return type == Type.PUBLIC;
}
@ -272,22 +279,35 @@ public class Group {
public void destroy(String userid) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, userid);
if (!isEmpty()) {
ValidationHelper.throwIfNoAdmin(this, userid);
}
groupid = null;
parent = null;
type = null;
title = null;
description = null;
parent = null;
limit = null;
memberships = null;
link = null;
meta = null;
options = null;
title = null;
description = null;
body = null;
memberships = null;
}
public String format() {
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")
String familyname;
@JsonProperty("mail")
@JsonProperty("email")
String email;
public User(KeycloakAuthenticationToken token) {

View File

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

View File

@ -1,5 +1,6 @@
package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;
@ -18,4 +19,9 @@ public class Title {
public String toString() {
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.Event;
import mops.gruppen2.domain.event.EventType;
import mops.gruppen2.domain.event.SetTypeEvent;
import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.helper.JsonHelper;
import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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.DESTROYGROUP;
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.uuidsToString;
@ -93,7 +98,7 @@ public class EventStoreService {
event.getVersion(),
event.getExec(),
event.getTarget(),
event.getType(),
event.type(),
payload);
} catch (JsonProcessingException e) {
log.error("Event ({}) konnte nicht serialisiert werden!", event, e);
@ -155,13 +160,12 @@ public class EventStoreService {
*
* @return Liste von neuen und alten Events
*/
List<Event> findChangedGroupEvents(long status) {
List<UUID> findChangedGroups(long 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());
return getEventsFromDTOs(groupEventDTOS);
return CommonHelper.stringsToUUID(changedGroupIds);
}
/**
@ -179,6 +183,30 @@ public class EventStoreService {
.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.
*
@ -239,16 +267,16 @@ public class EventStoreService {
}
List<Event> findEventsByType(String... types) {
return getEventsFromDTOs(eventStore.findEventDTOsByType(types));
return getEventsFromDTOs(eventStore.findEventDTOsByType(Arrays.asList(types)));
}
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) {
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
*/
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
*/
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.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent;
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.
*/
@Log4j2
@TraceMethodCalls
@RequiredArgsConstructor
@Service
public class GroupService {
@ -166,11 +168,11 @@ public class GroupService {
* 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.
*/
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));
if (ValidationHelper.checkIfGroupEmpty(group)) {
deleteGroup(group, target);
deleteGroup(group, exec);
}
}
@ -179,6 +181,10 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist.
*/
public void deleteGroup(Group group, String exec) {
if (!group.exists()) {
return;
}
applyAndSave(group, new DestroyGroupEvent(group, exec));
}
@ -188,6 +194,10 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt.
*/
public void setTitle(Group group, String exec, Title title) {
if (group.getTitle().equals(title.getValue())) {
return;
}
applyAndSave(group, new SetTitleEvent(group, exec, title));
}
@ -197,6 +207,10 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt.
*/
public void setDescription(Group group, String exec, Description description) {
if (group.getDescription().equals(description.getValue())) {
return;
}
applyAndSave(group, new SetDescriptionEvent(group, exec, description));
}
@ -206,6 +220,10 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt.
*/
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));
}
@ -215,18 +233,34 @@ public class GroupService {
* Bei keiner Änderung wird nichts erzeugt.
*/
public void setLimit(Group group, String exec, Limit userLimit) {
if (userLimit.getValue() == group.getLimit()) {
return;
}
applyAndSave(group, new SetLimitEvent(group, exec, userLimit));
}
public void setParent(Group group, String exec, Parent parent) {
if (parent.getValue() == group.getParent()) {
return;
}
applyAndSave(group, new SetParentEvent(group, exec, parent));
}
public void setLink(Group group, String exec, Link link) {
if (group.getLink().equals(link.getValue())) {
return;
}
applyAndSave(group, new SetInviteLinkEvent(group, exec, link));
}
private void setType(Group group, String exec, Type type) {
if (group.getType() == type) {
return;
}
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.helper.CommonHelper;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.Type;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@ -17,13 +16,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.
@ -40,23 +32,6 @@ public class ProjectionService {
// ################################## 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.
*
@ -66,7 +41,7 @@ public class ProjectionService {
*
* @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()) {
throw new GroupNotFoundException(ProjectionService.class.toString());
}
@ -78,6 +53,23 @@ public class ProjectionService {
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
* wird die Gruppe erstellt und der Map hizugefügt.
@ -99,6 +91,41 @@ public class ProjectionService {
// ############################### 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.
* Die Gruppen werden dabei vollständig konstruiert.
@ -107,10 +134,10 @@ public class ProjectionService {
*
* @return Liste an Gruppen
*/
public List<Group> projectNewGroups(long status) {
List<Event> events = eventStoreService.findChangedGroupEvents(status);
public List<Group> projectChangedGroups(long status) {
List<UUID> changedids = eventStoreService.findChangedGroups(status);
return projectGroups(events);
return projectGroupsByIds(changedids);
}
/**
@ -124,23 +151,13 @@ public class ProjectionService {
*/
@Cacheable("groups")
public List<Group> projectPublicGroups() throws EventException {
List<UUID> groupIds = eventStoreService.findExistingGroupIds();
List<UUID> groupIds = eventStoreService.findPublicGroupIds();
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds,
eventTypesToString(CREATEGROUP,
SETDESCRIPTION,
SETTITLE,
SETLIMIT));
List<Group> groups = projectGroups(events);
return groups.stream()
.filter(group -> group.getType() != Type.PRIVATE)
.collect(Collectors.toList());
return projectGroupsByIds(groupIds);
}
/**
@ -151,21 +168,13 @@ public class ProjectionService {
*/
@Cacheable("groups")
public List<Group> projectLectures() {
List<UUID> groupIds = eventStoreService.findExistingGroupIds();
List<UUID> groupIds = eventStoreService.findLectureGroupIds();
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds,
eventTypesToString(CREATEGROUP,
SETTITLE));
List<Group> lectures = projectGroups(events);
return lectures.stream()
.filter(group -> group.getType() == Type.LECTURE)
.collect(Collectors.toList());
return projectGroupsByIds(groupIds);
}
/**
@ -184,41 +193,7 @@ public class ProjectionService {
return Collections.emptyList();
}
List<Event> groupEvents = eventStoreService.findEventsByGroupAndType(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);
return projectGroupsByIds(groupIds);
}
/**
@ -234,7 +209,7 @@ public class ProjectionService {
}
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
*/
@Cacheable("groups")
public List<Group> searchPublicGroups(String search, String userid) {
public List<Group> searchPublicGroups(String search, String principal) {
List<Group> groups = projectionService.projectPublicGroups();
projectionService.removeUserGroups(groups, userid);
System.out.println(groups);
projectionService.removeUserGroups(groups, principal);
System.out.println(groups);
SortHelper.sortByGroupType(groups);
if (search.isEmpty()) {

View File

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

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@
</div>
<!--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"
th:action="@{/gruppen2/details/{id}/edit(id=${group.getId()})}">
<button class="btn btn-secondary btn-block">Gruppe verwalten</button>
@ -56,7 +56,7 @@
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between"
th:each="member : ${group.getMembers()}">
<span th:text="${member}"></span>
<span th:text="${member.format()}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span>
</li>
</ul>

View File

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

View File

@ -1,7 +1,12 @@
<!DOCTYPE HTML>
<!--suppress ALL -->
<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-->
<th:block th:fragment="meta">
<!--Gruppentitel-->
@ -14,6 +19,7 @@
th:value="${group?.getTitle()}" required>
</div>
<!--Gruppenbeschreibung-->
<div class="input-group"
title="Eine kurze Gruppenbeschreibung zwischen 4 und 512 Zeichen. Die Beschreibung ist öffentlich.">

View File

@ -1,6 +1,10 @@
<!DOCTYPE HTML>
<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-->
<th:block th:fragment="badges">
<span class="badge badge-pill private"
@ -14,7 +18,7 @@
th:if='${group.isLecture()}'>Veranstaltung</span>
<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:text="${parent.getTitle()}">Parent</span>
@ -27,7 +31,7 @@
<!--User Badges-->
<th:block th:fragment="userbadges">
<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:fragment="groupcontent">
@ -56,7 +60,7 @@
</div>
<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()}">
<button class="btn btn-success" type="submit">Gruppe beitreten.</button>
</form>

View File

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

View File

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