From cd445f12e800eccaa9a7b8cc292b9b33d5116bb2 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 14 Apr 2020 17:13:29 +0200 Subject: [PATCH] refactor vorerst fertig, bugsuche --- mysql/db/entrypoint/schema.sql | 17 +- .../gruppen2/domain/event/AddMemberEvent.java | 2 +- .../domain/event/CreateGroupEvent.java | 2 +- .../domain/event/DestroyGroupEvent.java | 2 +- .../mops/gruppen2/domain/event/Event.java | 6 +- .../domain/event/KickMemberEvent.java | 2 +- .../domain/event/SetDescriptionEvent.java | 2 +- .../domain/event/SetInviteLinkEvent.java | 2 +- .../gruppen2/domain/event/SetLimitEvent.java | 2 +- .../gruppen2/domain/event/SetParentEvent.java | 2 +- .../gruppen2/domain/event/SetTitleEvent.java | 2 +- .../gruppen2/domain/event/SetTypeEvent.java | 2 +- .../domain/event/UpdateRoleEvent.java | 2 +- .../gruppen2/domain/helper/CommonHelper.java | 6 + .../gruppen2/domain/model/group/Group.java | 44 ++++-- .../gruppen2/domain/model/group/User.java | 2 +- .../model/group/wrapper/Description.java | 6 + .../domain/model/group/wrapper/Parent.java | 8 +- .../domain/model/group/wrapper/Title.java | 6 + .../domain/service/EventStoreService.java | 46 ++++-- .../gruppen2/domain/service/GroupService.java | 38 ++++- .../domain/service/ProjectionService.java | 149 ++++++++---------- .../domain/service/SearchService.java | 6 +- .../gruppen2/persistance/EventRepository.java | 24 +-- .../gruppen2/persistance/dto/EventDTO.java | 4 +- .../java/mops/gruppen2/web/APIController.java | 4 +- .../gruppen2/web/GroupDetailsController.java | 24 +-- src/main/resources/schema-h2.sql | 14 +- src/main/resources/templates/details.html | 4 +- src/main/resources/templates/edit.html | 2 +- .../resources/templates/fragments/forms.html | 8 +- .../resources/templates/fragments/groups.html | 10 +- .../domain/service/ControllerServiceTest.java | 1 + .../domain/service/GroupServiceTest.java | 6 +- 34 files changed, 272 insertions(+), 185 deletions(-) diff --git a/mysql/db/entrypoint/schema.sql b/mysql/db/entrypoint/schema.sql index a97d31e..33c023a 100644 --- a/mysql/db/entrypoint/schema.sql +++ b/mysql/db/entrypoint/schema.sql @@ -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 ); diff --git a/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java b/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java index f492037..6f2ecc2 100644 --- a/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java @@ -38,7 +38,7 @@ public class AddMemberEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.ADDMEMBER.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/CreateGroupEvent.java b/src/main/java/mops/gruppen2/domain/event/CreateGroupEvent.java index 0d763e7..ac9c826 100644 --- a/src/main/java/mops/gruppen2/domain/event/CreateGroupEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/CreateGroupEvent.java @@ -33,7 +33,7 @@ public class CreateGroupEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.CREATEGROUP.toString(); } diff --git a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java index 78a1b45..83ba15d 100644 --- a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java @@ -23,7 +23,7 @@ public class DestroyGroupEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.DESTROYGROUP.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index be97a44..84e563c 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -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(); } diff --git a/src/main/java/mops/gruppen2/domain/event/KickMemberEvent.java b/src/main/java/mops/gruppen2/domain/event/KickMemberEvent.java index cc3ad27..7e11c93 100644 --- a/src/main/java/mops/gruppen2/domain/event/KickMemberEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/KickMemberEvent.java @@ -27,7 +27,7 @@ public class KickMemberEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.KICKMEMBER.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/SetDescriptionEvent.java b/src/main/java/mops/gruppen2/domain/event/SetDescriptionEvent.java index 976dedd..40b5906 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetDescriptionEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetDescriptionEvent.java @@ -34,7 +34,7 @@ public class SetDescriptionEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.SETDESCRIPTION.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/SetInviteLinkEvent.java b/src/main/java/mops/gruppen2/domain/event/SetInviteLinkEvent.java index 25f2af0..34acd6b 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetInviteLinkEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetInviteLinkEvent.java @@ -31,7 +31,7 @@ public class SetInviteLinkEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.SETLINK.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java b/src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java index d160435..073e94b 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java @@ -32,7 +32,7 @@ public class SetLimitEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.SETLIMIT.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java b/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java index 736cc89..901b329 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java @@ -31,7 +31,7 @@ public class SetParentEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.SETPARENT.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java b/src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java index 17e6599..ee721d3 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java @@ -34,7 +34,7 @@ public class SetTitleEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.SETTITLE.toString(); } diff --git a/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java b/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java index e87c8ee..6533eae 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java @@ -30,7 +30,7 @@ public class SetTypeEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.SETTYPE.toString(); } } diff --git a/src/main/java/mops/gruppen2/domain/event/UpdateRoleEvent.java b/src/main/java/mops/gruppen2/domain/event/UpdateRoleEvent.java index 0164d51..10b5ccb 100644 --- a/src/main/java/mops/gruppen2/domain/event/UpdateRoleEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/UpdateRoleEvent.java @@ -33,7 +33,7 @@ public class UpdateRoleEvent extends Event { } @Override - public String getType() { + public String type() { return EventType.UPDATEROLE.toString(); } diff --git a/src/main/java/mops/gruppen2/domain/helper/CommonHelper.java b/src/main/java/mops/gruppen2/domain/helper/CommonHelper.java index ce01dc7..6e833d8 100644 --- a/src/main/java/mops/gruppen2/domain/helper/CommonHelper.java +++ b/src/main/java/mops/gruppen2/domain/helper/CommonHelper.java @@ -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 stringsToUUID(List groupids) { + return groupids.stream() + .map(UUID::fromString) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/mops/gruppen2/domain/model/group/Group.java b/src/main/java/mops/gruppen2/domain/model/group/Group.java index bdd1fba..5c407f1 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -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()) + + ")"; + } } diff --git a/src/main/java/mops/gruppen2/domain/model/group/User.java b/src/main/java/mops/gruppen2/domain/model/group/User.java index 07ec8f1..63464ee 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/User.java +++ b/src/main/java/mops/gruppen2/domain/model/group/User.java @@ -26,7 +26,7 @@ public class User { @JsonProperty("familyname") String familyname; - @JsonProperty("mail") + @JsonProperty("email") String email; public User(KeycloakAuthenticationToken token) { diff --git a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Description.java b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Description.java index c37c29a..5be889e 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Description.java +++ b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Description.java @@ -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"); + } } diff --git a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Parent.java b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Parent.java index aaa93d0..bd3d05d 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Parent.java +++ b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Parent.java @@ -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); } } diff --git a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Title.java b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Title.java index 2bcc8ee..651dabc 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Title.java +++ b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Title.java @@ -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"); + } } diff --git a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java index 4ee3328..00f4d2d 100644 --- a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java +++ b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java @@ -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 findChangedGroupEvents(long status) { + List findChangedGroups(long status) { List changedGroupIds = eventStore.findGroupIdsWhereEventIdGreaterThanStatus(status); - List 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 findPublicGroupIds() { + List groups = findExistingGroupIds(); + List 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 findLectureGroupIds() { + List groups = findExistingGroupIds(); + List 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 findEventsByType(String... types) { - return getEventsFromDTOs(eventStore.findEventDTOsByType(types)); + return getEventsFromDTOs(eventStore.findEventDTOsByType(Arrays.asList(types))); } List findEventsByType(String type) { - return getEventsFromDTOs(eventStore.findEventDTOsByType(type)); + return getEventsFromDTOs(eventStore.findEventDTOsByType(Collections.singletonList(type))); } List findEventsByGroupAndType(List 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 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 findLatestEventsFromGroupsByType(EventType... types) { - return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(eventTypesToString(types))); + return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(Arrays.asList(eventTypesToString(types)))); } } diff --git a/src/main/java/mops/gruppen2/domain/service/GroupService.java b/src/main/java/mops/gruppen2/domain/service/GroupService.java index 237db1d..8fd1c92 100644 --- a/src/main/java/mops/gruppen2/domain/service/GroupService.java +++ b/src/main/java/mops/gruppen2/domain/service/GroupService.java @@ -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)); } diff --git a/src/main/java/mops/gruppen2/domain/service/ProjectionService.java b/src/main/java/mops/gruppen2/domain/service/ProjectionService.java index e7b64a3..57282d5 100644 --- a/src/main/java/mops/gruppen2/domain/service/ProjectionService.java +++ b/src/main/java/mops/gruppen2/domain/service/ProjectionService.java @@ -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 projectGroups(List events) throws EventException { - Map 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 events) throws EventException { + private static Group projectGroupByEvents(List 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 projectGroupsByEvents(List events) throws EventException { + Map 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 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 projectGroupsByIds(List groupids) { + List 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 projectNewGroups(long status) { - List events = eventStoreService.findChangedGroupEvents(status); + public List projectChangedGroups(long status) { + List changedids = eventStoreService.findChangedGroups(status); - return projectGroups(events); + return projectGroupsByIds(changedids); } /** @@ -124,23 +151,13 @@ public class ProjectionService { */ @Cacheable("groups") public List projectPublicGroups() throws EventException { - List groupIds = eventStoreService.findExistingGroupIds(); + List groupIds = eventStoreService.findPublicGroupIds(); if (groupIds.isEmpty()) { return Collections.emptyList(); } - List events = eventStoreService.findEventsByGroupAndType(groupIds, - eventTypesToString(CREATEGROUP, - SETDESCRIPTION, - SETTITLE, - SETLIMIT)); - - List 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 projectLectures() { - List groupIds = eventStoreService.findExistingGroupIds(); + List groupIds = eventStoreService.findLectureGroupIds(); if (groupIds.isEmpty()) { return Collections.emptyList(); } - List events = eventStoreService.findEventsByGroupAndType(groupIds, - eventTypesToString(CREATEGROUP, - SETTITLE)); - - List 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 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 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)); } } diff --git a/src/main/java/mops/gruppen2/domain/service/SearchService.java b/src/main/java/mops/gruppen2/domain/service/SearchService.java index 627c964..dcb5228 100644 --- a/src/main/java/mops/gruppen2/domain/service/SearchService.java +++ b/src/main/java/mops/gruppen2/domain/service/SearchService.java @@ -32,9 +32,11 @@ public class SearchService { * @throws EventException Projektionsfehler */ @Cacheable("groups") - public List searchPublicGroups(String search, String userid) { + public List searchPublicGroups(String search, String principal) { List 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()) { diff --git a/src/main/java/mops/gruppen2/persistance/EventRepository.java b/src/main/java/mops/gruppen2/persistance/EventRepository.java index eaf266e..d5b5164 100644 --- a/src/main/java/mops/gruppen2/persistance/EventRepository.java +++ b/src/main/java/mops/gruppen2/persistance/EventRepository.java @@ -13,10 +13,10 @@ public interface EventRepository extends CrudRepository { // ####################################### 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 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 { + " WHERE group_id IN (:groupIds) ") List findEventDTOsByGroup(@Param("groupIds") List groupIds); - @Query("SELECT * FROM event" + /*@Query("SELECT * FROM event" + " WHERE group_id IN (:userIds) ") - List findEventDTOsByUser(@Param("groupIds") String... userIds); + List findEventDTOsByUser(@Param("groupIds") String... userIds);*/ @Query("SELECT * FROM event" + " WHERE event_type IN (:types)") - List findEventDTOsByType(@Param("types") String... types); + List findEventDTOsByType(@Param("types") List types); @Query("SELECT * FROM event" + " WHERE event_type IN (:types) AND group_id IN (:groupIds)") List findEventDTOsByGroupAndType(@Param("groupIds") List groupIds, - @Param("types") String... types); + @Param("types") List types); - @Query("SELECT * FROM event" + /*@Query("SELECT * FROM event" + " WHERE event_type IN (:types) AND user_id = :userId") List 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 findLatestEventDTOsPartitionedByGroupByUser(@Param("userId") String userId); + List 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 { + " WHERE event_type IN (:types)" + ")" + "SELECT * FROM ranked_events WHERE rn = 1;") - List findLatestEventDTOsPartitionedByGroupByType(@Param("types") String... types); + List findLatestEventDTOsPartitionedByGroupByType(@Param("types") List types); + // ######################################### COUNT ########################################### + @Query("SELECT MAX(event_id) FROM event") Long findMaxEventId(); } diff --git a/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java b/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java index eacbfd1..88d4272 100644 --- a/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java +++ b/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java @@ -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; diff --git a/src/main/java/mops/gruppen2/web/APIController.java b/src/main/java/mops/gruppen2/web/APIController.java index 38d1432..0a8dc5c 100644 --- a/src/main/java/mops/gruppen2/web/APIController.java +++ b/src/main/java/mops/gruppen2/web/APIController.java @@ -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)); } } diff --git a/src/main/java/mops/gruppen2/web/GroupDetailsController.java b/src/main/java/mops/gruppen2/web/GroupDetailsController.java index eafb33c..58c3131 100644 --- a/src/main/java/mops/gruppen2/web/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/web/GroupDetailsController.java @@ -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); diff --git a/src/main/resources/schema-h2.sql b/src/main/resources/schema-h2.sql index 15e1d44..46b52a7 100644 --- a/src/main/resources/schema-h2.sql +++ b/src/main/resources/schema-h2.sql @@ -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 ); diff --git a/src/main/resources/templates/details.html b/src/main/resources/templates/details.html index c9a81b4..aa64206 100644 --- a/src/main/resources/templates/details.html +++ b/src/main/resources/templates/details.html @@ -44,7 +44,7 @@ -
+
@@ -56,7 +56,7 @@
  • - +
diff --git a/src/main/resources/templates/edit.html b/src/main/resources/templates/edit.html index 17baeaa..a710baf 100644 --- a/src/main/resources/templates/edit.html +++ b/src/main/resources/templates/edit.html @@ -98,7 +98,7 @@
  • - +
    diff --git a/src/main/resources/templates/fragments/forms.html b/src/main/resources/templates/fragments/forms.html index d8b539b..f38f3bc 100644 --- a/src/main/resources/templates/fragments/forms.html +++ b/src/main/resources/templates/fragments/forms.html @@ -1,7 +1,12 @@ - + + + + + + @@ -14,6 +19,7 @@ th:value="${group?.getTitle()}" required>
+
diff --git a/src/main/resources/templates/fragments/groups.html b/src/main/resources/templates/fragments/groups.html index 921ca9b..4d5f38c 100644 --- a/src/main/resources/templates/fragments/groups.html +++ b/src/main/resources/templates/fragments/groups.html @@ -1,6 +1,10 @@ + + + + Veranstaltung Parent @@ -27,7 +31,7 @@ Admin + th:if="${group.isAdmin(member.getId())}">Admin @@ -56,7 +60,7 @@
- diff --git a/src/test/java/mops/gruppen2/domain/service/ControllerServiceTest.java b/src/test/java/mops/gruppen2/domain/service/ControllerServiceTest.java index 96f89b4..d83ea89 100644 --- a/src/test/java/mops/gruppen2/domain/service/ControllerServiceTest.java +++ b/src/test/java/mops/gruppen2/domain/service/ControllerServiceTest.java @@ -21,6 +21,7 @@ class ControllerServiceTest { Account account; Account account2; Account account3; + GroupEmpty @Autowired EventStoreService eventStoreService; @Autowired diff --git a/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java b/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java index 8f1c94a..3e153c0 100644 --- a/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java +++ b/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java @@ -70,7 +70,7 @@ class GroupServiceTest { void rightClassForSuccessfulGroup() { List eventList = completePrivateGroup(1); - List groups = ProjectionService.projectGroups(eventList); + List groups = ProjectionService.projectGroupsByEvents(eventList); assertThat(groups.get(0)).isInstanceOf(Group.class); } @@ -79,7 +79,7 @@ class GroupServiceTest { void projectEventList_SingleGroup() { List eventList = completePrivateGroup(5); - List groups = ProjectionService.projectGroups(eventList); + List groups = ProjectionService.projectGroupsByEvents(eventList); assertThat(groups).hasSize(1); assertThat(groups.get(0).getMemberships()).hasSize(5); @@ -92,7 +92,7 @@ class GroupServiceTest { List eventList = completePrivateGroups(10, 2); eventList.addAll(completePublicGroups(10, 5)); - List groups = ProjectionService.projectGroups(eventList); + List groups = ProjectionService.projectGroupsByEvents(eventList); assertThat(groups).hasSize(20); assertThat(groups.stream().map(group -> group.getMemberships().size()).reduce(Integer::sum).get()).isEqualTo(70);