From 8097cbfaf3a6b16d504eb907722e6007079f9577 Mon Sep 17 00:00:00 2001 From: Christoph Date: Thu, 16 Apr 2020 18:51:19 +0200 Subject: [PATCH 01/30] new keycloak realm --- src/main/java/mops/gruppen2/config/KeycloakConfig.java | 4 ++-- src/main/resources/application.properties | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/mops/gruppen2/config/KeycloakConfig.java b/src/main/java/mops/gruppen2/config/KeycloakConfig.java index 3d99c83..0f7ba62 100644 --- a/src/main/java/mops/gruppen2/config/KeycloakConfig.java +++ b/src/main/java/mops/gruppen2/config/KeycloakConfig.java @@ -22,10 +22,10 @@ public class KeycloakConfig { @Value("${keycloak.resource}") private String clientId; - @Value("f73354fd-614e-4e24-b801-a8d0f4abf531") + @Value("9f8d3616-60d3-48ea-9d14-06ae9eeee5cb") private String clientSecret; - @Value("https://gruppenkeycloak.herokuapp.com/auth/realms/master/protocol/openid-connect/token") + @Value("https://gruppenkeycloak.herokuapp.com/auth/realms/Gruppen/protocol/openid-connect/token") private String tokenUri; @Bean diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 45828c0..c5dcd39 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -12,11 +12,11 @@ spring.profiles.active = dev #keycloak.autodetect-bearer-only = true #keycloak.confidential-port = 443 keycloak.auth-server-url = https://gruppenkeycloak.herokuapp.com/auth -hhu_keycloak.token-uri = https://gruppenkeycloak.herokuapp.com/auth/realms/master/protocol/openid-connect/token +hhu_keycloak.token-uri = https://gruppenkeycloak.herokuapp.com/auth/realms/Gruppen/protocol/openid-connect/token keycloak.principal-attribute = preferred_username -keycloak.realm = master +keycloak.realm = Gruppen keycloak.resource = gruppen-app -keycloak.credentials.secret = f73354fd-614e-4e24-b801-a8d0f4abf531 +keycloak.credentials.secret = 9f8d3616-60d3-48ea-9d14-06ae9eeee5cb keycloak.verify-token-audience = true keycloak.use-resource-role-mappings = true keycloak.autodetect-bearer-only = true From 0bb0b4594fbb6f137bd4d78e19dafe6efd99965a Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 14:55:43 +0200 Subject: [PATCH 02/30] initial group log --- .../mops/gruppen2/config/KeycloakConfig.java | 4 +- .../gruppen2/domain/event/AddMemberEvent.java | 5 +++ .../domain/event/CreateGroupEvent.java | 5 +++ .../domain/event/DestroyGroupEvent.java | 5 +++ .../mops/gruppen2/domain/event/Event.java | 7 ++++ .../domain/event/KickMemberEvent.java | 5 +++ .../domain/event/SetDescriptionEvent.java | 5 +++ .../domain/event/SetInviteLinkEvent.java | 5 +++ .../gruppen2/domain/event/SetLimitEvent.java | 5 +++ .../gruppen2/domain/event/SetParentEvent.java | 5 +++ .../gruppen2/domain/event/SetTitleEvent.java | 5 +++ .../gruppen2/domain/event/SetTypeEvent.java | 5 +++ .../domain/event/UpdateRoleEvent.java | 5 +++ .../domain/service/EventStoreService.java | 8 +++- .../controller/GroupDetailsController.java | 15 +++++++- .../gruppen2/persistance/EventRepository.java | 4 ++ .../gruppen2/persistance/dto/EventDTO.java | 4 ++ src/main/resources/application.properties | 8 ++-- src/main/resources/schema-h2.sql | 1 + src/main/resources/templates/edit.html | 15 +++++++- src/main/resources/templates/history.html | 37 +++++++++++++++++++ 21 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 src/main/resources/templates/history.html diff --git a/src/main/java/mops/gruppen2/config/KeycloakConfig.java b/src/main/java/mops/gruppen2/config/KeycloakConfig.java index 0f7ba62..74e607d 100644 --- a/src/main/java/mops/gruppen2/config/KeycloakConfig.java +++ b/src/main/java/mops/gruppen2/config/KeycloakConfig.java @@ -22,10 +22,10 @@ public class KeycloakConfig { @Value("${keycloak.resource}") private String clientId; - @Value("9f8d3616-60d3-48ea-9d14-06ae9eeee5cb") + @Value("2e2e5770-c454-4d31-be99-9d8c34c93089") private String clientSecret; - @Value("https://gruppenkeycloak.herokuapp.com/auth/realms/Gruppen/protocol/openid-connect/token") + @Value("https://churl-keycloak.herokuapp.com/auth/realms/Gruppen/protocol/openid-connect/token") private String tokenUri; @Bean diff --git a/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java b/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java index e294cc1..d638ca2 100644 --- a/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java @@ -45,6 +45,11 @@ public class AddMemberEvent extends Event { log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers()); } + @Override + public String format() { + return "Benutzer hinzugefügt: " + target + "."; + } + @Override 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 c3457cb..8e55353 100644 --- a/src/main/java/mops/gruppen2/domain/event/CreateGroupEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/CreateGroupEvent.java @@ -39,6 +39,11 @@ public class CreateGroupEvent extends Event { log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString()); } + @Override + public String format() { + return "Gruppe erstellt."; + } + @Override 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 432c174..989db37 100644 --- a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java @@ -30,6 +30,11 @@ public class DestroyGroupEvent extends Event { log.trace("\t\t\t\t\tGelöschte Gruppe: {}", group.toString()); } + @Override + public String format() { + return "Gruppe gelöscht."; + } + @Override 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 7ef6a92..d1b484b 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -13,6 +13,7 @@ import mops.gruppen2.domain.exception.IdMismatchException; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.infrastructure.GroupCache; +import java.time.LocalDateTime; import java.util.UUID; @Log4j2 @@ -44,6 +45,9 @@ public abstract class Event { @JsonProperty("target") protected String target; + @JsonProperty("date") + protected LocalDateTime date; + public Event(UUID groupid, String exec, String target) { this.groupid = groupid; this.exec = exec; @@ -54,6 +58,7 @@ public abstract class Event { if (this.version != 0) { throw new BadArgumentException("Event wurde schon initialisiert. (" + type() + ")"); } + date = LocalDateTime.now(); log.trace("Event wurde initialisiert. (" + type() + "," + version + ")"); @@ -88,6 +93,8 @@ public abstract class Event { protected abstract void applyEvent(Group group) throws EventException; + public abstract String format(); + @JsonIgnore 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 d5341d0..8d9a080 100644 --- a/src/main/java/mops/gruppen2/domain/event/KickMemberEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/KickMemberEvent.java @@ -34,6 +34,11 @@ public class KickMemberEvent extends Event { log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers()); } + @Override + public String format() { + return "Mitglied entfernt: " + target + "."; + } + @Override 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 f400d98..5ad51dd 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetDescriptionEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetDescriptionEvent.java @@ -38,6 +38,11 @@ public class SetDescriptionEvent extends Event { log.trace("\t\t\t\t\tNeue Beschreibung: {}", group.getDescription()); } + @Override + public String format() { + return "Beschreibung gesetzt: " + description + "."; + } + @Override 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 72979c4..30c19e7 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetInviteLinkEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetInviteLinkEvent.java @@ -38,6 +38,11 @@ public class SetInviteLinkEvent extends Event { log.trace("\t\t\t\t\tNeuer Link: {}", group.getLink()); } + @Override + public String format() { + return "Einladungslink gesetzt: " + link + "."; + } + @Override 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 f1b06eb..e69576b 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java @@ -36,6 +36,11 @@ public class SetLimitEvent extends Event { log.trace("\t\t\t\t\tNeues UserLimit: {}", group.getLimit()); } + @Override + public String format() { + return "Benutzerlimit gesetzt: " + limit + "."; + } + @Override 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 98f5fe9..2de2b60 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java @@ -35,6 +35,11 @@ public class SetParentEvent extends Event { log.trace("\t\t\t\t\tNeues Parent: {}", group.getParent()); } + @Override + public String format() { + return "Veranstaltungszugehörigkeit gesetzt: " + parent.getValue() + "."; + } + @Override 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 1a5acd6..bdfa3d1 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java @@ -38,6 +38,11 @@ public class SetTitleEvent extends Event { log.trace("\t\t\t\t\tNeuer Titel: {}", group.getTitle()); } + @Override + public String format() { + return "Titel gesetzt: " + title + "."; + } + @Override 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 b62e09d..6b0af22 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java @@ -37,6 +37,11 @@ public class SetTypeEvent extends Event { group.setType(exec, type); } + @Override + public String format() { + return "Gruppentype gesetzt: " + type + "."; + } + @Override 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 4df2fc8..e5f16fb 100644 --- a/src/main/java/mops/gruppen2/domain/event/UpdateRoleEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/UpdateRoleEvent.java @@ -38,6 +38,11 @@ public class UpdateRoleEvent extends Event { log.trace("\t\t\t\t\tNeue Admin: {}", group.getAdmins()); } + @Override + public String format() { + return "Mitgliedsrolle gesetzt: " + target + ": " + role + "."; + } + @Override public String type() { return EventType.UPDATEROLE.toString(); diff --git a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java index a7cd712..84e1912 100644 --- a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java +++ b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java @@ -10,6 +10,7 @@ import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.dto.EventDTO; import org.springframework.stereotype.Service; +import java.sql.Timestamp; import java.util.List; import java.util.stream.Collectors; @@ -59,7 +60,8 @@ public class EventStoreService { event.getExec(), event.getTarget(), event.type(), - payload); + payload, + Timestamp.valueOf(event.getDate())); } catch (JsonProcessingException e) { log.error("Event ({}) konnte nicht serialisiert werden!", event, e); throw new BadPayloadException(EventStoreService.class.toString()); @@ -95,4 +97,8 @@ public class EventStoreService { public List findAllEvents() { return getEventsFromDTOs(eventStore.findAllEvents()); } + + public List findGroupEvents(String groupId) { + return getEventsFromDTOs(eventStore.findGroupEvents(groupId)); + } } diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java index 5fffd22..e3f412d 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java @@ -8,6 +8,7 @@ import mops.gruppen2.domain.model.group.User; import mops.gruppen2.domain.model.group.wrapper.Description; import mops.gruppen2.domain.model.group.wrapper.Limit; import mops.gruppen2.domain.model.group.wrapper.Title; +import mops.gruppen2.domain.service.EventStoreService; import mops.gruppen2.domain.service.GroupService; import mops.gruppen2.domain.service.helper.CsvHelper; import mops.gruppen2.domain.service.helper.ValidationHelper; @@ -37,6 +38,7 @@ public class GroupDetailsController { private final GroupCache groupCache; private final GroupService groupService; + private final EventStoreService eventStoreService; @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @GetMapping("/details/{id}") @@ -94,6 +96,17 @@ public class GroupDetailsController { return "redirect:/gruppen2"; } + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) + @GetMapping("details/{id}/history") + public String getDetailsHistory(KeycloakAuthenticationToken token, + Model model, + @PathVariable("id") String groupId) { + + model.addAttribute("events", eventStoreService.findGroupEvents(groupId)); + + return "history"; + } + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @GetMapping("/details/{id}/edit") public String getDetailsEdit(KeycloakAuthenticationToken token, @@ -127,8 +140,6 @@ public class GroupDetailsController { String principal = token.getName(); Group group = groupCache.group(UUID.fromString(groupId)); - System.out.println(group); - groupService.setTitle(group, principal, title); groupService.setDescription(group, principal, description); diff --git a/src/main/java/mops/gruppen2/persistance/EventRepository.java b/src/main/java/mops/gruppen2/persistance/EventRepository.java index 26a94bb..7abe489 100644 --- a/src/main/java/mops/gruppen2/persistance/EventRepository.java +++ b/src/main/java/mops/gruppen2/persistance/EventRepository.java @@ -3,6 +3,7 @@ package mops.gruppen2.persistance; import mops.gruppen2.persistance.dto.EventDTO; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.List; @@ -16,4 +17,7 @@ public interface EventRepository extends CrudRepository { @Query("SELECT * FROM event") List findAllEvents(); + + @Query("SELECT * FROM event WHERE group_id = :groupid") + List findGroupEvents(@Param("groupid") String groupId); } diff --git a/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java b/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java index d0c326f..1032027 100644 --- a/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java +++ b/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java @@ -5,6 +5,8 @@ import lombok.Getter; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Table; +import java.sql.Timestamp; + @Table("event") @Getter @AllArgsConstructor @@ -21,4 +23,6 @@ public class EventDTO { String event_type; String event_payload; + + Timestamp timestamp; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index c5dcd39..be154ec 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -11,12 +11,12 @@ spring.profiles.active = dev #keycloak.use-resource-role-mappings = true #keycloak.autodetect-bearer-only = true #keycloak.confidential-port = 443 -keycloak.auth-server-url = https://gruppenkeycloak.herokuapp.com/auth -hhu_keycloak.token-uri = https://gruppenkeycloak.herokuapp.com/auth/realms/Gruppen/protocol/openid-connect/token +keycloak.auth-server-url = https://churl-keycloak.herokuapp.com/auth +hhu_keycloak.token-uri = https://churl-keycloak.herokuapp.com/auth/realms/gruppen/protocol/openid-connect/token keycloak.principal-attribute = preferred_username -keycloak.realm = Gruppen +keycloak.realm = gruppen keycloak.resource = gruppen-app -keycloak.credentials.secret = 9f8d3616-60d3-48ea-9d14-06ae9eeee5cb +keycloak.credentials.secret = 2e2e5770-c454-4d31-be99-9d8c34c93089 keycloak.verify-token-audience = true keycloak.use-resource-role-mappings = true keycloak.autodetect-bearer-only = true diff --git a/src/main/resources/schema-h2.sql b/src/main/resources/schema-h2.sql index 46b52a7..8cc499d 100644 --- a/src/main/resources/schema-h2.sql +++ b/src/main/resources/schema-h2.sql @@ -8,5 +8,6 @@ CREATE TABLE event exec_id VARCHAR(50) NOT NULL, target_id VARCHAR(50), event_type VARCHAR(32) NOT NULL, + timestamp DATETIME NOT NULL, event_payload VARCHAR(2500) NOT NULL ); diff --git a/src/main/resources/templates/edit.html b/src/main/resources/templates/edit.html index e65cbdb..c2da10b 100644 --- a/src/main/resources/templates/edit.html +++ b/src/main/resources/templates/edit.html @@ -36,8 +36,7 @@
-
@@ -89,6 +88,18 @@ +
+
+ Event-Historie +
+
+ + + + Event-Log +
+
+
diff --git a/src/main/resources/templates/history.html b/src/main/resources/templates/history.html new file mode 100644 index 0000000..a472a0c --- /dev/null +++ b/src/main/resources/templates/history.html @@ -0,0 +1,37 @@ + + + + + +
+
+ +

Event-Log

+ +
+
+ + + + + Datum: + +
+ +
+ + + +
+
+ +
+
+ + + From d8de8c6df5f5c919a2e35d96db394ac7a1b95548 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 15:04:14 +0200 Subject: [PATCH 03/30] order my groups by type --- .../gruppen2/infrastructure/GroupCache.java | 19 +++++++ .../controller/GruppenfindungController.java | 5 +- src/main/resources/templates/index.html | 49 ++++++++++++++++--- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java index d3999fd..10afc84 100644 --- a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java +++ b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java @@ -17,6 +17,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; @Log4j2 @RequiredArgsConstructor @@ -67,6 +68,24 @@ public class GroupCache { return Collections.unmodifiableList(users.get(userid)); } + public List userLectures(String userid) { + return userGroups(userid).stream() + .filter(Group::isLecture) + .collect(Collectors.toUnmodifiableList()); + } + + public List userPublics(String userid) { + return userGroups(userid).stream() + .filter(Group::isPublic) + .collect(Collectors.toUnmodifiableList()); + } + + public List userPrivates(String userid) { + return userGroups(userid).stream() + .filter(Group::isPrivate) + .collect(Collectors.toUnmodifiableList()); + } + public List publics() { if (!types.containsKey(Type.PUBLIC)) { return Collections.emptyList(); diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GruppenfindungController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GruppenfindungController.java index 4d09bdb..2e414ba 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GruppenfindungController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GruppenfindungController.java @@ -33,7 +33,10 @@ public class GruppenfindungController { @GetMapping("/gruppen2") public String getIndexPage(KeycloakAuthenticationToken token, Model model) { - model.addAttribute("groups", groupCache.userGroups(token.getName())); + + model.addAttribute("lectures", groupCache.userLectures(token.getName())); + model.addAttribute("publics", groupCache.userPublics(token.getName())); + model.addAttribute("privates", groupCache.userPrivates(token.getName())); return "index"; } diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 5a3ef98..f961036 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -13,19 +13,52 @@

Meine Gruppen

+ -
-
- +
+

Veranstaltungen

+
+
+ - -
-
- + +
+
+ +
+
+

Öffentliche Gruppen

+
+
+ + + +
+
+ +
+
+
+ +
+

Private Gruppen

+
+
+ + + +
+
+ +
+
+
From a27d3f6f27f707fb864b22f98a7802f8416dbd3a Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 15:14:57 +0200 Subject: [PATCH 04/30] user fragment for groupcontent plus link --- .../resources/templates/fragments/groups.html | 13 ++++++ src/main/resources/templates/index.html | 40 +++---------------- src/main/resources/templates/search.html | 14 +------ 3 files changed, 21 insertions(+), 46 deletions(-) diff --git a/src/main/resources/templates/fragments/groups.html b/src/main/resources/templates/fragments/groups.html index 4d5f38c..51b35cc 100644 --- a/src/main/resources/templates/fragments/groups.html +++ b/src/main/resources/templates/fragments/groups.html @@ -48,6 +48,19 @@ + +
+ + + +
+ +
+ +
+
+
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index f961036..35e6587 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -13,51 +13,23 @@

Meine Gruppen

-

Veranstaltungen

-
-
- - - -
-
- -
-
+

Öffentliche Gruppen

-
-
- - - -
-
- -
-
+

Private Gruppen

-
-
- - - -
-
- -
-
+
diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index 132d82d..24f6023 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -27,18 +27,8 @@
-
-
- - - -
-
- -
-
- +
From d88402954e9b1ea0ccd73f626386606ad9a53637 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 16:20:50 +0200 Subject: [PATCH 05/30] initial member + event export --- .../mops/gruppen2/domain/event/Event.java | 29 +++++++----- .../domain/service/EventStoreService.java | 13 +++++- .../domain/service/helper/CsvHelper.java | 16 +++++++ .../controller/GroupDetailsController.java | 44 ++++++++++++++++++- .../gruppen2/persistance/EventRepository.java | 3 ++ src/main/resources/templates/edit.html | 15 ++++++- 6 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index d1b484b..eee06b4 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -16,19 +16,23 @@ import mops.gruppen2.infrastructure.GroupCache; import java.time.LocalDateTime; import java.util.UUID; +import static com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + @Log4j2 -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "class") -@JsonSubTypes({@JsonSubTypes.Type(value = AddMemberEvent.class, name = "ADDMEMBER"), - @JsonSubTypes.Type(value = CreateGroupEvent.class, name = "CREATEGROUP"), - @JsonSubTypes.Type(value = DestroyGroupEvent.class, name = "DESTROYGROUP"), - @JsonSubTypes.Type(value = KickMemberEvent.class, name = "KICKMEMBER"), - @JsonSubTypes.Type(value = SetDescriptionEvent.class, name = "SETDESCRIPTION"), - @JsonSubTypes.Type(value = SetInviteLinkEvent.class, name = "SETLINK"), - @JsonSubTypes.Type(value = SetLimitEvent.class, name = "SETLIMIT"), - @JsonSubTypes.Type(value = SetParentEvent.class, name = "SETPARENT"), - @JsonSubTypes.Type(value = SetTitleEvent.class, name = "SETTITLE"), - @JsonSubTypes.Type(value = SetTypeEvent.class, name = "SETTYPE"), - @JsonSubTypes.Type(value = UpdateRoleEvent.class, name = "UPDATEROLE")}) +@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "class") +@JsonSubTypes({@Type(value = AddMemberEvent.class, name = "ADDMEMBER"), + @Type(value = CreateGroupEvent.class, name = "CREATEGROUP"), + @Type(value = DestroyGroupEvent.class, name = "DESTROYGROUP"), + @Type(value = KickMemberEvent.class, name = "KICKMEMBER"), + @Type(value = SetDescriptionEvent.class, name = "SETDESCRIPTION"), + @Type(value = SetInviteLinkEvent.class, name = "SETLINK"), + @Type(value = SetLimitEvent.class, name = "SETLIMIT"), + @Type(value = SetParentEvent.class, name = "SETPARENT"), + @Type(value = SetTitleEvent.class, name = "SETTITLE"), + @Type(value = SetTypeEvent.class, name = "SETTYPE"), + @Type(value = UpdateRoleEvent.class, name = "UPDATEROLE")}) @Getter @NoArgsConstructor // Lombok needs a default constructor in the base class public abstract class Event { @@ -93,6 +97,7 @@ public abstract class Event { protected abstract void applyEvent(Group group) throws EventException; + @JsonIgnore public abstract String format(); @JsonIgnore diff --git a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java index 84e1912..69500e6 100644 --- a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java +++ b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.exception.BadPayloadException; +import mops.gruppen2.domain.exception.GroupNotFoundException; import mops.gruppen2.domain.service.helper.JsonHelper; import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.dto.EventDTO; @@ -12,6 +13,7 @@ import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; @Log4j2 @@ -98,7 +100,14 @@ public class EventStoreService { return getEventsFromDTOs(eventStore.findAllEvents()); } - public List findGroupEvents(String groupId) { - return getEventsFromDTOs(eventStore.findGroupEvents(groupId)); + public List findGroupEvents(UUID groupId) { + return getEventsFromDTOs(eventStore.findGroupEvents(groupId.toString())); + } + + public String findGroupPayloads(UUID groupId) { + return eventStore.findGroupPayloads(groupId.toString()).stream() + .map(payload -> payload + "\n") + .reduce((String payloadA, String payloadB) -> payloadA + payloadB) + .orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden.")); } } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java index c087540..a12dc8a 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java @@ -45,4 +45,20 @@ public final class CsvHelper { return reader.readValues(stream).readAll(); } + + public static String writeCsvUserList(List members) { + StringBuilder builder = new StringBuilder(); + builder.append("id,givenname,familyname,email"); + + members.forEach(user -> builder.append(user.getId()) + .append(",") + .append(user.getGivenname()) + .append(",") + .append(user.getFamilyname()) + .append(",") + .append(user.getEmail()) + .append("\n")); + + return builder.toString(); + } } diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java index e3f412d..d482a47 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java @@ -14,6 +14,7 @@ import mops.gruppen2.domain.service.helper.CsvHelper; import mops.gruppen2.domain.service.helper.ValidationHelper; import mops.gruppen2.infrastructure.GroupCache; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; +import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -25,7 +26,9 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.security.RolesAllowed; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.IOException; import java.util.UUID; @SuppressWarnings("SameReturnValue") @@ -102,11 +105,49 @@ public class GroupDetailsController { Model model, @PathVariable("id") String groupId) { - model.addAttribute("events", eventStoreService.findGroupEvents(groupId)); + model.addAttribute("events", + eventStoreService.findGroupEvents(UUID.fromString(groupId))); return "history"; } + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) + @GetMapping("details/{id}/export/history") + public void getDetailsExportHistory(HttpServletResponse response, + @PathVariable("id") String groupId) { + + String filename = "eventlog-" + groupId + ".txt"; + + response.setContentType("text/txt"); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + filename + "\""); + + try { + response.getWriter().write(eventStoreService.findGroupPayloads(UUID.fromString(groupId))); + } catch (IOException e) { + log.error("Payloads konnten nicht geschrieben werden.", e); + } + } + + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) + @GetMapping("details/{id}/export/members") + public void getDetailsExportMembers(HttpServletResponse response, + @PathVariable("id") String groupId) { + + String filename = "teilnehmer-" + groupId + ".csv"; + + response.setContentType("text/csv"); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + filename + "\""); + + try { + response.getWriter() + .print(CsvHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers())); + } catch (IOException e) { + log.error("Teilnehmerliste konnte nicht geschrieben werden.", e); + } + } + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) @GetMapping("/details/{id}/edit") public String getDetailsEdit(KeycloakAuthenticationToken token, @@ -183,6 +224,7 @@ public class GroupDetailsController { Group group = groupCache.group(UUID.fromString(groupId)); ValidationHelper.throwIfNoAdmin(group, principal); + ValidationHelper.throwIfLastAdmin(group, principal); groupService.toggleMemberRole(group, principal, target); diff --git a/src/main/java/mops/gruppen2/persistance/EventRepository.java b/src/main/java/mops/gruppen2/persistance/EventRepository.java index 7abe489..5ce23fb 100644 --- a/src/main/java/mops/gruppen2/persistance/EventRepository.java +++ b/src/main/java/mops/gruppen2/persistance/EventRepository.java @@ -20,4 +20,7 @@ public interface EventRepository extends CrudRepository { @Query("SELECT * FROM event WHERE group_id = :groupid") List findGroupEvents(@Param("groupid") String groupId); + + @Query("SELECT event_payload FROM event WHERE group_id = :groupid") + List findGroupPayloads(@Param("groupid") String groupId); } diff --git a/src/main/resources/templates/edit.html b/src/main/resources/templates/edit.html index c2da10b..853d15d 100644 --- a/src/main/resources/templates/edit.html +++ b/src/main/resources/templates/edit.html @@ -93,10 +93,23 @@ Event-Historie
From 8a7c02e01a63eb6d613d2133ff6a7289277cf7ac Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 16:25:29 +0200 Subject: [PATCH 06/30] fix csv format --- .../mops/gruppen2/domain/service/helper/CsvHelper.java | 2 +- .../infrastructure/controller/GroupDetailsController.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java index a12dc8a..99dddc7 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java @@ -48,7 +48,7 @@ public final class CsvHelper { public static String writeCsvUserList(List members) { StringBuilder builder = new StringBuilder(); - builder.append("id,givenname,familyname,email"); + builder.append("id,givenname,familyname,email\n"); members.forEach(user -> builder.append(user.getId()) .append(",") diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java index d482a47..79d5972 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java @@ -112,13 +112,13 @@ public class GroupDetailsController { } @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) - @GetMapping("details/{id}/export/history") + @GetMapping(value = "details/{id}/export/history", produces = "text/plain;charset=UTF-8") public void getDetailsExportHistory(HttpServletResponse response, @PathVariable("id") String groupId) { String filename = "eventlog-" + groupId + ".txt"; - response.setContentType("text/txt"); + response.setContentType("text/txt;charset=UTF-8"); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\""); @@ -130,13 +130,13 @@ public class GroupDetailsController { } @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) - @GetMapping("details/{id}/export/members") + @GetMapping(value = "details/{id}/export/members", produces = "text/csv;charset=UTF-8") public void getDetailsExportMembers(HttpServletResponse response, @PathVariable("id") String groupId) { String filename = "teilnehmer-" + groupId + ".csv"; - response.setContentType("text/csv"); + response.setContentType("text/csv;charset=UTF-8"); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\""); From 9b58abdab23f19f6341133eafeed353d7d616d76 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 17:24:49 +0200 Subject: [PATCH 07/30] add initial search-tags, die finden aber evtl zuviel --- .../gruppen2/domain/model/group/Group.java | 3 +- .../domain/model/group/SortHelper.java | 51 ------------------- .../domain/service/SearchService.java | 5 +- .../domain/service/helper/SortHelper.java | 27 ++++++++++ .../controller/GroupDetailsController.java | 4 +- src/main/resources/templates/search.html | 32 ++++++++++-- 6 files changed, 61 insertions(+), 61 deletions(-) delete mode 100644 src/main/java/mops/gruppen2/domain/model/group/SortHelper.java create mode 100644 src/main/java/mops/gruppen2/domain/service/helper/SortHelper.java 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 c081f45..0ab9ae8 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -19,6 +19,7 @@ import mops.gruppen2.domain.model.group.wrapper.Link; import mops.gruppen2.domain.model.group.wrapper.Parent; import mops.gruppen2.domain.model.group.wrapper.Title; import mops.gruppen2.domain.service.helper.CommonHelper; +import mops.gruppen2.domain.service.helper.SortHelper; import mops.gruppen2.domain.service.helper.ValidationHelper; import javax.validation.Valid; @@ -302,7 +303,7 @@ public class Group { } public String format() { - return title + " " + description; + return type + ": " + title + " - " + description; } @Override diff --git a/src/main/java/mops/gruppen2/domain/model/group/SortHelper.java b/src/main/java/mops/gruppen2/domain/model/group/SortHelper.java deleted file mode 100644 index 0dbf19d..0000000 --- a/src/main/java/mops/gruppen2/domain/model/group/SortHelper.java +++ /dev/null @@ -1,51 +0,0 @@ -package mops.gruppen2.domain.model.group; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -import java.util.List; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class SortHelper { - - /** - * Sortiert die übergebene Liste an Gruppen, sodass Veranstaltungen am Anfang der Liste sind. - * - * @param groups Die Liste von Gruppen die sortiert werden soll - */ - public static void sortByGroupType(List groups) { - groups.sort((Group g1, Group g2) -> { - if (g1.getType() == Type.LECTURE) { - return -1; - } - if (g2.getType() == Type.LECTURE) { - return 1; - } - - if (g1.getType() == Type.PUBLIC) { - return -1; - } - - if (g2.getType() == Type.PUBLIC) { - return 1; - } - - return 0; - }); - } - - public static List sortByMemberRole(List memberships) { - memberships.sort((Membership m1, Membership m2) -> { - if (m1.getRole() == Role.ADMIN) { - return -1; - } - if (m2.getRole() == Role.ADMIN) { - return 1; - } - - return 0; - }); - - return memberships; - } -} diff --git a/src/main/java/mops/gruppen2/domain/service/SearchService.java b/src/main/java/mops/gruppen2/domain/service/SearchService.java index d2bedeb..528069b 100644 --- a/src/main/java/mops/gruppen2/domain/service/SearchService.java +++ b/src/main/java/mops/gruppen2/domain/service/SearchService.java @@ -4,7 +4,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.model.group.Group; -import mops.gruppen2.domain.model.group.SortHelper; import mops.gruppen2.infrastructure.GroupCache; import org.springframework.stereotype.Service; @@ -30,13 +29,11 @@ public class SearchService { * * @throws EventException Projektionsfehler */ - //TODO: search in lectures public List search(String search, String principal) { List groups = new ArrayList<>(); groups.addAll(groupCache.publics()); groups.addAll(groupCache.lectures()); groups = removeUserGroups(groups, principal); - SortHelper.sortByGroupType(groups); if (search.isEmpty()) { return groups; @@ -44,6 +41,8 @@ public class SearchService { log.debug("Es wurde gesucht nach: {}", search); + // Die Suche nach Typ (LECTURE, PUBLIC), ist nicht wirklich sicher, + // da im gesamtstring danach gesucht wird return groups.stream() .filter(group -> group.format().toLowerCase().contains(search.toLowerCase())) .collect(Collectors.toList()); diff --git a/src/main/java/mops/gruppen2/domain/service/helper/SortHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/SortHelper.java new file mode 100644 index 0000000..2f7583f --- /dev/null +++ b/src/main/java/mops/gruppen2/domain/service/helper/SortHelper.java @@ -0,0 +1,27 @@ +package mops.gruppen2.domain.service.helper; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import mops.gruppen2.domain.model.group.Membership; +import mops.gruppen2.domain.model.group.Role; + +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class SortHelper { + + public static List sortByMemberRole(List memberships) { + memberships.sort((Membership m1, Membership m2) -> { + if (m1.getRole() == Role.ADMIN) { + return -1; + } + if (m2.getRole() == Role.ADMIN) { + return 1; + } + + return 0; + }); + + return memberships; + } +} diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java index 79d5972..81a3dc2 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java @@ -224,7 +224,9 @@ public class GroupDetailsController { Group group = groupCache.group(UUID.fromString(groupId)); ValidationHelper.throwIfNoAdmin(group, principal); - ValidationHelper.throwIfLastAdmin(group, principal); + if (target.equals(principal)) { + ValidationHelper.throwIfLastAdmin(group, principal); + } groupService.toggleMemberRole(group, principal, target); diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index 24f6023..c8d7fd6 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -16,14 +16,36 @@
-
-
- Suchbegriff: +
+
+
+ Suchbegriff: +
+
- + +
- + +
+
+ + +
+ + + + +
+ + +
+
+ + +
+
From 0d79467e0b45af5c3b9117555ff759c363c517e7 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 17:36:24 +0200 Subject: [PATCH 08/30] make tag-search safe --- .../gruppen2/domain/model/group/Group.java | 2 +- .../domain/service/SearchService.java | 20 ++++++++-- .../controller/SearchAndInviteController.java | 37 ++++++++++++++++--- src/main/resources/templates/search.html | 22 +++++------ 4 files changed, 58 insertions(+), 23 deletions(-) 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 0ab9ae8..628bb3a 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -303,7 +303,7 @@ public class Group { } public String format() { - return type + ": " + title + " - " + description; + return title + " - " + description; } @Override diff --git a/src/main/java/mops/gruppen2/domain/service/SearchService.java b/src/main/java/mops/gruppen2/domain/service/SearchService.java index 528069b..06b65f3 100644 --- a/src/main/java/mops/gruppen2/domain/service/SearchService.java +++ b/src/main/java/mops/gruppen2/domain/service/SearchService.java @@ -4,10 +4,12 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.Type; import mops.gruppen2.infrastructure.GroupCache; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -29,7 +31,7 @@ public class SearchService { * * @throws EventException Projektionsfehler */ - public List search(String search, String principal) { + public List searchString(String search, String principal) { List groups = new ArrayList<>(); groups.addAll(groupCache.publics()); groups.addAll(groupCache.lectures()); @@ -40,14 +42,24 @@ public class SearchService { } log.debug("Es wurde gesucht nach: {}", search); - - // Die Suche nach Typ (LECTURE, PUBLIC), ist nicht wirklich sicher, - // da im gesamtstring danach gesucht wird return groups.stream() .filter(group -> group.format().toLowerCase().contains(search.toLowerCase())) .collect(Collectors.toList()); } + public List searchType(Type type, String principal) { + log.debug("Es wurde gesucht nach: {}", type); + + if (type == Type.LECTURE) { + return removeUserGroups(groupCache.lectures(), principal); + } + if (type == Type.PUBLIC) { + return removeUserGroups(groupCache.publics(), principal); + } + + return Collections.emptyList(); + } + private static List removeUserGroups(List groups, String principal) { return groups.stream() .filter(group -> !group.isMember(principal)) diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/SearchAndInviteController.java b/src/main/java/mops/gruppen2/infrastructure/controller/SearchAndInviteController.java index 1c96f01..35ae56c 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/SearchAndInviteController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/SearchAndInviteController.java @@ -41,13 +41,40 @@ public class SearchAndInviteController { } @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) - @PostMapping("/search") - public String postSearch(KeycloakAuthenticationToken token, - Model model, - @RequestParam("string") String search) { + @PostMapping("/search/string") + public String postSearchString(KeycloakAuthenticationToken token, + Model model, + @RequestParam("string") String search) { String principal = token.getName(); - List groups = searchService.search(search, principal); + List groups = searchService.searchString(search, principal); + + model.addAttribute("groups", groups); + + return "search"; + } + + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) + @GetMapping("/search/all") + public String getSearchAll(KeycloakAuthenticationToken token, + Model model) { + + String principal = token.getName(); + List groups = searchService.searchString("", principal); + + model.addAttribute("groups", groups); + + return "search"; + } + + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) + @GetMapping("/search/type/{type}") + public String getSearchType(KeycloakAuthenticationToken token, + Model model, + @PathVariable("type") Type type) { + + String principal = token.getName(); + List groups = searchService.searchType(type, principal); model.addAttribute("groups", groups); diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index c8d7fd6..70163e9 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -8,6 +8,9 @@ + + +
@@ -15,7 +18,7 @@
-
+
@@ -29,22 +32,15 @@
-
- - -
+ Alle Anzeigen -
- - -
-
- - -
+ Vorlesungen + Öffentliche Gruppen
From 95272075d4089dcd81ebf3a3513ac527d9ae1996 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 18:53:09 +0200 Subject: [PATCH 09/30] add sql-export --- .../domain/service/EventStoreService.java | 23 ++- .../domain/service/helper/CsvHelper.java | 64 -------- .../domain/service/helper/FileHelper.java | 148 ++++++++++++++++++ .../domain/service/helper/JsonHelper.java | 50 ------ .../controller/GroupCreationController.java | 4 +- .../controller/GroupDetailsController.java | 36 ++++- .../gruppen2/persistance/dto/EventDTO.java | 4 +- src/main/resources/data.sql | 27 ++++ src/main/resources/schema-h2.sql | 3 +- src/main/resources/templates/edit.html | 10 +- 10 files changed, 227 insertions(+), 142 deletions(-) delete mode 100644 src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java create mode 100644 src/main/java/mops/gruppen2/domain/service/helper/FileHelper.java delete mode 100644 src/main/java/mops/gruppen2/domain/service/helper/JsonHelper.java create mode 100644 src/main/resources/data.sql diff --git a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java index 69500e6..e17778a 100644 --- a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java +++ b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java @@ -5,8 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.exception.BadPayloadException; -import mops.gruppen2.domain.exception.GroupNotFoundException; -import mops.gruppen2.domain.service.helper.JsonHelper; +import mops.gruppen2.domain.service.helper.FileHelper; import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.dto.EventDTO; import org.springframework.stereotype.Service; @@ -55,15 +54,14 @@ public class EventStoreService { */ private static EventDTO getDTOFromEvent(Event event) { try { - String payload = JsonHelper.serializeEvent(event); + String payload = FileHelper.serializeEventJson(event); return new EventDTO(null, event.getGroupid().toString(), event.getVersion(), event.getExec(), event.getTarget(), - event.type(), - payload, - Timestamp.valueOf(event.getDate())); + Timestamp.valueOf(event.getDate()), + payload); } catch (JsonProcessingException e) { log.error("Event ({}) konnte nicht serialisiert werden!", event, e); throw new BadPayloadException(EventStoreService.class.toString()); @@ -85,7 +83,7 @@ public class EventStoreService { private static Event getEventFromDTO(EventDTO dto) { try { - return JsonHelper.deserializeEvent(dto.getEvent_payload()); + return FileHelper.deserializeEventJson(dto.getEvent_payload()); } catch (JsonProcessingException e) { log.error("Payload {} konnte nicht deserialisiert werden!", dto.getEvent_payload(), e); throw new BadPayloadException(EventStoreService.class.toString()); @@ -104,10 +102,11 @@ public class EventStoreService { return getEventsFromDTOs(eventStore.findGroupEvents(groupId.toString())); } - public String findGroupPayloads(UUID groupId) { - return eventStore.findGroupPayloads(groupId.toString()).stream() - .map(payload -> payload + "\n") - .reduce((String payloadA, String payloadB) -> payloadA + payloadB) - .orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden.")); + public List findGroupPayloads(UUID groupId) { + return eventStore.findGroupPayloads(groupId.toString()); + } + + public List findGroupDTOs(UUID groupid) { + return eventStore.findGroupEvents(groupid.toString()); } } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java deleted file mode 100644 index 99dddc7..0000000 --- a/src/main/java/mops/gruppen2/domain/service/helper/CsvHelper.java +++ /dev/null @@ -1,64 +0,0 @@ -package mops.gruppen2.domain.service.helper; - -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.dataformat.csv.CsvMapper; -import com.fasterxml.jackson.dataformat.csv.CsvSchema; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.log4j.Log4j2; -import mops.gruppen2.domain.exception.EventException; -import mops.gruppen2.domain.exception.WrongFileException; -import mops.gruppen2.domain.model.group.User; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -@Log4j2 -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class CsvHelper { - - public static List readCsvFile(MultipartFile file) throws EventException { - if (file == null || file.isEmpty()) { - return Collections.emptyList(); - } - - try { - List userList = read(file.getInputStream()); - return userList.stream() - .distinct() - .collect(Collectors.toList()); //filter duplicates from list - } catch (IOException e) { - log.error("File konnte nicht gelesen werden!", e); - throw new WrongFileException(file.getOriginalFilename()); - } - } - - private static List read(InputStream stream) throws IOException { - CsvMapper mapper = new CsvMapper(); - - CsvSchema schema = mapper.schemaFor(User.class).withHeader().withColumnReordering(true); - ObjectReader reader = mapper.readerFor(User.class).with(schema); - - return reader.readValues(stream).readAll(); - } - - public static String writeCsvUserList(List members) { - StringBuilder builder = new StringBuilder(); - builder.append("id,givenname,familyname,email\n"); - - members.forEach(user -> builder.append(user.getId()) - .append(",") - .append(user.getGivenname()) - .append(",") - .append(user.getFamilyname()) - .append(",") - .append(user.getEmail()) - .append("\n")); - - return builder.toString(); - } -} diff --git a/src/main/java/mops/gruppen2/domain/service/helper/FileHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/FileHelper.java new file mode 100644 index 0000000..78857a6 --- /dev/null +++ b/src/main/java/mops/gruppen2/domain/service/helper/FileHelper.java @@ -0,0 +1,148 @@ +package mops.gruppen2.domain.service.helper; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.CsvSchema; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.log4j.Log4j2; +import mops.gruppen2.domain.event.Event; +import mops.gruppen2.domain.exception.EventException; +import mops.gruppen2.domain.exception.GroupNotFoundException; +import mops.gruppen2.domain.exception.WrongFileException; +import mops.gruppen2.domain.model.group.User; +import mops.gruppen2.persistance.dto.EventDTO; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Log4j2 +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class FileHelper { + + // ######################################## CSV ############################################# + + + public static List readCsvFile(MultipartFile file) throws EventException { + if (file == null || file.isEmpty()) { + return Collections.emptyList(); + } + + try { + List userList = readCsv(file.getInputStream()); + return userList.stream() + .distinct() + .collect(Collectors.toList()); //filter duplicates from list + } catch (IOException e) { + log.error("File konnte nicht gelesen werden!", e); + throw new WrongFileException(file.getOriginalFilename()); + } + } + + private static List readCsv(InputStream stream) throws IOException { + CsvMapper mapper = new CsvMapper(); + + CsvSchema schema = mapper.schemaFor(User.class).withHeader().withColumnReordering(true); + ObjectReader reader = mapper.readerFor(User.class).with(schema); + + return reader.readValues(stream).readAll(); + } + + public static String writeCsvUserList(List members) { + StringBuilder builder = new StringBuilder(); + builder.append("id,givenname,familyname,email\n"); + + members.forEach(user -> builder.append(user.getId()) + .append(",") + .append(user.getGivenname()) + .append(",") + .append(user.getFamilyname()) + .append(",") + .append(user.getEmail()) + .append("\n")); + + return builder.toString(); + } + + + // ########################################## JSON ########################################### + + + /** + * Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload. + * + * @param event Java-Event-Repräsentation + * + * @return JSON-Event-Payload als String + * + * @throws JsonProcessingException Bei JSON Fehler + */ + + public static String serializeEventJson(Event event) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); + String payload = mapper.writeValueAsString(event); + log.trace(payload); + return payload; + } + + /** + * Übersetzt eine JSON-Event-Payload zu einer Java-Event-Repräsentation. + * + * @param json JSON-Event-Payload als String + * + * @return Java-Event-Repräsentation + * + * @throws JsonProcessingException Bei JSON Fehler + */ + public static Event deserializeEventJson(String json) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); + Event event = mapper.readValue(json, Event.class); + log.trace(event); + return event; + } + + + // ############################################### TXT ####################################### + + + public static String payloadsToPlain(List payloads) { + return payloads.stream() + .map(payload -> payload + "\n") + .reduce((String payloadA, String payloadB) -> payloadA + payloadB) + .orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden.")); + } + + public static String eventDTOsToSql(List dtos) { + StringBuilder builder = new StringBuilder(); + + builder.append("INSERT INTO event(group_id, group_version, exec_id, target_id, event_date, event_payload)\nVALUES\n"); + + dtos.forEach(dto -> builder.append("('") + .append(dto.getGroup_id()) + .append("','") + .append(dto.getGroup_version()) + .append("','") + .append(dto.getExec_id()) + .append("','") + .append(dto.getTarget_id()) + .append("','") + .append(dto.getEvent_date()) + .append("','") + .append(dto.getEvent_payload()) + .append("'),\n")); + + builder.replace(builder.length() - 2, builder.length(), ";"); + + return builder.toString(); + } + + + // ############################################### SQL ####################################### +} diff --git a/src/main/java/mops/gruppen2/domain/service/helper/JsonHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/JsonHelper.java deleted file mode 100644 index b66c57f..0000000 --- a/src/main/java/mops/gruppen2/domain/service/helper/JsonHelper.java +++ /dev/null @@ -1,50 +0,0 @@ -package mops.gruppen2.domain.service.helper; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.log4j.Log4j2; -import mops.gruppen2.domain.event.Event; - -/** - * Übersetzt JSON-Event-Payloads zu Java-Event-Repräsentationen und zurück. - */ -@Log4j2 -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class JsonHelper { - - /** - * Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload. - * - * @param event Java-Event-Repräsentation - * - * @return JSON-Event-Payload als String - * - * @throws JsonProcessingException Bei JSON Fehler - */ - - public static String serializeEvent(Event event) throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); - String payload = mapper.writeValueAsString(event); - log.trace(payload); - return payload; - } - - /** - * Übersetzt eine JSON-Event-Payload zu einer Java-Event-Repräsentation. - * - * @param json JSON-Event-Payload als String - * - * @return Java-Event-Repräsentation - * - * @throws JsonProcessingException Bei JSON Fehler - */ - public static Event deserializeEvent(String json) throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule()); - Event event = mapper.readValue(json, Event.class); - log.trace(event); - return event; - } -} diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupCreationController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupCreationController.java index 84d4e1e..60cb879 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupCreationController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupCreationController.java @@ -11,7 +11,7 @@ import mops.gruppen2.domain.model.group.wrapper.Limit; import mops.gruppen2.domain.model.group.wrapper.Parent; import mops.gruppen2.domain.model.group.wrapper.Title; import mops.gruppen2.domain.service.GroupService; -import mops.gruppen2.domain.service.helper.CsvHelper; +import mops.gruppen2.domain.service.helper.FileHelper; import mops.gruppen2.domain.service.helper.ValidationHelper; import mops.gruppen2.infrastructure.GroupCache; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @@ -67,7 +67,7 @@ public class GroupCreationController { // ROLE_studentin kann kein CSV importieren if (token.getAccount().getRoles().contains("orga")) { - groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file)); + groupService.addUsersToGroup(group, principal, FileHelper.readCsvFile(file)); } return "redirect:/gruppen2/details/" + group.getId(); diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java index 81a3dc2..e7043fd 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java @@ -10,7 +10,7 @@ import mops.gruppen2.domain.model.group.wrapper.Limit; import mops.gruppen2.domain.model.group.wrapper.Title; import mops.gruppen2.domain.service.EventStoreService; import mops.gruppen2.domain.service.GroupService; -import mops.gruppen2.domain.service.helper.CsvHelper; +import mops.gruppen2.domain.service.helper.FileHelper; import mops.gruppen2.domain.service.helper.ValidationHelper; import mops.gruppen2.infrastructure.GroupCache; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @@ -112,9 +112,9 @@ public class GroupDetailsController { } @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) - @GetMapping(value = "details/{id}/export/history", produces = "text/plain;charset=UTF-8") - public void getDetailsExportHistory(HttpServletResponse response, - @PathVariable("id") String groupId) { + @GetMapping(value = "details/{id}/export/history/plain", produces = "text/plain;charset=UTF-8") + public void getDetailsExportHistoryPlain(HttpServletResponse response, + @PathVariable("id") String groupId) { String filename = "eventlog-" + groupId + ".txt"; @@ -123,7 +123,29 @@ public class GroupDetailsController { "attachment; filename=\"" + filename + "\""); try { - response.getWriter().write(eventStoreService.findGroupPayloads(UUID.fromString(groupId))); + response.getWriter() + .write(FileHelper.payloadsToPlain( + eventStoreService.findGroupPayloads(UUID.fromString(groupId)))); + } catch (IOException e) { + log.error("Payloads konnten nicht geschrieben werden.", e); + } + } + + @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) + @GetMapping(value = "details/{id}/export/history/sql", produces = "application/sql;charset=UTF-8") + public void getDetailsExportHistorySql(HttpServletResponse response, + @PathVariable("id") String groupId) { + + String filename = "data.sql"; + + response.setContentType("application/sql;charset=UTF-8"); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + filename + "\""); + + try { + response.getWriter() + .write(FileHelper.eventDTOsToSql( + eventStoreService.findGroupDTOs(UUID.fromString(groupId)))); } catch (IOException e) { log.error("Payloads konnten nicht geschrieben werden.", e); } @@ -142,7 +164,7 @@ public class GroupDetailsController { try { response.getWriter() - .print(CsvHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers())); + .print(FileHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers())); } catch (IOException e) { log.error("Teilnehmerliste konnte nicht geschrieben werden.", e); } @@ -209,7 +231,7 @@ public class GroupDetailsController { String principal = token.getName(); Group group = groupCache.group(UUID.fromString(groupId)); - groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file)); + groupService.addUsersToGroup(group, principal, FileHelper.readCsvFile(file)); return "redirect:/gruppen2/details/" + groupId + "/edit"; } diff --git a/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java b/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java index 1032027..c46878a 100644 --- a/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java +++ b/src/main/java/mops/gruppen2/persistance/dto/EventDTO.java @@ -21,8 +21,6 @@ public class EventDTO { String exec_id; String target_id; - String event_type; + Timestamp event_date; String event_payload; - - Timestamp timestamp; } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..45613fb --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,27 @@ +INSERT INTO event(group_id, group_version, exec_id, target_id, event_date, event_payload) +VALUES ('e65dd5f1-b252-4512-8db4-0407b31d199f', '1', 'orga', 'null', '2020-04-17 18:52:01.259555', + '{"class":"CREATEGROUP","date":[2020,4,17,18,52,1,259555000],"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":1,"exec":"orga","target":null}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '2', 'orga', 'orga', '2020-04-17 18:52:01.291448', + '{"class":"ADDMEMBER","user":{"id":"orga","givenname":"Thomas","familyname":"Organisator","email":"orga@hhu.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":2,"exec":"orga","target":"orga","date":[2020,4,17,18,52,1,291448000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '3', 'orga', 'orga', '2020-04-17 18:52:01.301972', + '{"class":"UPDATEROLE","role":"ADMIN","groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":3,"exec":"orga","target":"orga","date":[2020,4,17,18,52,1,301972000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '4', 'orga', 'null', '2020-04-17 18:52:01.3053', + '{"class":"SETLIMIT","limit":{"value":1000},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":4,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,305300000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '5', 'orga', 'null', '2020-04-17 18:52:01.310406', + '{"class":"SETTYPE","type":"LECTURE","groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":5,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,310406000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '6', 'orga', 'null', '2020-04-17 18:52:01.313421', + '{"class":"SETPARENT","parent":{"id":"00000000-0000-0000-0000-000000000000"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":6,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,313421000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '7', 'orga', 'null', '2020-04-17 18:52:01.317931', + '{"class":"SETTITLE","title":{"value":"Programmierung SS2020"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":7,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,317931000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '8', 'orga', 'null', '2020-04-17 18:52:01.320693', + '{"class":"SETDESCRIPTION","desc":{"value":"Einführung in die objektorientierte Programmierung mit Java."},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":8,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,320693000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '9', 'orga', 'A5ggd', '2020-04-17 18:52:01.330879', + '{"class":"ADDMEMBER","user":{"id":"A5ggd","givenname":"peter","familyname":"pan","email":"peter@pan.com"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":9,"exec":"orga","target":"A5ggd","date":[2020,4,17,18,52,1,330879000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '10', 'orga', 'Affs', '2020-04-17 18:52:01.333756', + '{"class":"ADDMEMBER","user":{"id":"Affs","givenname":"olaf","familyname":"pomodoro","email":"ol@f-99.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":10,"exec":"orga","target":"Affs","date":[2020,4,17,18,52,1,333756000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '11', 'orga', '55fdd', '2020-04-17 18:52:01.336206', + '{"class":"ADDMEMBER","user":{"id":"55fdd","givenname":"dieter","familyname":"niemöller","email":"pfarrer@erde.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":11,"exec":"orga","target":"55fdd","date":[2020,4,17,18,52,1,336206000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '12', 'orga', '22ööl', '2020-04-17 18:52:01.338582', + '{"class":"ADDMEMBER","user":{"id":"22ööl","givenname":"thomas","familyname":"müller","email":"thot@scheisse.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":12,"exec":"orga","target":"22ööl","date":[2020,4,17,18,52,1,338582000]}'), + ('e65dd5f1-b252-4512-8db4-0407b31d199f', '13', 'orga', 'tdsd8', '2020-04-17 18:52:01.341216', + '{"class":"ADDMEMBER","user":{"id":"tdsd8","givenname":"tobidignouserandingdong","familyname":"abraham","email":"g@gmail.mail"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":13,"exec":"orga","target":"tdsd8","date":[2020,4,17,18,52,1,341216000]}'); diff --git a/src/main/resources/schema-h2.sql b/src/main/resources/schema-h2.sql index 8cc499d..0f2779a 100644 --- a/src/main/resources/schema-h2.sql +++ b/src/main/resources/schema-h2.sql @@ -7,7 +7,6 @@ CREATE TABLE event group_version INT NOT NULL, exec_id VARCHAR(50) NOT NULL, target_id VARCHAR(50), - event_type VARCHAR(32) NOT NULL, - timestamp DATETIME NOT NULL, + event_date DATETIME NOT NULL, event_payload VARCHAR(2500) NOT NULL ); diff --git a/src/main/resources/templates/edit.html b/src/main/resources/templates/edit.html index 853d15d..f93120b 100644 --- a/src/main/resources/templates/edit.html +++ b/src/main/resources/templates/edit.html @@ -100,9 +100,15 @@ - Event-Log exportieren + Event-Log exportieren (TXT) + + + + Event-Log exportieren (SQL) Date: Fri, 17 Apr 2020 21:11:02 +0200 Subject: [PATCH 10/30] better responsive design for details --- src/main/resources/templates/details.html | 59 +++++++++++++------ src/main/resources/templates/edit.html | 3 +- .../resources/templates/fragments/groups.html | 2 +- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/main/resources/templates/details.html b/src/main/resources/templates/details.html index aa64206..ea3f8b4 100644 --- a/src/main/resources/templates/details.html +++ b/src/main/resources/templates/details.html @@ -11,32 +11,56 @@
-

+

-
+
-
+
-
+
-
+ + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
-
- Fertig +
+ Fertig - - - -
- -
+
+
+ +
+
-
+
Teilnehmer: @@ -56,13 +80,12 @@
  • - - + +
-
diff --git a/src/main/resources/templates/edit.html b/src/main/resources/templates/edit.html index f93120b..a665a11 100644 --- a/src/main/resources/templates/edit.html +++ b/src/main/resources/templates/edit.html @@ -88,12 +88,13 @@
+
Event-Historie
- Event-Log diff --git a/src/main/resources/templates/fragments/groups.html b/src/main/resources/templates/fragments/groups.html index 51b35cc..bdea713 100644 --- a/src/main/resources/templates/fragments/groups.html +++ b/src/main/resources/templates/fragments/groups.html @@ -36,7 +36,7 @@ -
+
From 59e9b3432422ec26bc67efcc5f2dcbd7f3a66695 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 21:28:21 +0200 Subject: [PATCH 11/30] better responsive design for search --- .../resources/templates/fragments/groups.html | 4 +-- src/main/resources/templates/index.html | 11 +++++--- src/main/resources/templates/search.html | 26 +++++++++---------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/resources/templates/fragments/groups.html b/src/main/resources/templates/fragments/groups.html index bdea713..f1fe02d 100644 --- a/src/main/resources/templates/fragments/groups.html +++ b/src/main/resources/templates/fragments/groups.html @@ -49,14 +49,14 @@ -
+
-
+
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 35e6587..10deac9 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -11,23 +11,26 @@
-

Meine Gruppen

+

Meine Gruppen

-

Veranstaltungen

+

+ Veranstaltungen

-

Öffentliche Gruppen

+

Öffentliche + Gruppen

-

Private Gruppen

+

Private + Gruppen

diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index 70163e9..4118057 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -17,29 +17,29 @@

Suchen

-
+
-
-
-
- Suchbegriff: +
+
+
+ Suchbegriff:
- +
- From 01b0f2b52d5412f14dc333cbe27902313b0e627c Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 21:50:16 +0200 Subject: [PATCH 12/30] better responsive design for create --- src/main/resources/templates/create.html | 25 +++++++++--------- .../resources/templates/fragments/forms.html | 26 +++++++++---------- src/main/resources/templates/search.html | 2 +- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/main/resources/templates/create.html b/src/main/resources/templates/create.html index b2f4c21..1bb5cef 100644 --- a/src/main/resources/templates/create.html +++ b/src/main/resources/templates/create.html @@ -11,11 +11,12 @@
-

Neue Gruppe

+

Neue Gruppe

-
-
-

Eigenschaften:

+
+ +

+ Eigenschaften:

@@ -24,21 +25,21 @@
-
+
-
+
-
+
-
- - -
-
+
+ + + +
diff --git a/src/main/resources/templates/fragments/forms.html b/src/main/resources/templates/fragments/forms.html index f38f3bc..1b93ee8 100644 --- a/src/main/resources/templates/fragments/forms.html +++ b/src/main/resources/templates/fragments/forms.html @@ -12,7 +12,7 @@
-
+
Gruppentitel:
-
+
Beschreibung:
@@ -50,8 +50,8 @@
-
- Gehört zu: +
+ Gehört zu:
-
- Teilnehmer +
+ Teilnehmer
@@ -102,7 +102,7 @@
- CSV: + CSV:
diff --git a/src/main/resources/templates/fragments/general.html b/src/main/resources/templates/fragments/general.html index 34e4b56..80e4a81 100644 --- a/src/main/resources/templates/fragments/general.html +++ b/src/main/resources/templates/fragments/general.html @@ -7,7 +7,6 @@ - diff --git a/src/main/resources/templates/fragments/groups.html b/src/main/resources/templates/fragments/groups.html index f1fe02d..7030f88 100644 --- a/src/main/resources/templates/fragments/groups.html +++ b/src/main/resources/templates/fragments/groups.html @@ -7,17 +7,17 @@ - Privat - Öffentlich - Veranstaltung - Parent @@ -72,15 +72,13 @@
-
+
-
- - Startseite.
diff --git a/src/main/resources/templates/history.html b/src/main/resources/templates/history.html index a472a0c..ab34ab9 100644 --- a/src/main/resources/templates/history.html +++ b/src/main/resources/templates/history.html @@ -14,12 +14,10 @@

Event-Log

-
+
- - - Datum: + Datum:
diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 10deac9..cc1284a 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -11,26 +11,24 @@
-

Meine Gruppen

+

Meine Gruppen

-

- Veranstaltungen

+

Veranstaltungen

+
-

Öffentliche - Gruppen

+

Öffentliche Gruppen

-

Private - Gruppen

+

Private Gruppen

diff --git a/src/main/resources/templates/link.html b/src/main/resources/templates/link.html index 5a5741a..c4f8f81 100644 --- a/src/main/resources/templates/link.html +++ b/src/main/resources/templates/link.html @@ -10,7 +10,7 @@
-

+

diff --git a/src/main/resources/templates/preview.html b/src/main/resources/templates/preview.html index 94b6ea0..2b78821 100644 --- a/src/main/resources/templates/preview.html +++ b/src/main/resources/templates/preview.html @@ -10,17 +10,15 @@
-

+

-
diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index 5fbda20..cb40756 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -28,18 +28,19 @@
-
From 781ead8fc07548ad5dd85dfeefb70f1354013b18 Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 23:47:10 +0200 Subject: [PATCH 15/30] add dummy integrations --- .../gruppen2/domain/model/group/Group.java | 20 +++ .../domain/model/group/GroupOptions.java | 4 + src/main/resources/templates/details.html | 129 +++++++++++++++++- 3 files changed, 147 insertions(+), 6 deletions(-) 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 628bb3a..a17343f 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -216,6 +216,26 @@ public class Group { return !parent.isEmpty(); } + public boolean hasMaterial() { + return options.isHasMaterialIntegration(); + } + + public boolean hasForums() { + return options.isHasForumsIntegration(); + } + + public boolean hasCalendar() { + return options.isHasTermineIntegration(); + } + + public boolean hasModules() { + return options.isHasModulesIntegration(); + } + + public boolean hasPortfolios() { + return options.isHasPortfolioIntegration(); + } + // ######################################## Setters ########################################## diff --git a/src/main/java/mops/gruppen2/domain/model/group/GroupOptions.java b/src/main/java/mops/gruppen2/domain/model/group/GroupOptions.java index 88d069a..f0cd1f6 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/GroupOptions.java +++ b/src/main/java/mops/gruppen2/domain/model/group/GroupOptions.java @@ -20,6 +20,8 @@ class GroupOptions { boolean hasMaterialIntegration; boolean hasTermineIntegration; boolean hasPortfolioIntegration; + boolean hasForumsIntegration; + boolean hasModulesIntegration; static GroupOptions DEFAULT() { return new GroupOptions(true, @@ -31,6 +33,8 @@ class GroupOptions { null, true, true, + true, + true, true); } } diff --git a/src/main/resources/templates/details.html b/src/main/resources/templates/details.html index e474f0f..cb43a23 100644 --- a/src/main/resources/templates/details.html +++ b/src/main/resources/templates/details.html @@ -20,26 +20,143 @@
- -
+ +
-
+
+
+ Gruppenmaterialien +
+ +
-
+
+
+ Forum +
+ + +
-
+
+
+ Gruppentermine +
+ + + +
+ + +
+
+ Gruppenportfolio +
+ +
+

Neueste Einträge:

+ +
+ +
-
+
+
+ Modulhandbuch +
+
+

Veranstaltungsinfo:

+

+ Dieses Modul vermittelt grundlegende Programmierkenntnisse in einer + objektorientierten Programmiersprache. + Darüber hinaus werden einführend Aspekte von Algorithmen und + Datenstrukturen behandelt. + Es wird keine Programmiererfahrung vorausgesetzt. +

+
    +
  • Grundlegende Begriffe der Informatik
  • +
  • Primitive Datentypen und Variablen
  • +
  • Kontrollstrukturen
  • +
  • Eigene Datentypen (Klassen) und Arrays
  • +
  • Programmstrukturen im Speicher (Heap, Stack)
  • +
  • Konzepte der Objektorientierung (Polymorphie, Schnittstellen) +
  • +
  • Rekursion
  • +
  • Fehlerbehandlung
  • +
  • Dynamische Datenstrukturen (Listen, Binärbäume, Hashing)
  • +
  • Suchen und Sortieren (ausgewählte Algorithmen, u.a. binäre + Suche, BubbleSort, QuickSort) +
  • +
  • Datenströme (Standard-Eingabe und -Ausgabe, einfache 2D-Grafik, + Dateien) +
  • +
+
+ +
From 02fe3659b35eb60de2d5c45c29152d5e9b93d9cd Mon Sep 17 00:00:00 2001 From: Christoph Date: Fri, 17 Apr 2020 23:47:23 +0200 Subject: [PATCH 16/30] database schames --- mysql/db/entrypoint/schema.sql | 2 +- mysql/db/schema-heroku.sql | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mysql/db/entrypoint/schema.sql b/mysql/db/entrypoint/schema.sql index 5a86b10..cfdfe0c 100644 --- a/mysql/db/entrypoint/schema.sql +++ b/mysql/db/entrypoint/schema.sql @@ -5,6 +5,6 @@ CREATE TABLE event group_version INT NOT NULL, exec_id VARCHAR(32) NOT NULL, target_id VARCHAR(32), - event_type VARCHAR(16) NOT NULL, + event_date DATETIME NOT NULL, event_payload JSON NOT NULL ); diff --git a/mysql/db/schema-heroku.sql b/mysql/db/schema-heroku.sql index f8bf704..fd92919 100644 --- a/mysql/db/schema-heroku.sql +++ b/mysql/db/schema-heroku.sql @@ -1,3 +1,5 @@ +DROP TABLE IF EXISTS event; + CREATE TABLE event ( event_id INT PRIMARY KEY AUTO_INCREMENT, @@ -5,6 +7,6 @@ CREATE TABLE event group_version INT NOT NULL, exec_id VARCHAR(32) NOT NULL, target_id VARCHAR(32), - event_type VARCHAR(16) NOT NULL, + event_date DATETIME NOT NULL, event_payload TEXT NOT NULL ); From 9dd6794a1cec0db46878222c3edbe58cc12bec2a Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 00:17:07 +0200 Subject: [PATCH 17/30] redo api --- .../mops/gruppen2/domain/event/Event.java | 12 + .../domain/service/EventStoreService.java | 18 ++ .../domain/service/helper/APIHelper.java | 18 +- .../domain/service/helper/CommonHelper.java | 8 + .../service/helper/ProjectionHelper.java | 19 ++ .../infrastructure/api/GroupWrapper.java | 27 ++ .../controller/APIController.java | 38 ++- .../gruppen2/persistance/EventRepository.java | 6 + src/main/resources/templates/details.html | 2 +- src/test/java/mops/gruppen2/TestBuilder.java | 297 ------------------ 10 files changed, 133 insertions(+), 312 deletions(-) delete mode 100644 src/test/java/mops/gruppen2/TestBuilder.java diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index eee06b4..9f87d20 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -82,6 +82,18 @@ public abstract class Event { applyEvent(group); } + public void apply(Group group) throws EventException { + log.trace("Event wird angewendet:\t{}", this); + + if (version == 0) { + throw new BadArgumentException("Event wurde nicht initialisiert."); + } + + checkGroupIdMatch(group.getId()); + group.updateVersion(version); + applyEvent(group); + } + private void checkGroupIdMatch(UUID groupid) throws IdMismatchException { // CreateGroupEvents müssen die Id erst initialisieren if (this instanceof CreateGroupEvent) { diff --git a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java index e17778a..31ea0b8 100644 --- a/src/main/java/mops/gruppen2/domain/service/EventStoreService.java +++ b/src/main/java/mops/gruppen2/domain/service/EventStoreService.java @@ -5,12 +5,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.exception.BadPayloadException; +import mops.gruppen2.domain.service.helper.CommonHelper; import mops.gruppen2.domain.service.helper.FileHelper; import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.dto.EventDTO; import org.springframework.stereotype.Service; import java.sql.Timestamp; +import java.util.Collection; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -102,6 +104,14 @@ public class EventStoreService { return getEventsFromDTOs(eventStore.findGroupEvents(groupId.toString())); } + public List findGroupEvents(List ids) { + return ids.stream() + .map(id -> eventStore.findGroupEvents(id.toString())) + .map(EventStoreService::getEventsFromDTOs) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + public List findGroupPayloads(UUID groupId) { return eventStore.findGroupPayloads(groupId.toString()); } @@ -109,4 +119,12 @@ public class EventStoreService { public List findGroupDTOs(UUID groupid) { return eventStore.findGroupEvents(groupid.toString()); } + + public List findChangedGroups(long eventid) { + return CommonHelper.stringsToUUID(eventStore.findChangedGroupIds(eventid)); + } + + public long findMaxEventId() { + return eventStore.findMaxEventId(); + } } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java index 906d979..ec2e925 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java @@ -3,13 +3,25 @@ package mops.gruppen2.domain.service.helper; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j2; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.infrastructure.api.GroupRequestWrapper; +import mops.gruppen2.infrastructure.api.GroupWrapper; + +import java.util.List; +import java.util.stream.Collectors; //TODO: sinnvolles format @Log4j2 @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class APIHelper { - /*public static GroupRequestWrapper wrap(long status, List groupList) { - return new GroupRequestWrapper(status, groupList); - }*/ + public static GroupRequestWrapper wrap(long status, List groupList) { + return new GroupRequestWrapper(status, wrap(groupList)); + } + + public static List wrap(List groups) { + return groups.stream() + .map(GroupWrapper::new) + .collect(Collectors.toUnmodifiableList()); + } } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/CommonHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/CommonHelper.java index 4694501..25a8575 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/CommonHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/CommonHelper.java @@ -4,7 +4,9 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j2; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; @Log4j2 @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -13,4 +15,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 changedGroupIds) { + return changedGroupIds.stream() + .map(UUID::fromString) + .collect(Collectors.toUnmodifiableList()); + } } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java index f4f9011..022166d 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java @@ -7,6 +7,9 @@ import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.infrastructure.GroupCache; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -19,6 +22,22 @@ import java.util.UUID; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class ProjectionHelper { + + public static List project(List events) { + Map groups = new HashMap<>(); + + if (events.isEmpty()) { + return Collections.emptyList(); + } + + log.trace(groups); + log.trace(events); + + events.forEach(event -> event.apply(getOrCreateGroup(groups, event.getGroupid()))); + + return new ArrayList<>(groups.values()); + } + public static void project(Map groups, List events, GroupCache cache) { if (events.isEmpty()) { return; diff --git a/src/main/java/mops/gruppen2/infrastructure/api/GroupWrapper.java b/src/main/java/mops/gruppen2/infrastructure/api/GroupWrapper.java index 2fb3fba..75b628d 100644 --- a/src/main/java/mops/gruppen2/infrastructure/api/GroupWrapper.java +++ b/src/main/java/mops/gruppen2/infrastructure/api/GroupWrapper.java @@ -1,4 +1,31 @@ package mops.gruppen2.infrastructure.api; +import lombok.Value; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.Type; +import mops.gruppen2.domain.model.group.User; + +import java.util.List; +import java.util.UUID; + +@Value public class GroupWrapper { + + UUID groupid; + Type type; + UUID parent; + String title; + String description; + List admins; + List regulars; + + public GroupWrapper(Group group) { + groupid = group.getId(); + type = group.getType(); + parent = group.getParent(); + title = group.getTitle(); + description = group.getDescription(); + admins = group.getAdmins(); + regulars = group.getRegulars(); + } } diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java b/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java index ac02052..3e3f9b9 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java @@ -1,13 +1,27 @@ package mops.gruppen2.infrastructure.controller; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.aspect.annotation.TraceMethodCalls; +import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.domain.service.helper.APIHelper; +import mops.gruppen2.domain.service.helper.ProjectionHelper; +import mops.gruppen2.infrastructure.GroupCache; +import mops.gruppen2.infrastructure.api.GroupRequestWrapper; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + /** * Api zum Datenabgleich. */ @@ -18,8 +32,7 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/gruppen2/api") public class APIController { - //TODO: redo api - + private final GroupCache cache; private final EventStoreService eventStoreService; /** @@ -28,38 +41,41 @@ public class APIController { * * @param eventId Die Event-ID, welche der Anfragesteller beim letzten Aufruf erhalten hat */ - /*@GetMapping("/update/{id}") + @GetMapping("/update/{id}") @Secured("ROLE_api_user") @ApiOperation("Gibt veränderte Gruppen zurück") public GroupRequestWrapper getApiUpdate(@ApiParam("Letzte gespeicherte EventId des Anfragestellers") @PathVariable("id") long eventId) { return APIHelper.wrap(eventStoreService.findMaxEventId(), - projectionHelper.projectChangedGroups(eventId)); - }*/ + ProjectionHelper.project(eventStoreService.findGroupEvents(eventStoreService.findChangedGroups(eventId)))); + } /** * Gibt die Gruppen-IDs von Gruppen, in welchen der übergebene Nutzer teilnimmt, zurück. */ - /*@GetMapping("/usergroups/{id}") + @GetMapping("/usergroups/{id}") @Secured("ROLE_api_user") @ApiOperation("Gibt Gruppen zurück, in welchen ein Nutzer teilnimmt") public List getApiUserGroups(@ApiParam("Nutzer-Id") @PathVariable("id") String userId) { - return CommonHelper.uuidsToString(eventStoreService.findExistingUserGroups(userId)); - }*/ + return cache.userGroups(userId).stream() + .map(Group::getId) + .map(UUID::toString) + .collect(Collectors.toUnmodifiableList()); + } /** * Konstruiert eine einzelne, vollständige Gruppe. */ - /*@GetMapping("/group/{id}") + @GetMapping("/group/{id}") @Secured("ROLE_api_user") @ApiOperation("Gibt die Gruppe mit der als Parameter mitgegebenden groupId zurück") public Group getApiGroup(@ApiParam("Gruppen-Id der gefordeten Gruppe") @PathVariable("id") String groupId) { - return projectionHelper.projectGroupById(UUID.fromString(groupId)); - }*/ + return cache.group(UUID.fromString(groupId)); + } } diff --git a/src/main/java/mops/gruppen2/persistance/EventRepository.java b/src/main/java/mops/gruppen2/persistance/EventRepository.java index 5ce23fb..8d21c79 100644 --- a/src/main/java/mops/gruppen2/persistance/EventRepository.java +++ b/src/main/java/mops/gruppen2/persistance/EventRepository.java @@ -23,4 +23,10 @@ public interface EventRepository extends CrudRepository { @Query("SELECT event_payload FROM event WHERE group_id = :groupid") List findGroupPayloads(@Param("groupid") String groupId); + + @Query("SELECT MAX(event_id) FROM event") + long findMaxEventId(); + + @Query("SELECT DISTINCT group_id FROM event WHERE event_id > :eventid") + List findChangedGroupIds(@Param("eventid") long eventid); } diff --git a/src/main/resources/templates/details.html b/src/main/resources/templates/details.html index cb43a23..a271bb4 100644 --- a/src/main/resources/templates/details.html +++ b/src/main/resources/templates/details.html @@ -20,7 +20,7 @@
- +
diff --git a/src/test/java/mops/gruppen2/TestBuilder.java b/src/test/java/mops/gruppen2/TestBuilder.java deleted file mode 100644 index d1c666a..0000000 --- a/src/test/java/mops/gruppen2/TestBuilder.java +++ /dev/null @@ -1,297 +0,0 @@ -package mops.gruppen2; - -public class TestBuilder { - - /*private static final Faker faker = new Faker(); - - public static Account account(String name) { - return new Account(name, - "", - "", - "", - "", - null); - } - - public static Group apply(Group group, Event... events) { - for (Event event : events) { - event.apply(group); - } - - return group; - } - - public static Group apply(Event... events) { - return apply(new Group(), events); - } - - *//** - * Baut eine UUID. - * - * @param id Integer id - * - * @return UUID - *//* - public static UUID uuidMock(int id) { - String idString = String.valueOf(Math.abs(id + 1)); - return UUID.fromString("00000000-0000-0000-0000-" - + "0".repeat(11 - idString.length()) - + idString); - } - - *//** - * Generiert ein EventLog mit mehreren Gruppen und Usern. - * - * @param count Gruppenanzahl - * @param membercount Mitgliederanzahl pro Gruppe - * - * @return Eventliste - *//* - public static List completePublicGroups(int count, int membercount) { - return IntStream.range(0, count) - .parallel() - .mapToObj(i -> completePublicGroup(membercount)) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } - - public static List completePrivateGroups(int count, int membercount) { - return IntStream.range(0, count) - .parallel() - .mapToObj(i -> completePrivateGroup(membercount)) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } - - public static List completePublicGroup(int membercount) { - List eventList = new ArrayList<>(); - UUID groupId = UUID.randomUUID(); - - eventList.add(createPublicGroupEvent(groupId)); - eventList.add(updateGroupTitleEvent(groupId)); - eventList.add(updateGroupDescriptionEvent(groupId)); - eventList.add(new SetLimitEvent(groupId, "fgsadggas", new Limit(Long.MAX_VALUE))); - eventList.addAll(addUserEvents(membercount, groupId)); - - return eventList; - } - - public static List completePrivateGroup(int membercount) { - List eventList = new ArrayList<>(); - UUID groupId = UUID.randomUUID(); - - eventList.add(createPrivateGroupEvent(groupId)); - eventList.add(updateGroupTitleEvent(groupId)); - eventList.add(updateGroupDescriptionEvent(groupId)); - eventList.add(new SetLimitEvent(groupId, "fgsadggas", new Limit(Long.MAX_VALUE))); - eventList.addAll(addUserEvents(membercount, groupId)); - - return eventList; - } - - public static List completePublicGroup() { - return completePublicGroup(100); - } - - public static List completePrivateGroup() { - return completePrivateGroup(100); - } - - *//** - * Generiert mehrere CreateGroupEvents, 1 <= groupId <= count. - * - * @param count Anzahl der verschiedenen Gruppen - * - * @return Eventliste - *//* - public static List createPublicGroupEvents(int count) { - return IntStream.range(0, count) - .parallel() - .mapToObj(i -> createPublicGroupEvent()) - .collect(Collectors.toList()); - } - - public static List createPrivateGroupEvents(int count) { - return IntStream.range(0, count) - .parallel() - .mapToObj(i -> createPublicGroupEvent()) - .collect(Collectors.toList()); - } - - public static List createMixedGroupEvents(int count) { - return IntStream.range(0, count) - .parallel() - .mapToObj(i -> faker.random().nextInt(0, 1) > 0.5 - ? createPublicGroupEvent() - : createPrivateGroupEvent()) - .collect(Collectors.toList()); - } - - public static List createPrivateGroupEvents(UUID groupId) { - return (new ArrayList<>()).addAll(createGroupEvent(groupId)), - new SetTypeEvent(groupId); - } - - public static Event createPrivateGroupEvent() { - return createPrivateGroupEvent(UUID.randomUUID()); - } - - public static Event createPublicGroupEvent(UUID groupId) { - return createGroupEvent(groupId, Type.PUBLIC); - } - - public static Event createPublicGroupEvent() { - return createPublicGroupEvent(UUID.randomUUID()); - } - - public static Event createGroupEvent(UUID groupId) { - return new CreateGroupEvent(groupId, - faker.random().hex(), - LocalDateTime.now()); - } - - public static Event createLectureEvent() { - return createLectureEvent(UUID.randomUUID()); - } - - public static Event createLectureEvent(UUID groupId) { - return new CreateGroupEvent( - groupId, - faker.random().hex(), - null, - Type.LECTURE - ); - } - - *//** - * Generiert mehrere AddUserEvents für eine Gruppe, 1 <= user_id <= count. - * - * @param count Anzahl der Mitglieder - * @param groupId Gruppe, zu welcher geaddet wird - * - * @return Eventliste - *//* - public static List addUserEvents(int count, UUID groupId) { - return IntStream.range(0, count) - .parallel() - .mapToObj(i -> addUserEvent(groupId, String.valueOf(i))) - .collect(Collectors.toList()); - } - - public static Event addUserEvent(UUID groupId, String userId) { - String firstname = firstname(); - String lastname = lastname(); - - return new AddMemberEvent( - groupId, - userId, - firstname, - lastname, - firstname + "." + lastname + "@mail.de" - ); - } - - public static Event addUserEvent(UUID groupId) { - return addUserEvent(groupId, faker.random().hex()); - } - - // Am besten einfach nicht benutzen - public static List deleteUserEvents(int count, List eventList) { - List removeEvents = new ArrayList<>(); - List shuffle = eventList.parallelStream() - .filter(event -> event instanceof AddMemberEvent) - .collect(Collectors.toList()); - - Collections.shuffle(shuffle); - - for (Event event : shuffle) { - removeEvents.add(new KickMemberEvent(event.getGroupid(), event.getTarget())); - - if (removeEvents.size() >= count) { - break; - } - } - - return removeEvents; - } - - *//** - * Erzeugt mehrere DeleteUserEvents, sodass eine Gruppe komplett geleert wird. - * - * @param group Gruppe welche geleert wird - * - * @return Eventliste - *//* - public static List deleteUserEvents(Group group) { - return group.getMemberships().parallelStream() - .map(user -> deleteUserEvent(group.getGroupid(), user.getUserId())) - .collect(Collectors.toList()); - } - - public static Event deleteUserEvent(UUID groupId, String userId) { - return new KickMemberEvent( - groupId, - userId - ); - } - - public static Event updateGroupDescriptionEvent(UUID groupId) { - return updateGroupDescriptionEvent(groupId, quote()); - } - - public static Event updateGroupDescriptionEvent(UUID groupId, String description) { - return new SetDescriptionEvent( - groupId, - faker.random().hex(), - description - ); - } - - public static Event updateGroupTitleEvent(UUID groupId) { - return updateGroupTitleEvent(groupId, champion()); - } - - public static Event updateGroupTitleEvent(UUID groupId, String title) { - return new SetTitleEvent( - groupId, - faker.random().hex(), - title - ); - } - - public static Event updateUserLimitMaxEvent(UUID groupId) { - return new SetLimitEvent(groupId, firstname(), Long.MAX_VALUE); - } - - public static Event updateRoleEvent(UUID groupId, String userId, Role role) { - return new UpdateRoleEvent( - groupId, - userId, - role - ); - } - - public static Event deleteGroupEvent(UUID groupId) { - return new DestroyGroupEvent(groupId, faker.random().hex()); - } - - private static String firstname() { - return clean(faker.name().firstName()); - } - - private static String lastname() { - return clean(faker.name().lastName()); - } - - private static String champion() { - return clean(faker.leagueOfLegends().champion()); - } - - private static String quote() { - return clean(faker.leagueOfLegends().quote()); - } - - private static String clean(String string) { - return string.replaceAll("['\";,]", ""); - }*/ -} From baafa806ad2aa0f1c4290254e261b4403e2c60fe Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 00:18:05 +0200 Subject: [PATCH 18/30] redo api --- src/main/java/mops/gruppen2/domain/model/group/Group.java | 2 ++ .../java/mops/gruppen2/domain/service/helper/APIHelper.java | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) 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 a17343f..f06a0b0 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -57,6 +57,7 @@ public class Group { private GroupMeta meta = GroupMeta.EMPTY(); + //TODO: UI set + use for options private GroupOptions options = GroupOptions.DEFAULT(); // Inhalt @@ -64,6 +65,7 @@ public class Group { private Description description = Description.EMPTY(); + //TODO: Asciidoc description private Body body; // Integrationen diff --git a/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java index ec2e925..7f1d283 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/APIHelper.java @@ -10,7 +10,6 @@ import mops.gruppen2.infrastructure.api.GroupWrapper; import java.util.List; import java.util.stream.Collectors; -//TODO: sinnvolles format @Log4j2 @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class APIHelper { From 83ba5480d5cdc436bc29167527df2ed176fd4ccc Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 13:33:44 +0200 Subject: [PATCH 19/30] smaller padding on small screens --- .../controller/GroupDetailsController.java | 2 +- src/main/resources/static/css/index.css | 3 - src/main/resources/static/css/style.css | 6 +- src/main/resources/templates/create.html | 47 ++- src/main/resources/templates/details.html | 348 +++++++++--------- src/main/resources/templates/edit.html | 278 +++++++------- .../resources/templates/fragments/forms.html | 2 +- src/main/resources/templates/history.html | 35 -- src/main/resources/templates/index.html | 35 +- src/main/resources/templates/log.html | 31 ++ src/main/resources/templates/search.html | 53 ++- 11 files changed, 411 insertions(+), 429 deletions(-) delete mode 100644 src/main/resources/templates/history.html create mode 100644 src/main/resources/templates/log.html diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java index e7043fd..a75abf6 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/GroupDetailsController.java @@ -108,7 +108,7 @@ public class GroupDetailsController { model.addAttribute("events", eventStoreService.findGroupEvents(UUID.fromString(groupId))); - return "history"; + return "log"; } @RolesAllowed({"ROLE_orga", "ROLE_studentin"}) diff --git a/src/main/resources/static/css/index.css b/src/main/resources/static/css/index.css index a5a3922..08993cb 100644 --- a/src/main/resources/static/css/index.css +++ b/src/main/resources/static/css/index.css @@ -1,19 +1,16 @@ /*Grouplist Hover Effect*/ .content { - padding-right: 20px; background: #e6f4ff; box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075); } .content:hover { - padding-right: 10px; background: #d4ecff; box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .25); } .content:hover .content-heading { padding-bottom: 10px; - padding-left: 10px; } .content-text-in { diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index 7bd21c4..c9861fc 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -76,11 +76,9 @@ h1, h3 { /*Button*/ .btn { - max-width: 250px; } .btn-spacer { - max-width: 250px; } /*Badges*/ @@ -107,6 +105,10 @@ h1, h3 { } /*Input*/ +.input-group { + width: auto; +} + .input-group-append { max-width: 50%; overflow: hidden; diff --git a/src/main/resources/templates/create.html b/src/main/resources/templates/create.html index 4986c29..8c0d28b 100644 --- a/src/main/resources/templates/create.html +++ b/src/main/resources/templates/create.html @@ -9,38 +9,35 @@
-
+

Neue Gruppe

-

Neue Gruppe

+
+
+

Eigenschaften:

- -
-

Eigenschaften:

+ +
- -
+ - +
+ +
-
- -
- - -
-
- - -
+ +
-
- - -
- -
+ +
+
+ +
+ + +
+
diff --git a/src/main/resources/templates/details.html b/src/main/resources/templates/details.html index a271bb4..0a4a400 100644 --- a/src/main/resources/templates/details.html +++ b/src/main/resources/templates/details.html @@ -9,199 +9,197 @@
-
+

-

+
+ +
-
- -
- -
+
- -
- -
-
- Gruppenmaterialien -
- - - + +
+ +
+
+ Gruppenmaterialien
- -
-
- Forum -
- - - - + - - -
-
- Gruppentermine -
- - - - -
- - -
-
- Gruppenportfolio -
- -
-

Neueste Einträge:

- -
- - -
- - -
-
- Modulhandbuch -
- -
-

Veranstaltungsinfo:

-

- Dieses Modul vermittelt grundlegende Programmierkenntnisse in einer - objektorientierten Programmiersprache. - Darüber hinaus werden einführend Aspekte von Algorithmen und - Datenstrukturen behandelt. - Es wird keine Programmiererfahrung vorausgesetzt. -

-
    -
  • Grundlegende Begriffe der Informatik
  • -
  • Primitive Datentypen und Variablen
  • -
  • Kontrollstrukturen
  • -
  • Eigene Datentypen (Klassen) und Arrays
  • -
  • Programmstrukturen im Speicher (Heap, Stack)
  • -
  • Konzepte der Objektorientierung (Polymorphie, Schnittstellen) -
  • -
  • Rekursion
  • -
  • Fehlerbehandlung
  • -
  • Dynamische Datenstrukturen (Listen, Binärbäume, Hashing)
  • -
  • Suchen und Sortieren (ausgewählte Algorithmen, u.a. binäre - Suche, BubbleSort, QuickSort) -
  • -
  • Datenströme (Standard-Eingabe und -Ausgabe, einfache 2D-Grafik, - Dateien) -
  • -
-
- - +
-
- -
- Fertig + +
+
+ Forum +
-
-
- -
-
+ + + +
+ + +
+
+ Gruppentermine +
+ + + + +
+ + +
+
+ Gruppenportfolio +
+ +
+

Neueste Einträge:

+ +
+ + +
+ + +
+
+ Modulhandbuch +
+ +
+

Veranstaltungsinfo:

+

+ Dieses Modul vermittelt grundlegende Programmierkenntnisse in einer + objektorientierten Programmiersprache. + Darüber hinaus werden einführend Aspekte von Algorithmen und + Datenstrukturen behandelt. + Es wird keine Programmiererfahrung vorausgesetzt. +

+
    +
  • Grundlegende Begriffe der Informatik
  • +
  • Primitive Datentypen und Variablen
  • +
  • Kontrollstrukturen
  • +
  • Eigene Datentypen (Klassen) und Arrays
  • +
  • Programmstrukturen im Speicher (Heap, Stack)
  • +
  • Konzepte der Objektorientierung (Polymorphie, Schnittstellen) +
  • +
  • Rekursion
  • +
  • Fehlerbehandlung
  • +
  • Dynamische Datenstrukturen (Listen, Binärbäume, Hashing)
  • +
  • Suchen und Sortieren (ausgewählte Algorithmen, u.a. binäre + Suche, BubbleSort, QuickSort) +
  • +
  • Datenströme (Standard-Eingabe und -Ausgabe, einfache 2D-Grafik, + Dateien) +
  • +
+
+ +
- -
- -
- Teilnehmer: - -
+
+ +
+ Fertig - -
-
- -
+
+
+ +
+
+
+
- -
-
    -
  • - - -
  • -
-
+ +
+ +
+ Teilnehmer: + +
+ + +
+
+ +
+
+ + +
+
    +
  • + + +
  • +
diff --git a/src/main/resources/templates/edit.html b/src/main/resources/templates/edit.html index 6d4141a..0756e05 100644 --- a/src/main/resources/templates/edit.html +++ b/src/main/resources/templates/edit.html @@ -9,150 +9,148 @@
-
-

+

- -
-
- Fertig + +
+
+ Fertig -
-
- -
-
-
-
- - -
-
- Einladungslink: -
- -
- -
-
- - -
-
- Eigenschaften -
- - -
-
- -
- -
- -
+
+ +
- - -
-
- -
- -
- -
-
-
- - -
-
- -
- -
- -
-
-
-
- - - - - -
-
- Teilnehmer -
- -
    -
  • -
    - - -
    - -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
  • -
+ + +
+
+ Einladungslink: +
+ +
+ +
+
+ + +
+
+ Eigenschaften +
+ + +
+
+ +
+ +
+ +
+
+
+ + +
+
+ +
+ +
+ +
+
+
+ + +
+
+ +
+ +
+ +
+
+
+
+ + + + + +
+
+ Teilnehmer +
+ +
    +
  • +
    + + +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
  • +
+
diff --git a/src/main/resources/templates/fragments/forms.html b/src/main/resources/templates/fragments/forms.html index 9f12e1e..17607d7 100644 --- a/src/main/resources/templates/fragments/forms.html +++ b/src/main/resources/templates/fragments/forms.html @@ -66,7 +66,7 @@ -
+
diff --git a/src/main/resources/templates/history.html b/src/main/resources/templates/history.html deleted file mode 100644 index ab34ab9..0000000 --- a/src/main/resources/templates/history.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -
-
- -

Event-Log

- -
-
- - - Datum: - -
- -
- - - -
-
- -
-
- - - diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index cc1284a..8f918c4 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -9,29 +9,26 @@
-
+

Meine Gruppen

-

Meine Gruppen

+ +
+

Veranstaltungen

- -
-

Veranstaltungen

+
+
-
-
+
+

Öffentliche Gruppen

+
+
-
-

Öffentliche Gruppen

-
-
- -
-

Private Gruppen

-
-
+
+

Private Gruppen

+
diff --git a/src/main/resources/templates/log.html b/src/main/resources/templates/log.html new file mode 100644 index 0000000..f60bd36 --- /dev/null +++ b/src/main/resources/templates/log.html @@ -0,0 +1,31 @@ + + + + + +
+

Event-Log

+ +
+
+ + + Datum: + +
+ +
+ + + +
+
+
+ + + diff --git a/src/main/resources/templates/search.html b/src/main/resources/templates/search.html index cb40756..4ed2c60 100644 --- a/src/main/resources/templates/search.html +++ b/src/main/resources/templates/search.html @@ -12,43 +12,40 @@
-
+

Suchen

-

Suchen

- - -
-
-
-
-
+ +
+ +
+
+
Suchbegriff: -
-
- - +
- - -
+ - -
+
+ + +
From 3a8deb5cad2ef321e2adc3b383bd5bbf49f2be8b Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 15:48:11 +0200 Subject: [PATCH 20/30] test helpers --- src/test/java/mops/gruppen2/GroupBuilder.java | 154 ++++++++++++++++++ src/test/java/mops/gruppen2/TestHelper.java | 15 ++ 2 files changed, 169 insertions(+) create mode 100644 src/test/java/mops/gruppen2/GroupBuilder.java create mode 100644 src/test/java/mops/gruppen2/TestHelper.java diff --git a/src/test/java/mops/gruppen2/GroupBuilder.java b/src/test/java/mops/gruppen2/GroupBuilder.java new file mode 100644 index 0000000..154c27c --- /dev/null +++ b/src/test/java/mops/gruppen2/GroupBuilder.java @@ -0,0 +1,154 @@ +package mops.gruppen2; + +import mops.gruppen2.domain.event.AddMemberEvent; +import mops.gruppen2.domain.event.CreateGroupEvent; +import mops.gruppen2.domain.event.DestroyGroupEvent; +import mops.gruppen2.domain.event.Event; +import mops.gruppen2.domain.event.KickMemberEvent; +import mops.gruppen2.domain.event.SetDescriptionEvent; +import mops.gruppen2.domain.event.SetInviteLinkEvent; +import mops.gruppen2.domain.event.SetLimitEvent; +import mops.gruppen2.domain.event.SetTitleEvent; +import mops.gruppen2.domain.event.SetTypeEvent; +import mops.gruppen2.domain.event.UpdateRoleEvent; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.Role; +import mops.gruppen2.domain.model.group.Type; +import mops.gruppen2.domain.model.group.User; +import mops.gruppen2.domain.model.group.wrapper.Description; +import mops.gruppen2.domain.model.group.wrapper.Limit; +import mops.gruppen2.domain.model.group.wrapper.Link; +import mops.gruppen2.domain.model.group.wrapper.Title; +import mops.gruppen2.infrastructure.GroupCache; + +import java.time.LocalDateTime; +import java.util.UUID; + +import static mops.gruppen2.TestHelper.uuid; + +public final class GroupBuilder { + + private final UUID groupid; + private int version; + private final GroupCache groupCache; + private final Group group = Group.EMPTY(); + + private GroupBuilder(GroupCache cache, UUID id) { + groupCache = cache; + groupid = id; + } + + /** + * Erzeugt neuen GruppenBuilder mit Cache und ID + */ + public static GroupBuilder get(GroupCache cache, int id) { + return new GroupBuilder(cache, uuid(id)); + } + + /** + * Initialisiert Gruppe mit Id, Creator und Zeit + */ + public GroupBuilder group() { + return apply(new CreateGroupEvent(groupid, "TEST", LocalDateTime.now())); + } + + /** + * Initialisiert TestAdmin + */ + public GroupBuilder testadmin() { + apply(new AddMemberEvent(groupid, "TEST", "TEST", new User("TEST"))); + return apply(new UpdateRoleEvent(groupid, "TEST", "TEST", Role.ADMIN)); + } + + /** + * Fügt Nutzer hinzu + */ + public GroupBuilder add(String userid) { + return apply(new AddMemberEvent(groupid, "TEST", userid, new User(userid))); + } + + /** + * Entfernt Nutzer + */ + public GroupBuilder kick(String userid) { + return apply(new KickMemberEvent(groupid, "TEST", userid)); + } + + public GroupBuilder limit(int i) { + return apply(new SetLimitEvent(groupid, "TEST", new Limit(i))); + } + + /** + * Macht Nutzer zu Admin + */ + public GroupBuilder admin(String userid) { + return apply(new UpdateRoleEvent(groupid, "TEST", userid, Role.ADMIN)); + } + + /** + * Macht Nutzer zu regulärem + */ + public GroupBuilder regular(String userid) { + return apply(new UpdateRoleEvent(groupid, "TEST", userid, Role.REGULAR)); + } + + /** + * Macht Gruppe öffentlich + */ + public GroupBuilder publik() { + return apply(new SetTypeEvent(groupid, "TEST", Type.PUBLIC)); + } + + /** + * Macht Gruppe privat + */ + public GroupBuilder privat() { + return apply(new SetTypeEvent(groupid, "TEST", Type.PRIVATE)); + } + + /** + * Macht Gruppe zu Veranstaltung + */ + public GroupBuilder lecture() { + return apply(new SetTypeEvent(groupid, "TEST", Type.LECTURE)); + } + + /** + * Setzt Beschreibung + */ + public GroupBuilder desc(String descr) { + return apply(new SetDescriptionEvent(groupid, "TEST", new Description(descr))); + } + + /** + * Setzt Titel + */ + public GroupBuilder title(String titl) { + return apply(new SetTitleEvent(groupid, "TEST", new Title(titl))); + } + + /** + * Setzt Link + */ + public GroupBuilder link(int lnk) { + return apply(new SetInviteLinkEvent(groupid, "TEST", new Link(uuid(lnk).toString()))); + } + + /** + * Löscht Gruppe + */ + public GroupBuilder destroy() { + return apply(new DestroyGroupEvent(groupid, "TEST")); + } + + public Group build() { + return group; + } + + private GroupBuilder apply(Event event) { + version++; + event.init(version); + event.apply(group, groupCache); + return this; + } +} diff --git a/src/test/java/mops/gruppen2/TestHelper.java b/src/test/java/mops/gruppen2/TestHelper.java new file mode 100644 index 0000000..7895df3 --- /dev/null +++ b/src/test/java/mops/gruppen2/TestHelper.java @@ -0,0 +1,15 @@ +package mops.gruppen2; + +import java.util.UUID; + +public final class TestHelper { + + public static UUID uuid(int id) { + String num = String.valueOf(id); + String string = "00000000-0000-0000-0000-"; + string += "0".repeat(12 - num.length()); + string += num; + + return UUID.fromString(string); + } +} From 3d42e87dca7d9715b87e796de9b2c9c3869f05ac Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 15:48:23 +0200 Subject: [PATCH 21/30] cache test --- .../domain/event/DestroyGroupEvent.java | 2 +- .../mops/gruppen2/domain/event/Event.java | 10 +- .../gruppen2/infrastructure/GroupCache.java | 38 ++- .../infrastructure/GroupCacheTest.java | 312 ++++++++++++++++++ 4 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java diff --git a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java index 989db37..18791a6 100644 --- a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java @@ -20,7 +20,7 @@ public class DestroyGroupEvent extends Event { @Override protected void updateCache(GroupCache cache, Group group) { - cache.groupsRemove(group); + cache.groupsRemove(groupid); } @Override diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index 9f87d20..15d6680 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -77,9 +77,15 @@ public abstract class Event { } checkGroupIdMatch(group.getId()); - updateCache(cache, group); - group.updateVersion(version); applyEvent(group); + updateCache(cache, group); + + // Danach hat die Gruppe nur Nullfelder + if (this instanceof DestroyGroupEvent) { + return; + } + + group.updateVersion(version); } public void apply(Group group) throws EventException { diff --git a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java index 10afc84..736c975 100644 --- a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java +++ b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java @@ -3,6 +3,8 @@ package mops.gruppen2.infrastructure; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.exception.GroupNotFoundException; +import mops.gruppen2.domain.exception.IdMismatchException; +import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.Type; import mops.gruppen2.domain.service.EventStoreService; @@ -29,7 +31,7 @@ public class GroupCache { private final Map groups = new HashMap<>(); private final Map links = new HashMap<>(); - private final Map> users = new HashMap<>(); + private final Map> users = new HashMap<>(); // Wird vielleicht zu groß? private final Map> types = new EnumMap<>(Type.class); @@ -60,6 +62,14 @@ public class GroupCache { return links.get(link); } + public List groups() { + if (groups.isEmpty()) { + return Collections.emptyList(); + } + + return List.copyOf(groups.values()); + } + public List userGroups(String userid) { if (!users.containsKey(userid)) { return Collections.emptyList(); @@ -115,6 +125,9 @@ public class GroupCache { public void usersPut(String userid, Group group) { + if (!group.isMember(userid)) { + throw new UserNotFoundException("User ist kein Mitglied, Gruppe nicht gecached."); + } if (!users.containsKey(userid)) { users.put(userid, new ArrayList<>()); log.debug("Ein User wurde dem Cache hinzugefügt."); @@ -132,18 +145,34 @@ public class GroupCache { } public void groupsPut(UUID groupid, Group group) { + if (group.getId() != groupid) { + throw new IdMismatchException("ID passt nicht zu Gruppe, Gruppe nicht gecached."); + } + groups.put(groupid, group); } - public void groupsRemove(Group group) { - groups.remove(group.getId()); + public void groupsRemove(UUID groupid) { + if (!groups.containsKey(groupid)) { + return; + } + + groups.remove(groupid); } public void linksPut(String link, Group group) { + if (!link.equals(group.getLink())) { + throw new IdMismatchException("Link passt nicht zu Gruppe, Gruppe nicht gecached."); + } + links.put(link, group); } public void linksRemove(String link) { + if (!links.containsKey(link)) { + return; + } + links.remove(link); } @@ -152,6 +181,9 @@ public class GroupCache { types.put(type, new ArrayList<>()); log.debug("Ein Typ wurde dem Cache hinzugefügt."); } + if (group.getType() != type) { + throw new IdMismatchException("Typ passt nicht zu Gruppe, Gruppe nicht gecached."); + } types.get(type).add(group); } diff --git a/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java b/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java new file mode 100644 index 0000000..071289f --- /dev/null +++ b/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java @@ -0,0 +1,312 @@ +package mops.gruppen2.infrastructure; + +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.exception.GroupNotFoundException; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.service.EventStoreService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +// Kann nur indirket über events getestet werden, diese werden also "mitgetestet" +class GroupCacheTest { + + private GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(Mockito.mock(EventStoreService.class)); + } + + @Test + void groups_noGroups() { + assertThat(cache.groups()).isEmpty(); + } + + @Test + void group_groupNotFound() { + assertThatThrownBy(() -> cache.group(uuid(1))) + .isInstanceOf(GroupNotFoundException.class); + } + + @Test + void group_linkNotFound() { + assertThatThrownBy(() -> cache.group("00000000-0000-0000-0000-000000000000")) + .isInstanceOf(GroupNotFoundException.class); + } + + @Test + void group_groupFound() { + Group group = GroupBuilder.get(cache, 1).group().build(); + + assertThat(cache.group(uuid(1))).isEqualTo(group); + } + + @Test + void group_linkFound() { + Group group = GroupBuilder.get(cache, 1).group().build(); + + assertThat(cache.group(group.getLink())).isEqualTo(group); + } + + @Test + void userGroups_noGroups() { + assertThat(cache.userGroups("TEST")).isEmpty(); + } + + @Test + void userGroups_noUserGroups() { + Group group = GroupBuilder.get(cache, 1).group().add("PETER").build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userGroups("TEST")).isEmpty(); + } + + @Test + void userGroups_oneUserGroup() { + Group group = GroupBuilder.get(cache, 1).group().add("TEST").build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userGroups("TEST")).containsExactly(group); + } + + @Test + void userGroups_userGroup_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().add("TEST").build(); + Group groupB = GroupBuilder.get(cache, 2).group().add("PETER").build(); + Group groupC = GroupBuilder.get(cache, 3).group().add("TEST").build(); + Group groupD = GroupBuilder.get(cache, 4).group().add("PETER").build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.userGroups("PETER")).containsExactly(groupB, groupD); + } + + @Test + void userLectures_noGroups() { + assertThat(cache.userLectures("PETER")).isEmpty(); + } + + @Test + void userLectures_noLecture() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userLectures("PETER")).isEmpty(); + } + + @Test + void userLectures_oneLecture() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").lecture().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userLectures("PETER")).containsExactly(group); + } + + @Test + void userLectures_lecture_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").lecture().build(); + Group groupB = GroupBuilder.get(cache, 2).group().testadmin().limit(2).add("PETER").publik().build(); + Group groupC = GroupBuilder.get(cache, 3).group().testadmin().limit(2).add("PETER").privat().build(); + Group groupD = GroupBuilder.get(cache, 4).group().testadmin().lecture().build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.userLectures("PETER")).containsExactly(groupA); + } + + @Test + void userPublics_noGroups() { + assertThat(cache.userPublics("PETER")).isEmpty(); + } + + @Test + void userPublics_noPublic() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").publik().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userLectures("PETER")).isEmpty(); + } + + @Test + void userPublics_onePublic() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").lecture().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userLectures("PETER")).containsExactly(group); + } + + @Test + void userPublics_public_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").lecture().build(); + Group groupB = GroupBuilder.get(cache, 2).group().testadmin().limit(2).add("PETER").publik().build(); + Group groupC = GroupBuilder.get(cache, 3).group().testadmin().limit(2).add("PETER").privat().build(); + Group groupD = GroupBuilder.get(cache, 4).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.userPublics("PETER")).containsExactly(groupB); + } + + @Test + void userPrivates_noGroups() { + assertThat(cache.userPrivates("PETER")).isEmpty(); + } + + @Test + void userPrivates_noPrivate() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").publik().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userPrivates("PETER")).isEmpty(); + } + + @Test + void userPrivates_onePrivate() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").privat().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userPrivates("PETER")).containsExactly(group); + } + + @Test + void userPrivates_private_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").lecture().build(); + Group groupB = GroupBuilder.get(cache, 2).group().testadmin().limit(2).add("PETER").privat().build(); + Group groupC = GroupBuilder.get(cache, 3).group().testadmin().privat().build(); + Group groupD = GroupBuilder.get(cache, 4).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.userPrivates("PETER")).containsExactly(groupB); + } + + @Test + void publics_noGroups() { + assertThat(cache.publics()).isEmpty(); + } + + @Test + void publics_noPublic() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().privat().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.publics()).isEmpty(); + } + + @Test + void publics_onePublic() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.publics()).containsExactly(group); + } + + @Test + void publics_public_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().testadmin().lecture().build(); + Group groupB = GroupBuilder.get(cache, 2).group().testadmin().privat().build(); + Group groupC = GroupBuilder.get(cache, 3).group().testadmin().privat().build(); + Group groupD = GroupBuilder.get(cache, 4).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.publics()).containsExactly(groupD); + } + + @Test + void privates_noGroups() { + assertThat(cache.privates()).isEmpty(); + } + + @Test + void privates_noPrivate() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.privates()).isEmpty(); + } + + @Test + void privates_onePrivate() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().privat().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.privates()).containsExactly(group); + } + + @Test + void privates_private_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().testadmin().lecture().build(); + Group groupB = GroupBuilder.get(cache, 2).group().testadmin().privat().build(); + Group groupC = GroupBuilder.get(cache, 3).group().testadmin().privat().build(); + Group groupD = GroupBuilder.get(cache, 4).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.privates()).containsExactly(groupB, groupC); + } + + @Test + void lectures_noGroups() { + assertThat(cache.lectures()).isEmpty(); + } + + @Test + void lectures_noLecture() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().privat().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.lectures()).isEmpty(); + } + + @Test + void lectures_oneLecture() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.lectures()).containsExactly(group); + } + + @Test + void lectures_lecture_multiple() { + Group groupA = GroupBuilder.get(cache, 1).group().testadmin().lecture().build(); + Group groupB = GroupBuilder.get(cache, 2).group().testadmin().privat().build(); + Group groupC = GroupBuilder.get(cache, 3).group().testadmin().lecture().build(); + Group groupD = GroupBuilder.get(cache, 4).group().testadmin().publik().build(); + + assertThat(cache.groups()).hasSize(4); + assertThat(cache.lectures()).containsExactly(groupA, groupC); + } + + //Indirekt: void usersPut() {} + + @Test + void usersRemove() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().limit(2).add("PETER").kick("PETER").build(); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userLectures("PETER")).isEmpty(); + } + + //Indirekt: void groupsPut() {} + + @Test + void groupsRemove() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().destroy().build(); + + assertThat(cache.groups()).hasSize(0); + } + + + //Indirekt: void linksPut() {} + + @Test + void linksRemove() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().link(1).build(); + + assertThat(cache.group(String.valueOf(uuid(1)))).isEqualTo(group); + } + + //Indirekt: void typesPut() {} + + //Indirekt: void typesRemove() {} +} From 7a633c0396ac5438612bd93335ba0e84d94f153c Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 17:47:03 +0200 Subject: [PATCH 22/30] groupservice test --- .../mops/gruppen2/domain/event/Event.java | 2 +- .../gruppen2/domain/event/SetParentEvent.java | 3 +- .../gruppen2/domain/model/group/Group.java | 8 +- .../gruppen2/domain/model/group/User.java | 1 + .../domain/model/group/wrapper/Link.java | 12 +- .../gruppen2/domain/service/GroupService.java | 20 +- .../service/helper/ProjectionHelper.java | 1 - .../gruppen2/infrastructure/GroupCache.java | 7 + src/test/java/mops/gruppen2/TestHelper.java | 9 + .../domain/service/GroupServiceTest.java | 267 ++++++++++++++++++ 10 files changed, 317 insertions(+), 13 deletions(-) create mode 100644 src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index 15d6680..981e494 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -78,7 +78,7 @@ public abstract class Event { checkGroupIdMatch(group.getId()); applyEvent(group); - updateCache(cache, group); + updateCache(cache, group); // Update erst nachdem apply keine exception geworfen hat // Danach hat die Gruppe nur Nullfelder if (this instanceof DestroyGroupEvent) { diff --git a/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java b/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java index 2de2b60..be1dda9 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetParentEvent.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Value; import lombok.extern.log4j.Log4j2; +import mops.gruppen2.domain.exception.BadArgumentException; import mops.gruppen2.domain.exception.NoAccessException; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.wrapper.Parent; @@ -29,7 +30,7 @@ public class SetParentEvent extends Event { protected void updateCache(GroupCache cache, Group group) {} @Override - protected void applyEvent(Group group) throws NoAccessException { + protected void applyEvent(Group group) throws NoAccessException, BadArgumentException { group.setParent(exec, parent); log.trace("\t\t\t\t\tNeues Parent: {}", group.getParent()); 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 f06a0b0..860b2a0 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -278,14 +278,20 @@ public class Group { this.limit = limit; } - public void setParent(String exec, @Valid Parent parent) throws NoAccessException { + public void setParent(String exec, @Valid Parent parent) throws NoAccessException, BadArgumentException { ValidationHelper.throwIfNoAdmin(this, exec); + if (parent.getValue().equals(groupid)) { + throw new BadArgumentException("Die Gruppe kann nicht zu sich selbst gehören!"); + } this.parent = parent; } public void setLink(String exec, @Valid Link link) throws NoAccessException { ValidationHelper.throwIfNoAdmin(this, exec); + if (link.getValue().equals(groupid.toString())) { + throw new BadArgumentException("Link kann nicht der GruppenID entsprechen."); + } this.link = link; } 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 5fa6802..7f781ca 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/User.java +++ b/src/main/java/mops/gruppen2/domain/model/group/User.java @@ -12,6 +12,7 @@ import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; @Log4j2 @Value +@EqualsAndHashCode(onlyExplicitlyIncluded = true) @AllArgsConstructor public class User { diff --git a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Link.java b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Link.java index a79db9a..963dd94 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/wrapper/Link.java +++ b/src/main/java/mops/gruppen2/domain/model/group/wrapper/Link.java @@ -4,18 +4,24 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Value; import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; import java.util.UUID; @Value public class Link { @NotNull - @Size(min = 36, max = 36) @JsonProperty("value") - String value; + UUID value; + + public Link(String value) { + this.value = UUID.fromString(value); + } public static Link RANDOM() { return new Link(UUID.randomUUID().toString()); } + + public String getValue() { + return value.toString(); + } } diff --git a/src/main/java/mops/gruppen2/domain/service/GroupService.java b/src/main/java/mops/gruppen2/domain/service/GroupService.java index 1522394..9569a67 100644 --- a/src/main/java/mops/gruppen2/domain/service/GroupService.java +++ b/src/main/java/mops/gruppen2/domain/service/GroupService.java @@ -24,12 +24,15 @@ import mops.gruppen2.domain.model.group.wrapper.Limit; import mops.gruppen2.domain.model.group.wrapper.Link; import mops.gruppen2.domain.model.group.wrapper.Parent; import mops.gruppen2.domain.model.group.wrapper.Title; +import mops.gruppen2.domain.service.helper.ValidationHelper; import mops.gruppen2.infrastructure.GroupCache; import org.springframework.stereotype.Service; +import javax.validation.Valid; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; /** * Behandelt Aufgaben, welche sich auf eine Gruppe beziehen. @@ -94,9 +97,11 @@ public class GroupService { * @param exec Ausführender User */ public void addUsersToGroup(Group group, String exec, List newUsers) { - setLimit(group, exec, getAdjustedUserLimit(newUsers, group)); + List users = newUsers.stream().distinct().collect(Collectors.toUnmodifiableList()); - newUsers.forEach(newUser -> addUserSilent(group, exec, newUser.getId(), newUser)); + setLimit(group, exec, getAdjustedUserLimit(users, group)); + + users.forEach(newUser -> addUserSilent(group, exec, newUser.getId(), newUser)); } /** @@ -123,6 +128,8 @@ public class GroupService { * @throws EventException Falls der User nicht gefunden wird */ public void toggleMemberRole(Group group, String exec, String target) { + ValidationHelper.throwIfNoMember(group, target); + updateRole(group, exec, target, group.getRole(target).toggle()); } @@ -192,7 +199,7 @@ public class GroupService { * Prüft, ob der Nutzer Admin ist und ob der Titel valide ist. * Bei keiner Änderung wird nichts erzeugt. */ - public void setTitle(Group group, String exec, Title title) { + public void setTitle(Group group, String exec, @Valid Title title) { if (group.getTitle().equals(title.getValue())) { return; } @@ -205,7 +212,7 @@ public class GroupService { * Prüft, ob der Nutzer Admin ist und ob die Beschreibung valide ist. * Bei keiner Änderung wird nichts erzeugt. */ - public void setDescription(Group group, String exec, Description description) { + public void setDescription(Group group, String exec, @Valid Description description) { if (group.getDescription().equals(description.getValue())) { return; } @@ -231,7 +238,7 @@ public class GroupService { * Prüft, ob der Nutzer Admin ist und ob das Limit valide ist. * Bei keiner Änderung wird nichts erzeugt. */ - public void setLimit(Group group, String exec, Limit userLimit) { + public void setLimit(Group group, String exec, @Valid Limit userLimit) { if (userLimit.getValue() == group.getLimit()) { return; } @@ -247,7 +254,8 @@ public class GroupService { applyAndSave(group, new SetParentEvent(group.getId(), exec, parent)); } - public void setLink(Group group, String exec, Link link) { + //TODO: UI Link regenerieren button + public void setLink(Group group, String exec, @Valid Link link) { if (group.getLink().equals(link.getValue())) { return; } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java index 022166d..2e686e9 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/ProjectionHelper.java @@ -22,7 +22,6 @@ import java.util.UUID; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class ProjectionHelper { - public static List project(List events) { Map groups = new HashMap<>(); diff --git a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java index 736c975..651ab0c 100644 --- a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java +++ b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java @@ -21,6 +21,13 @@ import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; +/** + * Cached alle existierenden Gruppen und einige Beziehungen. + * Gruppen können nach Typ angefragt werden, nach ID, nach Link oder nach User. + * Der Cache wird von den Events aktualisiert. + * Beim Aufruf der init() Methode werden alle bisherigen Events projiziert und die Gruppen gespeichert. + * Die Komplette Anwendung verwendet eine Instanz des Caches. + */ @Log4j2 @RequiredArgsConstructor @Component diff --git a/src/test/java/mops/gruppen2/TestHelper.java b/src/test/java/mops/gruppen2/TestHelper.java index 7895df3..ad9968c 100644 --- a/src/test/java/mops/gruppen2/TestHelper.java +++ b/src/test/java/mops/gruppen2/TestHelper.java @@ -1,5 +1,8 @@ package mops.gruppen2; +import mops.gruppen2.domain.event.Event; + +import java.util.List; import java.util.UUID; public final class TestHelper { @@ -12,4 +15,10 @@ public final class TestHelper { return UUID.fromString(string); } + + public static void initEvents(List events) { + for (int i = 1; i <= events.size(); i++) { + events.get(i - 1).init(i); + } + } } diff --git a/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java b/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java new file mode 100644 index 0000000..f0f8c7e --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java @@ -0,0 +1,267 @@ +package mops.gruppen2.domain.service; + +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.exception.BadArgumentException; +import mops.gruppen2.domain.exception.GroupFullException; +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.model.group.Group; +import mops.gruppen2.domain.model.group.Type; +import mops.gruppen2.domain.model.group.User; +import mops.gruppen2.domain.model.group.wrapper.Description; +import mops.gruppen2.domain.model.group.wrapper.Limit; +import mops.gruppen2.domain.model.group.wrapper.Link; +import mops.gruppen2.domain.model.group.wrapper.Parent; +import mops.gruppen2.domain.model.group.wrapper.Title; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + +class GroupServiceTest { + + private GroupService groupService; + + @BeforeEach + void setUp() { + groupService = new GroupService(mock(GroupCache.class), mock(EventStoreService.class)); + } + + @Test + void createGroup() { + Group group = groupService.createGroup("TEST"); + + assertThat(group.getId()).isNotNull(); + assertThat(group.creator()).isEqualTo("TEST"); + } + + @Test + void initGroupMembers() { + Group group = groupService.createGroup("TEST"); + groupService.initGroupMembers(group, "TEST", "TEST", new User("TEST"), new Limit(1)); + + assertThat(group.getMembers()).containsExactly(new User("TEST")); + assertThat(group.getLimit()).isEqualTo(1); + assertThat(group.isAdmin("TEST")).isTrue(); + } + + @Test + void initGroupMeta() { + Group group = groupService.createGroup("TEST"); + groupService.initGroupMembers(group, "TEST", "TEST", new User("TEST"), new Limit(1)); + groupService.initGroupMeta(group, "TEST", Type.PUBLIC, Parent.EMPTY()); + + assertThat(group.isPublic()).isTrue(); + assertThat(group.hasParent()).isFalse(); + } + + @Test + void initGroupText() { + Group group = groupService.createGroup("TEST"); + groupService.initGroupMembers(group, "TEST", "TEST", new User("TEST"), new Limit(1)); + groupService.initGroupMeta(group, "TEST", Type.PUBLIC, Parent.EMPTY()); + groupService.initGroupText(group, "TEST", new Title("TITLE"), new Description("DESCR")); + + assertThat(group.getTitle()).isEqualTo("TITLE"); + assertThat(group.getDescription()).isEqualTo("DESCR"); + } + + @Test + void addUsersToGroup() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.addUsersToGroup(group, "TEST", Arrays.asList( + new User("A"), + new User("B"), + new User("C"), + new User("C"))); + + assertThat(group.getLimit()).isEqualTo(4); + assertThat(group.size()).isEqualTo(4); + assertThat(group.getRegulars()).hasSize(3); + assertThat(group.getAdmins()).hasSize(1); + } + + @Test + void toggleMemberRole_lastAdmin_lastUser() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.toggleMemberRole(group, "TEST", "TEST"); + } + + @Test + void toggleMemberRole_lastAdmin() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(2).add("PETER").build(); + + assertThatThrownBy(() -> groupService.toggleMemberRole(group, "TEST", "TEST")) + .isInstanceOf(LastAdminException.class); + } + + @Test + void toggleMemberRole_noMember() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + assertThatThrownBy(() -> groupService.toggleMemberRole(group, "TEST", "PETER")) + .isInstanceOf(UserNotFoundException.class); + } + + @Test + void addMember_newMember() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(2).build(); + + groupService.addMember(group, "Test", "PETER", new User("PETER")); + + assertThat(group.size()).isEqualTo(2); + assertThat(group.getAdmins()).hasSize(1); + assertThat(group.getRegulars()).hasSize(1); + } + + @Test + void addMember_newMember_groupFull() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + assertThatThrownBy(() -> groupService.addMember(group, "Test", "PETER", new User("PETER"))) + .isInstanceOf(GroupFullException.class); + } + + @Test + void addMember_newMember_userExists() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(3).add("PETER").build(); + + assertThatThrownBy(() -> groupService.addMember(group, "Test", "PETER", new User("PETER"))) + .isInstanceOf(UserAlreadyExistsException.class); + } + + @Test + void kickMember_noMember() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + assertThatThrownBy(() -> groupService.kickMember(group, "TEST", "PETER")) + .isInstanceOf(UserNotFoundException.class); + } + + @Test + void kickMember_lastMember() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.kickMember(group, "TEST", "TEST"); + + assertThat(group.exists()).isFalse(); + } + + @Test + void kickMember_lastAdmin() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(2).add("PETER").build(); + + assertThatThrownBy(() -> groupService.kickMember(group, "TEST", "TEST")) + .isInstanceOf(LastAdminException.class); + } + + @Test + void deleteGroup_noGroup() { + groupService.deleteGroup(Group.EMPTY(), "TEST"); + } + + @Test + void deleteGroup() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(2).add("PETER").build(); + + groupService.deleteGroup(group, "TEST"); + + assertThat(group.exists()).isFalse(); + } + + @Test + void deleteGroup_noAdmin() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(2).add("PETER").build(); + + assertThatThrownBy(() -> groupService.deleteGroup(group, "PETER")) + .isInstanceOf(NoAccessException.class); + assertThat(group.exists()).isTrue(); + } + + @Test + void setTitle() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.setTitle(group, "TEST", new Title("TITLE")); + + assertThat(group.getTitle()).isEqualTo("TITLE"); + } + + @Test + void setDescription() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.setDescription(group, "TEST", new Description("DESCR")); + + assertThat(group.getDescription()).isEqualTo("DESCR"); + } + + @Test + void setLimit_tooLow() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(2).add("PETER").build(); + + assertThatThrownBy(() -> groupService.setLimit(group, "TEST", new Limit(1))) + .isInstanceOf(BadArgumentException.class); + + assertThat(group.getLimit()).isEqualTo(2); + } + + @Test + void setLimit() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.setLimit(group, "TEST", new Limit(8)); + + assertThat(group.getLimit()).isEqualTo(8); + } + + @Test + void setParent_sameGroup() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + assertThatThrownBy(() -> groupService.setParent(group, "TEST", new Parent(uuid(1).toString()))) + .isInstanceOf(BadArgumentException.class); + + assertThat(group.getParent()).isEqualTo(uuid(0)); + assertThat(group.hasParent()).isFalse(); + } + + @Test + void setParent() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.setParent(group, "TEST", new Parent(uuid(2).toString())); + + assertThat(group.getParent()).isEqualTo(uuid(2)); + assertThat(group.hasParent()).isTrue(); + } + + @Test + void setLink_sameAsGroupId() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + assertThatThrownBy(() -> groupService.setLink(group, "TEST", new Link(uuid(1).toString()))) + .isInstanceOf(BadArgumentException.class); + + assertThat(group.getLink()).isNotEqualTo(uuid(1).toString()); + } + + @Test + void setLink() { + Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().build(); + + groupService.setLink(group, "TEST", new Link(uuid(2).toString())); + + assertThat(group.getLink()).isEqualTo(uuid(2).toString()); + } +} From fdbd2016bb60a2ee0d23f600bea1f1ff44489fca Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 17:47:13 +0200 Subject: [PATCH 23/30] projectionhelper testt --- .../service/helper/ProjectionHelperTest.java | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java diff --git a/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java b/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java new file mode 100644 index 0000000..1d2d5b4 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java @@ -0,0 +1,210 @@ +package mops.gruppen2.domain.service.helper; + +import mops.gruppen2.domain.event.AddMemberEvent; +import mops.gruppen2.domain.event.CreateGroupEvent; +import mops.gruppen2.domain.event.Event; +import mops.gruppen2.domain.event.SetLimitEvent; +import mops.gruppen2.domain.event.UpdateRoleEvent; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.Role; +import mops.gruppen2.domain.model.group.User; +import mops.gruppen2.domain.model.group.wrapper.Limit; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static mops.gruppen2.TestHelper.initEvents; +import static mops.gruppen2.TestHelper.uuid; +import static mops.gruppen2.domain.service.helper.ProjectionHelper.project; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class ProjectionHelperTest { + + @BeforeEach + void setUp() { + } + + @Test + void project_nocache_emptyList() { + assertThat(project(Collections.emptyList())).isEmpty(); + } + + @Test + void project_nocache_oneCreate() { + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now())); + + initEvents(events); + + assertThat(project(events)).hasSize(1); + } + + @Test + void project_nocache_multipleCreate() { + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(2), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(5), "TEST", LocalDateTime.now())); + + initEvents(events); + + assertThat(project(events)).hasSize(5); + } + + @Test + void project_nocache_oneDetailed() { + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(1), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(1), "TEST", new Limit(5))); + + initEvents(events); + + List groups = project(events); + + assertThat(groups).hasSize(1); + assertThat(groups.get(0).exists()).isTrue(); + assertThat(groups.get(0).getAdmins()).hasSize(1); + assertThat(groups.get(0).getLimit()).isEqualTo(5); + } + + @Test + void project_nocache_multipleDetailed() { + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(1), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(1), "TEST", new Limit(5)), + + new CreateGroupEvent(uuid(2), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(2), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(2), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(2), "TEST", new Limit(15)), + + new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(3), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(3), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(3), "TEST", new Limit(25)), + + new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(4), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(4), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(4), "TEST", new Limit(35))); + + initEvents(events); + + List groups = project(events); + + assertThat(groups).hasSize(4); + assertThat(groups.get(0).exists()).isTrue(); + assertThat(groups.get(0).getAdmins()).hasSize(1); + assertThat(groups.get(0).getLimit()).isEqualTo(5); + + assertThat(groups.get(1).exists()).isTrue(); + assertThat(groups.get(1).getAdmins()).hasSize(1); + assertThat(groups.get(1).getLimit()).isEqualTo(15); + + assertThat(groups.get(2).exists()).isTrue(); + assertThat(groups.get(2).getAdmins()).hasSize(1); + assertThat(groups.get(2).getLimit()).isEqualTo(25); + + assertThat(groups.get(3).exists()).isTrue(); + assertThat(groups.get(3).getAdmins()).hasSize(1); + assertThat(groups.get(3).getLimit()).isEqualTo(35); + } + + @Test + void project_cache_noGroups() { + Map groups = new HashMap<>(); + + project(groups, Collections.emptyList(), mock(GroupCache.class)); + + assertThat(groups).isEmpty(); + } + + @Test + void project_cache_oneCreate() { + Map groups = new HashMap<>(); + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now())); + + initEvents(events); + project(groups, events, mock(GroupCache.class)); + + assertThat(groups).hasSize(1); + assertThat(groups.keySet()).containsExactly(uuid(1)); + } + + @Test + void project_cache_multipleCreate() { + Map groups = new HashMap<>(); + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(2), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), + new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now())); + + initEvents(events); + project(groups, events, mock(GroupCache.class)); + + assertThat(groups).hasSize(4); + assertThat(groups.keySet()).containsExactly(uuid(1), uuid(2), uuid(3), uuid(4)); + } + + @Test + void project_cache_multipleDetailed() { + Map groups = new HashMap<>(); + List events = Arrays.asList( + new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(1), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(1), "TEST", new Limit(5)), + + new CreateGroupEvent(uuid(2), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(2), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(2), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(2), "TEST", new Limit(15)), + + new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(3), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(3), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(3), "TEST", new Limit(25)), + + new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now()), + new AddMemberEvent(uuid(4), "TEST", "TEST", new User("TEST")), + new UpdateRoleEvent(uuid(4), "TEST", "TEST", Role.ADMIN), + new SetLimitEvent(uuid(4), "TEST", new Limit(35))); + + initEvents(events); + project(groups, events, mock(GroupCache.class)); + + assertThat(groups).hasSize(4); + assertThat(groups.get(uuid(1)).exists()).isTrue(); + assertThat(groups.get(uuid(1)).getAdmins()).hasSize(1); + assertThat(groups.get(uuid(1)).getLimit()).isEqualTo(5); + + assertThat(groups.get(uuid(2)).exists()).isTrue(); + assertThat(groups.get(uuid(2)).getAdmins()).hasSize(1); + assertThat(groups.get(uuid(2)).getLimit()).isEqualTo(15); + + assertThat(groups.get(uuid(3)).exists()).isTrue(); + assertThat(groups.get(uuid(3)).getAdmins()).hasSize(1); + assertThat(groups.get(uuid(3)).getLimit()).isEqualTo(25); + + assertThat(groups.get(uuid(4)).exists()).isTrue(); + assertThat(groups.get(uuid(4)).getAdmins()).hasSize(1); + assertThat(groups.get(uuid(4)).getLimit()).isEqualTo(35); + } +} From d2a699dd711b762b03524e136e129017f89566ff Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 18:10:01 +0200 Subject: [PATCH 24/30] searchservice test --- .../domain/service/SearchServiceTest.java | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java diff --git a/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java b/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java new file mode 100644 index 0000000..cd7cd88 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java @@ -0,0 +1,116 @@ +package mops.gruppen2.domain.service; + +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.Type; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class SearchServiceTest { + + GroupCache groupCache; + SearchService searchService; + + @BeforeEach + void setUp() { + groupCache = new GroupCache(mock(EventStoreService.class)); + searchService = new SearchService(groupCache); + } + + @Test + void searchString_noResult() { + assertThat(searchService.searchString("", "TEST")).isEmpty(); + } + + @Test + void searchString_noResult_onePrivate_emptyString() { + GroupBuilder.get(groupCache, 1).group(); + + assertThat(searchService.searchString("", "TEST")).isEmpty(); + } + + @Test + void searchString_noResult_onePublic_emptyString_principalMember() { + GroupBuilder.get(groupCache, 1).group().testadmin().publik(); + + assertThat(searchService.searchString("", "TEST")).isEmpty(); + } + + @Test + void searchString_oneResult_onePublic_emptyString_principalNoMember() { + GroupBuilder.get(groupCache, 1).group().testadmin().publik(); + + assertThat(searchService.searchString("", "PETER")).hasSize(1); + } + + @Test + void searchString_oneResult_multiple_emptyString() { + Group groupA = GroupBuilder.get(groupCache, 1).group().testadmin().lecture().build(); + GroupBuilder.get(groupCache, 2).group().testadmin().publik().limit(2).add("PETER"); + GroupBuilder.get(groupCache, 3).group().testadmin().publik().limit(2).add("PETER"); + GroupBuilder.get(groupCache, 4).group().testadmin().privat(); + + assertThat(searchService.searchString("", "PETER")).containsExactly(groupA); + assertThat(searchService.searchString("", "TEST")).isEmpty(); + } + + @Test + void searchString_noPrivates() { + Group groupA = GroupBuilder.get(groupCache, 1).group().testadmin().lecture().build(); + Group groupB = GroupBuilder.get(groupCache, 2).group().testadmin().publik().limit(2).add("PETER").build(); + Group groupC = GroupBuilder.get(groupCache, 3).group().testadmin().publik().limit(2).add("PETER").build(); + GroupBuilder.get(groupCache, 4).group().testadmin().privat(); + GroupBuilder.get(groupCache, 5).group().testadmin().privat(); + GroupBuilder.get(groupCache, 6).group().testadmin().privat(); + + assertThat(searchService.searchString("", "PETER")).containsExactly(groupA); + assertThat(searchService.searchString("", "PRRR")).containsOnly(groupA, groupB, groupC); + assertThat(searchService.searchString("", "TEST")).isEmpty(); + } + + @Test + void searchString_matchString_title() { + Group groupA = GroupBuilder.get(groupCache, 1).group().testadmin().lecture().title("A").build(); + Group groupB = GroupBuilder.get(groupCache, 2).group().testadmin().lecture().title("B").build(); + Group groupC = GroupBuilder.get(groupCache, 3).group().testadmin().lecture().title("C").build(); + Group groupD = GroupBuilder.get(groupCache, 4).group().testadmin().lecture().title("CAESAR").build(); + + assertThat(searchService.searchString("C", "PETER")).containsExactly(groupC, groupD); + assertThat(searchService.searchString("C", "TEST")).isEmpty(); + } + + @Test + void searchString_matchString_desc() { + Group groupA = GroupBuilder.get(groupCache, 1).group().testadmin().lecture().desc("A").build(); + Group groupB = GroupBuilder.get(groupCache, 2).group().testadmin().lecture().desc("B").build(); + Group groupC = GroupBuilder.get(groupCache, 3).group().testadmin().lecture().desc("C").build(); + Group groupD = GroupBuilder.get(groupCache, 4).group().testadmin().lecture().desc("CAESAR").build(); + + assertThat(searchService.searchString("C", "PETER")).containsExactly(groupC, groupD); + assertThat(searchService.searchString("C", "TEST")).isEmpty(); + } + + @Test + void searchType_noGroup() { + assertThat(searchService.searchType(Type.LECTURE, "PETER")).isEmpty(); + assertThat(searchService.searchType(Type.PUBLIC, "PETER")).isEmpty(); + assertThat(searchService.searchType(Type.PRIVATE, "PETER")).isEmpty(); + } + + @Test + void searchType_noPrivates() { + GroupBuilder.get(groupCache, 1).group().testadmin().lecture(); + GroupBuilder.get(groupCache, 2).group().testadmin().publik(); + GroupBuilder.get(groupCache, 3).group().testadmin().privat(); + GroupBuilder.get(groupCache, 4).group().testadmin().privat(); + GroupBuilder.get(groupCache, 5).group().testadmin().lecture(); + + assertThat(searchService.searchType(Type.LECTURE, "PETER")).hasSize(2); + assertThat(searchService.searchType(Type.PUBLIC, "PETER")).hasSize(1); + assertThat(searchService.searchType(Type.PRIVATE, "PETER")).isEmpty(); + } +} From 4118d3f6b1e577742b39d4fb88121e2353a9e808 Mon Sep 17 00:00:00 2001 From: Christoph Date: Sat, 18 Apr 2020 18:19:23 +0200 Subject: [PATCH 25/30] init empty testclasses --- .../domain/event/AddMemberEventTest.java | 17 +++++ .../domain/event/CreateGroupEventTest.java | 5 ++ .../domain/event/DestroyGroupEventTest.java | 5 ++ .../domain/event/KickMemberEventTest.java | 5 ++ .../domain/event/SetDescriptionEventTest.java | 5 ++ .../domain/event/SetInviteLinkEventTest.java | 5 ++ .../domain/event/SetLimitEventTest.java | 5 ++ .../domain/event/SetParentEventTest.java | 5 ++ .../domain/event/SetTitleEventTest.java | 5 ++ .../domain/event/SetTypeEventTest.java | 5 ++ .../domain/event/UpdateRoleEventTest.java | 5 ++ .../domain/service/SearchServiceTest.java | 4 +- .../controller/APIControllerTest.java | 23 +++++++ .../GroupCreationControllerTest.java | 19 ++++++ .../GroupDetailsControllerTest.java | 67 +++++++++++++++++++ .../SearchAndInviteControllerTest.java | 31 +++++++++ 16 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java create mode 100644 src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java create mode 100644 src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java create mode 100644 src/test/java/mops/gruppen2/infrastructure/controller/GroupCreationControllerTest.java create mode 100644 src/test/java/mops/gruppen2/infrastructure/controller/GroupDetailsControllerTest.java create mode 100644 src/test/java/mops/gruppen2/infrastructure/controller/SearchAndInviteControllerTest.java diff --git a/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java b/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java new file mode 100644 index 0000000..a46f831 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java @@ -0,0 +1,17 @@ +package mops.gruppen2.domain.event; + +import mops.gruppen2.TestHelper; +import mops.gruppen2.domain.exception.IdMismatchException; +import mops.gruppen2.domain.model.group.User; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class AddMemberEventTest { + + @Test + void userMismatch() { + assertThatThrownBy(() -> new AddMemberEvent(TestHelper.uuid(1), "TEST", "TEST", new User("PETER"))) + .isInstanceOf(IdMismatchException.class); + } +} diff --git a/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java b/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java new file mode 100644 index 0000000..df67b7b --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class CreateGroupEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java b/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java new file mode 100644 index 0000000..ef97a59 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class DestroyGroupEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java b/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java new file mode 100644 index 0000000..3736773 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class KickMemberEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java new file mode 100644 index 0000000..34e56c6 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class SetDescriptionEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java new file mode 100644 index 0000000..a1a2480 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class SetInviteLinkEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java new file mode 100644 index 0000000..73e13d0 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class SetLimitEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java new file mode 100644 index 0000000..cd84c5a --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class SetParentEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java new file mode 100644 index 0000000..a4a505b --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class SetTitleEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java new file mode 100644 index 0000000..07ab586 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class SetTypeEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java b/src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java new file mode 100644 index 0000000..80e2b7e --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java @@ -0,0 +1,5 @@ +package mops.gruppen2.domain.event; + +class UpdateRoleEventTest { + +} diff --git a/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java b/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java index cd7cd88..848bfe2 100644 --- a/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java +++ b/src/test/java/mops/gruppen2/domain/service/SearchServiceTest.java @@ -12,8 +12,8 @@ import static org.mockito.Mockito.mock; class SearchServiceTest { - GroupCache groupCache; - SearchService searchService; + private GroupCache groupCache; + private SearchService searchService; @BeforeEach void setUp() { diff --git a/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java b/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java new file mode 100644 index 0000000..312376a --- /dev/null +++ b/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java @@ -0,0 +1,23 @@ +package mops.gruppen2.infrastructure.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class APIControllerTest { + + @BeforeEach + void setUp() { + } + + @Test + void getApiUpdate() { + } + + @Test + void getApiUserGroups() { + } + + @Test + void getApiGroup() { + } +} diff --git a/src/test/java/mops/gruppen2/infrastructure/controller/GroupCreationControllerTest.java b/src/test/java/mops/gruppen2/infrastructure/controller/GroupCreationControllerTest.java new file mode 100644 index 0000000..4d01119 --- /dev/null +++ b/src/test/java/mops/gruppen2/infrastructure/controller/GroupCreationControllerTest.java @@ -0,0 +1,19 @@ +package mops.gruppen2.infrastructure.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GroupCreationControllerTest { + + @BeforeEach + void setUp() { + } + + @Test + void getCreate() { + } + + @Test + void postCreateOrga() { + } +} diff --git a/src/test/java/mops/gruppen2/infrastructure/controller/GroupDetailsControllerTest.java b/src/test/java/mops/gruppen2/infrastructure/controller/GroupDetailsControllerTest.java new file mode 100644 index 0000000..42c7a9c --- /dev/null +++ b/src/test/java/mops/gruppen2/infrastructure/controller/GroupDetailsControllerTest.java @@ -0,0 +1,67 @@ +package mops.gruppen2.infrastructure.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class GroupDetailsControllerTest { + + @BeforeEach + void setUp() { + } + + @Test + void getDetailsPage() { + } + + @Test + void postDetailsJoin() { + } + + @Test + void postDetailsLeave() { + } + + @Test + void getDetailsHistory() { + } + + @Test + void getDetailsExportHistoryPlain() { + } + + @Test + void getDetailsExportHistorySql() { + } + + @Test + void getDetailsExportMembers() { + } + + @Test + void getDetailsEdit() { + } + + @Test + void postDetailsEditMeta() { + } + + @Test + void postDetailsEditUserLimit() { + } + + @Test + void postDetailsEditCsv() { + } + + @Test + void postDetailsEditRole() { + } + + @Test + void postDetailsEditDelete() { + } + + @Test + void postDetailsEditDestroy() { + } +} diff --git a/src/test/java/mops/gruppen2/infrastructure/controller/SearchAndInviteControllerTest.java b/src/test/java/mops/gruppen2/infrastructure/controller/SearchAndInviteControllerTest.java new file mode 100644 index 0000000..edc1ad0 --- /dev/null +++ b/src/test/java/mops/gruppen2/infrastructure/controller/SearchAndInviteControllerTest.java @@ -0,0 +1,31 @@ +package mops.gruppen2.infrastructure.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SearchAndInviteControllerTest { + + @BeforeEach + void setUp() { + } + + @Test + void getSearch() { + } + + @Test + void postSearchString() { + } + + @Test + void getSearchAll() { + } + + @Test + void getSearchType() { + } + + @Test + void getJoin() { + } +} From d1d6a2c95157f380b7dd37a2c5fd33518eeb0f6c Mon Sep 17 00:00:00 2001 From: Christoph Date: Sun, 19 Apr 2020 15:09:18 +0200 Subject: [PATCH 26/30] event test + addmemberevent test --- .../gruppen2/domain/event/AddMemberEvent.java | 4 +- ...xception.java => UserExistsException.java} | 4 +- .../gruppen2/domain/model/group/Group.java | 8 +- .../domain/model/group/GroupMeta.java | 3 + .../service/helper/ValidationHelper.java | 6 +- .../domain/event/AddMemberEventTest.java | 59 ++++++++++++++ .../mops/gruppen2/domain/event/EventTest.java | 76 +++++++++++++++++++ .../domain/service/GroupServiceTest.java | 4 +- .../service/helper/ProjectionHelperTest.java | 57 +++++++++++--- .../infrastructure/GroupCacheTest.java | 4 +- 10 files changed, 200 insertions(+), 25 deletions(-) rename src/main/java/mops/gruppen2/domain/exception/{UserAlreadyExistsException.java => UserExistsException.java} (68%) create mode 100644 src/test/java/mops/gruppen2/domain/event/EventTest.java diff --git a/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java b/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java index d638ca2..0394a0c 100644 --- a/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java @@ -6,7 +6,7 @@ import lombok.Value; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.IdMismatchException; -import mops.gruppen2.domain.exception.UserAlreadyExistsException; +import mops.gruppen2.domain.exception.UserExistsException; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.User; import mops.gruppen2.infrastructure.GroupCache; @@ -39,7 +39,7 @@ public class AddMemberEvent extends Event { } @Override - protected void applyEvent(Group group) throws UserAlreadyExistsException, GroupFullException { + protected void applyEvent(Group group) throws UserExistsException, GroupFullException { group.addMember(target, user); log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers()); diff --git a/src/main/java/mops/gruppen2/domain/exception/UserAlreadyExistsException.java b/src/main/java/mops/gruppen2/domain/exception/UserExistsException.java similarity index 68% rename from src/main/java/mops/gruppen2/domain/exception/UserAlreadyExistsException.java rename to src/main/java/mops/gruppen2/domain/exception/UserExistsException.java index d0288cd..43f5d59 100644 --- a/src/main/java/mops/gruppen2/domain/exception/UserAlreadyExistsException.java +++ b/src/main/java/mops/gruppen2/domain/exception/UserExistsException.java @@ -2,11 +2,11 @@ package mops.gruppen2.domain.exception; import org.springframework.http.HttpStatus; -public class UserAlreadyExistsException extends EventException { +public class UserExistsException extends EventException { private static final long serialVersionUID = -8150634358760194625L; - public UserAlreadyExistsException(String info) { + public UserExistsException(String info) { super(HttpStatus.INTERNAL_SERVER_ERROR, "User existiert bereits.", info); } } 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 860b2a0..51dfe79 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -10,7 +10,7 @@ import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.IdMismatchException; import mops.gruppen2.domain.exception.LastAdminException; import mops.gruppen2.domain.exception.NoAccessException; -import mops.gruppen2.domain.exception.UserAlreadyExistsException; +import mops.gruppen2.domain.exception.UserExistsException; import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.model.group.wrapper.Body; import mops.gruppen2.domain.model.group.wrapper.Description; @@ -101,7 +101,7 @@ public class Group { return memberships.get(userid).getRole(); } - public void addMember(String target, User user) throws UserAlreadyExistsException, GroupFullException { + public void addMember(String target, User user) throws UserExistsException, GroupFullException { ValidationHelper.throwIfMember(this, target); ValidationHelper.throwIfGroupFull(this); @@ -348,4 +348,8 @@ public class Group { public static Group EMPTY() { return new Group(); } + + public long getVersion() { + return meta.getVersion(); + } } diff --git a/src/main/java/mops/gruppen2/domain/model/group/GroupMeta.java b/src/main/java/mops/gruppen2/domain/model/group/GroupMeta.java index 60372dd..44a7a07 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/GroupMeta.java +++ b/src/main/java/mops/gruppen2/domain/model/group/GroupMeta.java @@ -21,6 +21,9 @@ class GroupMeta { if (this.version >= version) { throw new IdMismatchException("Die Gruppe ist bereits auf einem neueren Stand."); } + if (this.version + 1 != version) { + throw new IdMismatchException("Es fehlen vorherige Events."); + } return new GroupMeta(version, creator, creationDate); } diff --git a/src/main/java/mops/gruppen2/domain/service/helper/ValidationHelper.java b/src/main/java/mops/gruppen2/domain/service/helper/ValidationHelper.java index 4e3ddd5..c82fff4 100644 --- a/src/main/java/mops/gruppen2/domain/service/helper/ValidationHelper.java +++ b/src/main/java/mops/gruppen2/domain/service/helper/ValidationHelper.java @@ -7,7 +7,7 @@ import mops.gruppen2.domain.exception.BadArgumentException; import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.LastAdminException; import mops.gruppen2.domain.exception.NoAccessException; -import mops.gruppen2.domain.exception.UserAlreadyExistsException; +import mops.gruppen2.domain.exception.UserExistsException; import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.Type; @@ -39,10 +39,10 @@ public final class ValidationHelper { // ######################################## THROW ############################################ - public static void throwIfMember(Group group, String userid) throws UserAlreadyExistsException { + public static void throwIfMember(Group group, String userid) throws UserExistsException { if (group.isMember(userid)) { log.error("Benutzer {} ist schon in Gruppe {}", userid, group); - throw new UserAlreadyExistsException(userid); + throw new UserExistsException(userid); } } diff --git a/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java b/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java index a46f831..f2c060e 100644 --- a/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java @@ -1,17 +1,76 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.GroupBuilder; import mops.gruppen2.TestHelper; +import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.IdMismatchException; +import mops.gruppen2.domain.exception.UserExistsException; +import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.User; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; class AddMemberEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + @Test void userMismatch() { assertThatThrownBy(() -> new AddMemberEvent(TestHelper.uuid(1), "TEST", "TEST", new User("PETER"))) .isInstanceOf(IdMismatchException.class); } + + @Test + void apply() { + Group group = GroupBuilder.get(cache, 1).group().build(); + Event add = new AddMemberEvent(TestHelper.uuid(1), "TEST", "TEST", new User("TEST")); + add.init(2); + + add.apply(group); + + assertThat(group.getMembers()).containsExactly(new User("TEST")); + } + + @Test + void apply_cache() { + Group group = GroupBuilder.get(cache, 1).group().build(); + Event add = new AddMemberEvent(TestHelper.uuid(1), "TEST", "TEST", new User("TEST")); + add.init(2); + + add.apply(group, cache); + + assertThat(group.getMembers()).containsExactly(new User("TEST")); + assertThat(cache.userGroups("TEST")).containsExactly(group); + } + + @Test + void apply_userExists() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).build(); + Event add = new AddMemberEvent(TestHelper.uuid(1), "TEST", "TEST", new User("TEST")); + add.init(3); + + assertThatThrownBy(() -> add.apply(group, cache)) + .isInstanceOf(UserExistsException.class); + } + + @Test + void apply_groupFull() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); + Event add = new AddMemberEvent(TestHelper.uuid(1), "TEST", "PETER", new User("PETER")); + add.init(2); + + assertThatThrownBy(() -> add.apply(group, cache)) + .isInstanceOf(GroupFullException.class); + } } diff --git a/src/test/java/mops/gruppen2/domain/event/EventTest.java b/src/test/java/mops/gruppen2/domain/event/EventTest.java new file mode 100644 index 0000000..6998d42 --- /dev/null +++ b/src/test/java/mops/gruppen2/domain/event/EventTest.java @@ -0,0 +1,76 @@ +package mops.gruppen2.domain.event; + +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.exception.BadArgumentException; +import mops.gruppen2.domain.exception.IdMismatchException; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.User; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class EventTest { + + @Test + void apply_smallVersion() { + Group group = GroupBuilder.get(Mockito.mock(GroupCache.class), 1).group().build(); + Event add = new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")); + add.init(1); + + assertThatThrownBy(() -> add.apply(group)) + .isInstanceOf(IdMismatchException.class); + } + + @Test + void apply_bigVersion() { + Group group = GroupBuilder.get(Mockito.mock(GroupCache.class), 1).group().build(); + Event add = new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")); + add.init(3); + + assertThatThrownBy(() -> add.apply(group)) + .isInstanceOf(IdMismatchException.class); + } + + @Test + void apply_notInitialized() { + Group group = GroupBuilder.get(Mockito.mock(GroupCache.class), 1).group().build(); + Event add = new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")); + + assertThatThrownBy(() -> add.apply(group)) + .isInstanceOf(BadArgumentException.class); + } + + @Test + void apply_wrongGroup() { + Group group = GroupBuilder.get(Mockito.mock(GroupCache.class), 1).group().build(); + Event add = new AddMemberEvent(uuid(2), "TEST", "TEST", new User("TEST")); + add.init(2); + + assertThatThrownBy(() -> add.apply(group)) + .isInstanceOf(IdMismatchException.class); + } + + @Test + void apply_updateVersion() { + Group group = GroupBuilder.get(Mockito.mock(GroupCache.class), 1).group().build(); + Event add = new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")); + add.init(2); + + assertThat(group.getVersion()).isEqualTo(1); + add.apply(group); + assertThat(group.getVersion()).isEqualTo(2); + } + + @Test + void init_alreadyInitialized() { + Event add = new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")); + add.init(2); + + assertThatThrownBy(() -> add.init(3)) + .isInstanceOf(BadArgumentException.class); + } +} diff --git a/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java b/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java index f0f8c7e..2effa8e 100644 --- a/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java +++ b/src/test/java/mops/gruppen2/domain/service/GroupServiceTest.java @@ -5,7 +5,7 @@ import mops.gruppen2.domain.exception.BadArgumentException; import mops.gruppen2.domain.exception.GroupFullException; import mops.gruppen2.domain.exception.LastAdminException; import mops.gruppen2.domain.exception.NoAccessException; -import mops.gruppen2.domain.exception.UserAlreadyExistsException; +import mops.gruppen2.domain.exception.UserExistsException; import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.model.group.Group; import mops.gruppen2.domain.model.group.Type; @@ -137,7 +137,7 @@ class GroupServiceTest { Group group = GroupBuilder.get(mock(GroupCache.class), 1).group().testadmin().limit(3).add("PETER").build(); assertThatThrownBy(() -> groupService.addMember(group, "Test", "PETER", new User("PETER"))) - .isInstanceOf(UserAlreadyExistsException.class); + .isInstanceOf(UserExistsException.class); } @Test diff --git a/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java b/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java index 1d2d5b4..ced85a4 100644 --- a/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java +++ b/src/test/java/mops/gruppen2/domain/service/helper/ProjectionHelperTest.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -57,7 +58,11 @@ class ProjectionHelperTest { new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now()), new CreateGroupEvent(uuid(5), "TEST", LocalDateTime.now())); - initEvents(events); + events.get(0).init(1); + events.get(1).init(1); + events.get(2).init(1); + events.get(3).init(1); + events.get(4).init(1); assertThat(project(events)).hasSize(5); } @@ -82,28 +87,40 @@ class ProjectionHelperTest { @Test void project_nocache_multipleDetailed() { - List events = Arrays.asList( + List eventsA = Arrays.asList( new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(1), "TEST", "TEST", Role.ADMIN), - new SetLimitEvent(uuid(1), "TEST", new Limit(5)), + new SetLimitEvent(uuid(1), "TEST", new Limit(5))); + List eventsB = Arrays.asList( new CreateGroupEvent(uuid(2), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(2), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(2), "TEST", "TEST", Role.ADMIN), - new SetLimitEvent(uuid(2), "TEST", new Limit(15)), + new SetLimitEvent(uuid(2), "TEST", new Limit(15))); + List eventsC = Arrays.asList( new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(3), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(3), "TEST", "TEST", Role.ADMIN), - new SetLimitEvent(uuid(3), "TEST", new Limit(25)), + new SetLimitEvent(uuid(3), "TEST", new Limit(25))); + List eventsD = Arrays.asList( new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(4), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(4), "TEST", "TEST", Role.ADMIN), new SetLimitEvent(uuid(4), "TEST", new Limit(35))); - initEvents(events); + initEvents(eventsA); + initEvents(eventsB); + initEvents(eventsC); + initEvents(eventsD); + + List events = new ArrayList<>(); + events.addAll(eventsA); + events.addAll(eventsB); + events.addAll(eventsC); + events.addAll(eventsD); List groups = project(events); @@ -156,7 +173,10 @@ class ProjectionHelperTest { new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now())); - initEvents(events); + events.get(0).init(1); + events.get(1).init(1); + events.get(2).init(1); + events.get(3).init(1); project(groups, events, mock(GroupCache.class)); assertThat(groups).hasSize(4); @@ -166,28 +186,41 @@ class ProjectionHelperTest { @Test void project_cache_multipleDetailed() { Map groups = new HashMap<>(); - List events = Arrays.asList( + List eventsA = Arrays.asList( new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(1), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(1), "TEST", "TEST", Role.ADMIN), - new SetLimitEvent(uuid(1), "TEST", new Limit(5)), + new SetLimitEvent(uuid(1), "TEST", new Limit(5))); + List eventsB = Arrays.asList( new CreateGroupEvent(uuid(2), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(2), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(2), "TEST", "TEST", Role.ADMIN), - new SetLimitEvent(uuid(2), "TEST", new Limit(15)), + new SetLimitEvent(uuid(2), "TEST", new Limit(15))); + List eventsC = Arrays.asList( new CreateGroupEvent(uuid(3), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(3), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(3), "TEST", "TEST", Role.ADMIN), - new SetLimitEvent(uuid(3), "TEST", new Limit(25)), + new SetLimitEvent(uuid(3), "TEST", new Limit(25))); + List eventsD = Arrays.asList( new CreateGroupEvent(uuid(4), "TEST", LocalDateTime.now()), new AddMemberEvent(uuid(4), "TEST", "TEST", new User("TEST")), new UpdateRoleEvent(uuid(4), "TEST", "TEST", Role.ADMIN), new SetLimitEvent(uuid(4), "TEST", new Limit(35))); - initEvents(events); + initEvents(eventsA); + initEvents(eventsB); + initEvents(eventsC); + initEvents(eventsD); + + List events = new ArrayList<>(); + events.addAll(eventsA); + events.addAll(eventsB); + events.addAll(eventsC); + events.addAll(eventsD); + project(groups, events, mock(GroupCache.class)); assertThat(groups).hasSize(4); diff --git a/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java b/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java index 071289f..8031301 100644 --- a/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java +++ b/src/test/java/mops/gruppen2/infrastructure/GroupCacheTest.java @@ -301,9 +301,9 @@ class GroupCacheTest { @Test void linksRemove() { - Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().link(1).build(); + Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().link(2).build(); - assertThat(cache.group(String.valueOf(uuid(1)))).isEqualTo(group); + assertThat(cache.group(String.valueOf(uuid(2)))).isEqualTo(group); } //Indirekt: void typesPut() {} From 28ec6de308dae06470ca5bbb868ad3fdcdf9e15a Mon Sep 17 00:00:00 2001 From: Christoph Date: Sun, 19 Apr 2020 15:47:47 +0200 Subject: [PATCH 27/30] destroygroupevent test --- .../domain/event/DestroyGroupEvent.java | 2 +- .../mops/gruppen2/domain/event/Event.java | 9 +- .../gruppen2/domain/model/group/Group.java | 10 +- .../gruppen2/infrastructure/GroupCache.java | 5 +- .../domain/event/AddMemberEventTest.java | 4 +- .../domain/event/CreateGroupEventTest.java | 40 +++++++ .../domain/event/DestroyGroupEventTest.java | 105 ++++++++++++++++++ 7 files changed, 160 insertions(+), 15 deletions(-) diff --git a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java index 18791a6..d244f3f 100644 --- a/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/DestroyGroupEvent.java @@ -20,7 +20,7 @@ public class DestroyGroupEvent extends Event { @Override protected void updateCache(GroupCache cache, Group group) { - cache.groupsRemove(groupid); + cache.groupsRemove(groupid, group); } @Override diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index 981e494..32ec65f 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -77,15 +77,9 @@ public abstract class Event { } checkGroupIdMatch(group.getId()); + group.updateVersion(version); applyEvent(group); updateCache(cache, group); // Update erst nachdem apply keine exception geworfen hat - - // Danach hat die Gruppe nur Nullfelder - if (this instanceof DestroyGroupEvent) { - return; - } - - group.updateVersion(version); } public void apply(Group group) throws EventException { @@ -98,6 +92,7 @@ public abstract class Event { checkGroupIdMatch(group.getId()); group.updateVersion(version); applyEvent(group); + } private void checkGroupIdMatch(UUID groupid) throws IdMismatchException { 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 51dfe79..61feb00 100644 --- a/src/main/java/mops/gruppen2/domain/model/group/Group.java +++ b/src/main/java/mops/gruppen2/domain/model/group/Group.java @@ -58,7 +58,7 @@ public class Group { private GroupMeta meta = GroupMeta.EMPTY(); //TODO: UI set + use for options - private GroupOptions options = GroupOptions.DEFAULT(); + private final GroupOptions options = GroupOptions.DEFAULT(); // Inhalt private Title title = Title.EMPTY(); @@ -71,7 +71,7 @@ public class Group { // Integrationen // Teilnehmer - private Map memberships = new HashMap<>(); + private final Map memberships = new HashMap<>(); // ####################################### Members ########################################### @@ -318,7 +318,9 @@ public class Group { } groupid = null; - type = null; + // Wenn man alles null setzt hat der cache mehr arbeit, weil dieser erst nach der löschung + // geupdated wird und sich link und mitgliedschaften selber heraussuchen muss + /*type = null; parent = null; limit = null; link = null; @@ -327,7 +329,7 @@ public class Group { title = null; description = null; body = null; - memberships = null; + memberships = null;*/ } public String format() { diff --git a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java index 651ab0c..e9fadbf 100644 --- a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java +++ b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java @@ -159,12 +159,15 @@ public class GroupCache { groups.put(groupid, group); } - public void groupsRemove(UUID groupid) { + public void groupsRemove(UUID groupid, Group group) { if (!groups.containsKey(groupid)) { return; } groups.remove(groupid); + links.remove(group.getLink()); + group.getMembers().forEach(user -> users.get(user.getId()).removeIf(usergroup -> !usergroup.exists())); + types.get(group.getType()).removeIf(typegroup -> !typegroup.exists()); } public void linksPut(String link, Group group) { diff --git a/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java b/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java index f2c060e..e48fbe3 100644 --- a/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/AddMemberEventTest.java @@ -58,7 +58,7 @@ class AddMemberEventTest { void apply_userExists() { Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).build(); Event add = new AddMemberEvent(TestHelper.uuid(1), "TEST", "TEST", new User("TEST")); - add.init(3); + add.init(5); assertThatThrownBy(() -> add.apply(group, cache)) .isInstanceOf(UserExistsException.class); @@ -68,7 +68,7 @@ class AddMemberEventTest { void apply_groupFull() { Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); Event add = new AddMemberEvent(TestHelper.uuid(1), "TEST", "PETER", new User("PETER")); - add.init(2); + add.init(4); assertThatThrownBy(() -> add.apply(group, cache)) .isInstanceOf(GroupFullException.class); diff --git a/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java b/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java index df67b7b..ae720de 100644 --- a/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/CreateGroupEventTest.java @@ -1,5 +1,45 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + class CreateGroupEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + + @Test + void apply() { + Group group = Group.EMPTY(); + Event add = new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()); + add.init(1); + + assertThat(group.exists()).isFalse(); + add.apply(group); + assertThat(group.exists()).isTrue(); + } + + @Test + void apply_cache() { + Group group = Group.EMPTY(); + Event add = new CreateGroupEvent(uuid(1), "TEST", LocalDateTime.now()); + add.init(1); + + add.apply(group, cache); + assertThat(group.exists()).isTrue(); + assertThat(cache.groups()).hasSize(1); + } } diff --git a/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java b/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java index ef97a59..fa2634b 100644 --- a/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/DestroyGroupEventTest.java @@ -1,5 +1,110 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.exception.NoAccessException; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + class DestroyGroupEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + + @Test + void apply() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); + Event destroy = new DestroyGroupEvent(uuid(1), "TEST"); + destroy.init(4); + + assertThat(group.exists()).isTrue(); + destroy.apply(group); + assertThat(group.exists()).isFalse(); + } + + @Test + void apply_noadmin() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(3).add("PETER").build(); + Event destroy = new DestroyGroupEvent(uuid(1), "PETER"); + destroy.init(6); + + assertThatThrownBy(() -> destroy.apply(group)) + .isInstanceOf(NoAccessException.class); + } + + @Test + void apply_noadmin_empty() { + Group group = GroupBuilder.get(cache, 1).group().build(); + Event destroy = new DestroyGroupEvent(uuid(1), "PETER"); + destroy.init(2); + + destroy.apply(group); + } + + @Test + void apply_cache_private() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().privat().build(); + Event destroy = new DestroyGroupEvent(uuid(1), "TEST"); + destroy.init(5); + + assertThat(cache.groups()).hasSize(1); + assertThat(cache.userGroups("TEST")).hasSize(1); + assertThat(cache.privates()).hasSize(1); + destroy.apply(group, cache); + assertThat(cache.groups()).isEmpty(); + assertThat(cache.userGroups("TEST")).isEmpty(); + assertThat(cache.privates()).isEmpty(); + } + + @Test + void apply_cache_public() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().publik().build(); + Event destroy = new DestroyGroupEvent(uuid(1), "TEST"); + destroy.init(5); + + assertThat(cache.publics()).hasSize(1); + destroy.apply(group, cache); + assertThat(cache.publics()).isEmpty(); + } + + @Test + void apply_cache_lecture() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().lecture().build(); + Event destroy = new DestroyGroupEvent(uuid(1), "TEST"); + destroy.init(5); + + assertThat(cache.lectures()).hasSize(1); + destroy.apply(group, cache); + assertThat(cache.lectures()).isEmpty(); + } + + @Test + void apply_cache_multipleUsers() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().privat().limit(5).add("A").add("B").add("C").add("D").build(); + Event destroy = new DestroyGroupEvent(uuid(1), "TEST"); + destroy.init(10); + + assertThat(cache.userGroups("TEST")).hasSize(1); + assertThat(cache.userGroups("A")).hasSize(1); + assertThat(cache.userGroups("B")).hasSize(1); + assertThat(cache.userGroups("C")).hasSize(1); + assertThat(cache.userGroups("D")).hasSize(1); + destroy.apply(group, cache); + assertThat(cache.userGroups("TEST")).hasSize(0); + assertThat(cache.userGroups("A")).hasSize(0); + assertThat(cache.userGroups("B")).hasSize(0); + assertThat(cache.userGroups("C")).hasSize(0); + assertThat(cache.userGroups("D")).hasSize(0); + } } From 86f574ac23330ec80e9fd9f0bb1f3cffad91993a Mon Sep 17 00:00:00 2001 From: Christoph Date: Sun, 19 Apr 2020 16:20:20 +0200 Subject: [PATCH 28/30] last tests --- .../mops/gruppen2/domain/event/Event.java | 1 + .../gruppen2/domain/event/SetTypeEvent.java | 10 ++++- .../gruppen2/infrastructure/GroupCache.java | 6 +-- .../domain/event/KickMemberEventTest.java | 39 +++++++++++++++++++ .../domain/event/SetDescriptionEventTest.java | 5 --- .../domain/event/SetInviteLinkEventTest.java | 39 +++++++++++++++++++ .../domain/event/SetLimitEventTest.java | 29 ++++++++++++++ .../domain/event/SetParentEventTest.java | 5 --- .../domain/event/SetTitleEventTest.java | 5 --- .../domain/event/SetTypeEventTest.java | 31 +++++++++++++++ .../domain/event/UpdateRoleEventTest.java | 5 --- 11 files changed, 151 insertions(+), 24 deletions(-) delete mode 100644 src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java delete mode 100644 src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java delete mode 100644 src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java delete mode 100644 src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java diff --git a/src/main/java/mops/gruppen2/domain/event/Event.java b/src/main/java/mops/gruppen2/domain/event/Event.java index 32ec65f..0da2a41 100644 --- a/src/main/java/mops/gruppen2/domain/event/Event.java +++ b/src/main/java/mops/gruppen2/domain/event/Event.java @@ -52,6 +52,7 @@ public abstract class Event { @JsonProperty("date") protected LocalDateTime date; + //TODO: Eigentlich sollte die Gruppe aus dem Cache genommen werden, nicht übergeben public Event(UUID groupid, String exec, String target) { this.groupid = groupid; this.exec = exec; diff --git a/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java b/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java index 6b0af22..85be321 100644 --- a/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java +++ b/src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java @@ -3,6 +3,7 @@ package mops.gruppen2.domain.event; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Value; +import lombok.experimental.NonFinal; import lombok.extern.log4j.Log4j2; import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.model.group.Group; @@ -20,6 +21,12 @@ public class SetTypeEvent extends Event { @JsonProperty("type") Type type; + //TODO: blöder hack, das soll eigentlich anders gehen + // Problem ist, dass die Gruppe vor dem Cache verändert wird, also kann der cache den alten Typ + // nicht mehr aus der Gruppe holen + @NonFinal + Type oldType; + public SetTypeEvent(UUID groupId, String exec, @Valid Type type) { super(groupId, exec, null); @@ -28,12 +35,13 @@ public class SetTypeEvent extends Event { @Override protected void updateCache(GroupCache cache, Group group) { - cache.typesRemove(group); + cache.typesRemove(oldType, group); cache.typesPut(type, group); } @Override protected void applyEvent(Group group) throws EventException { + oldType = group.getType(); group.setType(exec, type); } diff --git a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java index e9fadbf..9dc33de 100644 --- a/src/main/java/mops/gruppen2/infrastructure/GroupCache.java +++ b/src/main/java/mops/gruppen2/infrastructure/GroupCache.java @@ -198,11 +198,11 @@ public class GroupCache { types.get(type).add(group); } - public void typesRemove(Group group) { - if (!types.containsKey(group.getType())) { + public void typesRemove(Type type, Group group) { + if (!types.containsKey(type)) { return; } - types.get(group.getType()).remove(group); + types.get(type).remove(group); } } diff --git a/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java b/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java index 3736773..51765f7 100644 --- a/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/KickMemberEventTest.java @@ -1,5 +1,44 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + class KickMemberEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + + @Test + void apply() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); + Event kick = new KickMemberEvent(uuid(1), "TEST", "TEST"); + kick.init(4); + + assertThat(group.size()).isOne(); + kick.apply(group); + assertThat(group.size()).isZero(); + } + + @Test + void apply_cache() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); + Event kick = new KickMemberEvent(uuid(1), "TEST", "TEST"); + kick.init(4); + + assertThat(cache.userGroups("TEST")).hasSize(1); + kick.apply(group, cache); + assertThat(cache.userGroups("TEST")).hasSize(0); + } } diff --git a/src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java deleted file mode 100644 index 34e56c6..0000000 --- a/src/test/java/mops/gruppen2/domain/event/SetDescriptionEventTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package mops.gruppen2.domain.event; - -class SetDescriptionEventTest { - -} diff --git a/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java index a1a2480..d009529 100644 --- a/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/SetInviteLinkEventTest.java @@ -1,5 +1,44 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.wrapper.Link; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + class SetInviteLinkEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + + @Test + void apply() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); + Event link = new SetInviteLinkEvent(uuid(1), "TEST", new Link(uuid(2).toString())); + link.init(4); + + link.apply(group); + assertThat(group.getLink()).isEqualTo(uuid(2).toString()); + } + + @Test + void apply_cache() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().build(); + Event link = new SetInviteLinkEvent(uuid(1), "TEST", new Link(uuid(2).toString())); + link.init(4); + + assertThat(cache.group(group.getLink())).isEqualTo(group); + link.apply(group, cache); + assertThat(cache.group(uuid(2).toString())).isEqualTo(group); + } } diff --git a/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java index 73e13d0..c23ace3 100644 --- a/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/SetLimitEventTest.java @@ -1,5 +1,34 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.exception.BadArgumentException; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.wrapper.Limit; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + class SetLimitEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + + @Test + void apply_tooSmall() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().limit(2).add("PETER").build(); + Event limit = new SetLimitEvent(uuid(1), "TEST", new Limit(1)); + limit.init(6); + + assertThatThrownBy(() -> limit.apply(group)) + .isInstanceOf(BadArgumentException.class); + } } diff --git a/src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java deleted file mode 100644 index cd84c5a..0000000 --- a/src/test/java/mops/gruppen2/domain/event/SetParentEventTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package mops.gruppen2.domain.event; - -class SetParentEventTest { - -} diff --git a/src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java deleted file mode 100644 index a4a505b..0000000 --- a/src/test/java/mops/gruppen2/domain/event/SetTitleEventTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package mops.gruppen2.domain.event; - -class SetTitleEventTest { - -} diff --git a/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java b/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java index 07ab586..892325f 100644 --- a/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java +++ b/src/test/java/mops/gruppen2/domain/event/SetTypeEventTest.java @@ -1,5 +1,36 @@ package mops.gruppen2.domain.event; +import mops.gruppen2.GroupBuilder; +import mops.gruppen2.domain.model.group.Group; +import mops.gruppen2.domain.model.group.Type; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static mops.gruppen2.TestHelper.uuid; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + class SetTypeEventTest { + GroupCache cache; + + @BeforeEach + void setUp() { + cache = new GroupCache(mock(EventStoreService.class)); + } + + @Test + void apply_cache() { + Group group = GroupBuilder.get(cache, 1).group().testadmin().privat().build(); + Event type = new SetTypeEvent(uuid(1), "TEST", Type.LECTURE); + type.init(5); + + assertThat(cache.privates()).hasSize(1); + assertThat(cache.lectures()).isEmpty(); + type.apply(group, cache); + assertThat(cache.lectures()).hasSize(1); + assertThat(cache.privates()).isEmpty(); + } } diff --git a/src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java b/src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java deleted file mode 100644 index 80e2b7e..0000000 --- a/src/test/java/mops/gruppen2/domain/event/UpdateRoleEventTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package mops.gruppen2.domain.event; - -class UpdateRoleEventTest { - -} From 828d7ac568a7db2d2b842967cba8fc22c1712378 Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 22 Apr 2020 14:27:04 +0200 Subject: [PATCH 29/30] tests --- .../api/GroupRequestWrapper.java | 2 ++ .../controller/APIController.java | 2 ++ .../controller/APIControllerTest.java | 32 ++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/java/mops/gruppen2/infrastructure/api/GroupRequestWrapper.java b/src/main/java/mops/gruppen2/infrastructure/api/GroupRequestWrapper.java index 93a0c20..d2d059d 100644 --- a/src/main/java/mops/gruppen2/infrastructure/api/GroupRequestWrapper.java +++ b/src/main/java/mops/gruppen2/infrastructure/api/GroupRequestWrapper.java @@ -1,12 +1,14 @@ package mops.gruppen2.infrastructure.api; import lombok.AllArgsConstructor; +import lombok.Getter; import java.util.List; /** * Kombiniert den Status und die Gruppenliste zur ausgabe über die API. */ +@Getter @AllArgsConstructor public class GroupRequestWrapper { diff --git a/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java b/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java index 3e3f9b9..1c79bd2 100644 --- a/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java +++ b/src/main/java/mops/gruppen2/infrastructure/controller/APIController.java @@ -41,6 +41,8 @@ public class APIController { * * @param eventId Die Event-ID, welche der Anfragesteller beim letzten Aufruf erhalten hat */ + //TODO: sollte den cache benutzen, am besten wäre eine groupversion, welche der eventid + //TODO: entspricht, dann kann man leicht alle geänderten gruppen finden @GetMapping("/update/{id}") @Secured("ROLE_api_user") @ApiOperation("Gibt veränderte Gruppen zurück") diff --git a/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java b/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java index 312376a..e4cbf27 100644 --- a/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java +++ b/src/test/java/mops/gruppen2/infrastructure/controller/APIControllerTest.java @@ -1,22 +1,52 @@ package mops.gruppen2.infrastructure.controller; +import mops.gruppen2.domain.service.EventStoreService; +import mops.gruppen2.infrastructure.GroupCache; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.springframework.security.test.context.support.WithMockUser; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class APIControllerTest { + private EventStoreService store; + private GroupCache cache; + private APIController controller; + @BeforeEach void setUp() { + store = mock(EventStoreService.class); + cache = new GroupCache(store); + controller = new APIController(cache, store); } + @WithMockUser("ROLE_api_user") @Test - void getApiUpdate() { + void getApiUpdate_noEvents() { + when(store.findMaxEventId()).thenReturn(0L); + + assertThat(controller.getApiUpdate(0).getVersion()).isZero(); + assertThat(controller.getApiUpdate(0).getGroups()).isEmpty(); } + @Disabled + @WithMockUser("ROLE_api_user") + @Test + void getApiUpdate_noUpdate() { + } + + @Disabled + @WithMockUser("ROLE_api_user") @Test void getApiUserGroups() { } + @Disabled + @WithMockUser("ROLE_api_user") @Test void getApiGroup() { } From 8a418fdeeab077814819da74bee4a3b22a07fa9e Mon Sep 17 00:00:00 2001 From: Christoph Date: Wed, 22 Apr 2020 14:34:16 +0200 Subject: [PATCH 30/30] tests --- src/main/resources/static/css/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index c9861fc..dab0b4a 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -76,6 +76,7 @@ h1, h3 { /*Button*/ .btn { + width: 250px; } .btn-spacer {