1

Merge pull request #10 from ChUrl/REFACTOR-domain-model

Refactor domain model
This commit is contained in:
Christoph
2020-04-14 17:32:24 +02:00
committed by GitHub
110 changed files with 1857 additions and 2602 deletions

View File

@ -82,6 +82,7 @@ dependencies {
implementation 'io.springfox:springfox-swagger-ui:2.9.2'
implementation 'com.github.javafaker:javafaker:1.0.2'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.10.3'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.3'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

2
lombok.config Normal file
View File

@ -0,0 +1,2 @@
lombok.anyConstructor.addConstructorProperties = true
lombok.equalsAndHashCode.callSuper = call

View File

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

View File

@ -13,7 +13,7 @@ import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Log4j2
@Profile("dev")
@Profile({"dev", "docker"})
@Aspect
@Component
public class LogAspect {
@ -36,12 +36,12 @@ public class LogAspect {
@Before("@annotation(mops.gruppen2.aspect.annotation.Trace)")
public void logCustom(JoinPoint joinPoint) {
public static void logCustom(JoinPoint joinPoint) {
log.trace(((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Trace.class).value());
}
@Before("@annotation(mops.gruppen2.aspect.annotation.TraceMethodCall) || logMethodCalls()")
public void logMethodCall(JoinPoint joinPoint) {
public static void logMethodCall(JoinPoint joinPoint) {
log.trace("Methodenaufruf: {} ({})",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName().replace("mops.gruppen2.", ""));
@ -50,7 +50,7 @@ public class LogAspect {
}
@Around("@annotation(mops.gruppen2.aspect.annotation.TraceExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
public static Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
joinPoint.proceed();
long stop = System.currentTimeMillis();

View File

@ -0,0 +1,15 @@
package mops.gruppen2.config;
import mops.gruppen2.config.converter.StringToLimitConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToLimitConverter());
}
}

View File

@ -0,0 +1,12 @@
package mops.gruppen2.config.converter;
import mops.gruppen2.domain.model.group.wrapper.Limit;
import org.springframework.core.convert.converter.Converter;
public class StringToLimitConverter implements Converter<String, Limit> {
@Override
public Limit convert(String value) {
return new Limit(Long.parseLong(value));
}
}

View File

@ -1,44 +0,0 @@
package mops.gruppen2.domain;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Repräsentiert den aggregierten Zustand einer Gruppe.
*/
@Getter
@Setter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString
public class Group {
@EqualsAndHashCode.Include
private UUID id;
@ToString.Exclude
private UUID parent;
//TODO: Single Type for Public/Private/Lecture?
private GroupType type;
private String title;
private String description;
// Default + Minimum: 1
@ToString.Exclude
private long userLimit = 1;
//TODO: List to Hashmap
@ToString.Exclude
private final List<User> members = new ArrayList<>();
@ToString.Exclude
private final Map<String, Role> roles = new HashMap<>();
}

View File

@ -1,7 +0,0 @@
package mops.gruppen2.domain;
public enum GroupType {
PUBLIC,
PRIVATE,
LECTURE
}

View File

@ -1,6 +0,0 @@
package mops.gruppen2.domain;
public enum Limit {
INFINITE,
LOCKED
}

View File

@ -1,10 +0,0 @@
package mops.gruppen2.domain;
public enum Role {
ADMIN,
MEMBER;
public Role toggle() {
return this == ADMIN ? MEMBER : ADMIN;
}
}

View File

@ -0,0 +1,46 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
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.model.group.Group;
import mops.gruppen2.domain.model.group.User;
import java.util.UUID;
/**
* Fügt einen einzelnen Nutzer einer Gruppe hinzu.
*/
@Log4j2
@Value
@AllArgsConstructor
public class AddMemberEvent extends Event {
@JsonProperty("user")
User user;
public AddMemberEvent(UUID groupId, String exec, String target, User user) throws IdMismatchException {
super(groupId, exec, target);
this.user = user;
if (!target.equals(user.getId())) {
throw new IdMismatchException("Der User passt nicht zur angegebenen userid.");
}
}
@Override
protected void applyEvent(Group group) throws UserAlreadyExistsException, GroupFullException {
group.addMember(target, user);
log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers());
}
@Override
public String type() {
return EventType.ADDMEMBER.toString();
}
}

View File

@ -1,61 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupFullException;
import mops.gruppen2.domain.exception.UserAlreadyExistsException;
import java.util.UUID;
/**
* Fügt einen einzelnen Nutzer einer Gruppe hinzu.
*/
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
public class AddUserEvent extends Event {
private String givenname;
private String familyname;
private String email;
public AddUserEvent(UUID groupId, String userId, String givenname, String familyname, String email) {
super(groupId, userId);
this.givenname = givenname;
this.familyname = familyname;
this.email = email;
}
public AddUserEvent(Group group, User user) {
super(group.getId(), user.getId());
givenname = user.getGivenname();
familyname = user.getFamilyname();
email = user.getEmail();
}
@Override
protected void applyEvent(Group group) throws EventException {
User user = new User(userId, givenname, familyname, email);
if (group.getMembers().contains(user)) {
throw new UserAlreadyExistsException(getClass().toString());
}
if (group.getMembers().size() >= group.getUserLimit()) {
throw new GroupFullException(getClass().toString());
}
group.getMembers().add(user);
group.getRoles().put(userId, Role.MEMBER);
log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers());
log.trace("\t\t\t\t\tNeue Rollen: {}", group.getRoles());
}
}

View File

@ -1,35 +1,44 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.model.group.Group;
import java.time.LocalDateTime;
import java.util.UUID;
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
@Value
@AllArgsConstructor// Value generiert den allArgsConstrucot nur, wenn keiner explizit angegeben ist
public class CreateGroupEvent extends Event {
private UUID groupParent;
private GroupType groupType;
@JsonProperty("date")
LocalDateTime date;
public CreateGroupEvent(UUID groupId, String userId, UUID parent, GroupType type) {
super(groupId, userId);
groupParent = parent;
groupType = type;
public CreateGroupEvent(UUID groupId, String exec, LocalDateTime date) {
super(groupId, exec, null);
this.date = date;
}
@Override
protected void applyEvent(Group group) {
group.setId(groupId);
group.setParent(groupParent);
group.setType(groupType);
protected void applyEvent(Group group) throws BadArgumentException {
group.setId(groupid);
group.setCreator(exec);
group.setCreationDate(date);
log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString());
}
@Override
public String type() {
return EventType.CREATEGROUP.toString();
}
@Override
public String toString() {
return "(" + version + "," + groupid + "," + date + ")";
}
}

View File

@ -1,38 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import java.util.UUID;
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
public class DeleteGroupEvent extends Event {
public DeleteGroupEvent(UUID groupId, String userId) {
super(groupId, userId);
}
public DeleteGroupEvent(Group group, User user) {
super(group.getId(), user.getId());
}
@Override
protected void applyEvent(Group group) {
group.getRoles().clear();
group.getMembers().clear();
group.setTitle(null);
group.setDescription(null);
group.setType(null);
group.setParent(null);
group.setUserLimit(0L);
log.trace("\t\t\t\t\tGelöschte Gruppe: {}", group);
}
}

View File

@ -1,47 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.UserNotFoundException;
import java.util.UUID;
/**
* Entfernt ein einzelnes Mitglied einer Gruppe.
*/
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
public class DeleteUserEvent extends Event {
public DeleteUserEvent(UUID groupId, String userId) {
super(groupId, userId);
}
public DeleteUserEvent(Group group, User user) {
super(group.getId(), user.getId());
}
//TODO: what the fuck use List.remove
@Override
protected void applyEvent(Group group) throws EventException {
for (User user : group.getMembers()) {
if (user.getId().equals(userId)) {
group.getMembers().remove(user);
group.getRoles().remove(user.getId());
log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers());
log.trace("\t\t\t\t\tNeue Rollen: {}", group.getRoles());
return;
}
}
throw new UserNotFoundException(getClass().toString());
}
}

View File

@ -0,0 +1,31 @@
package mops.gruppen2.domain.event;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import java.util.UUID;
@Log4j2
@Value
@AllArgsConstructor
public class DestroyGroupEvent extends Event {
public DestroyGroupEvent(UUID groupId, String exec) {
super(groupId, exec, null);
}
@Override
protected void applyEvent(Group group) throws NoAccessException {
group.destroy(exec);
log.trace("\t\t\t\t\tGelöschte Gruppe: {}", group.toString());
}
@Override
public String type() {
return EventType.DESTROYGROUP.toString();
}
}

View File

@ -1,61 +1,92 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupIdMismatchException;
import mops.gruppen2.domain.exception.IdMismatchException;
import mops.gruppen2.domain.model.group.Group;
import java.util.UUID;
@Log4j2
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = AddUserEvent.class, name = "AddUserEvent"),
@JsonSubTypes.Type(value = CreateGroupEvent.class, name = "CreateGroupEvent"),
@JsonSubTypes.Type(value = DeleteUserEvent.class, name = "DeleteUserEvent"),
@JsonSubTypes.Type(value = UpdateGroupDescriptionEvent.class, name = "UpdateGroupDescriptionEvent"),
@JsonSubTypes.Type(value = UpdateGroupTitleEvent.class, name = "UpdateGroupTitleEvent"),
@JsonSubTypes.Type(value = UpdateRoleEvent.class, name = "UpdateRoleEvent"),
@JsonSubTypes.Type(value = DeleteGroupEvent.class, name = "DeleteGroupEvent"),
@JsonSubTypes.Type(value = UpdateUserLimitEvent.class, name = "UpdateUserLimitEvent")
})
@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")})
@Getter
@NoArgsConstructor
@AllArgsConstructor
@NoArgsConstructor // Lombok needs a default constructor in the base class
public abstract class Event {
protected UUID groupId;
protected String userId;
@JsonProperty("groupid")
protected UUID groupid;
@JsonProperty("version")
protected long version;
@JsonProperty("exec")
protected String exec;
@JsonProperty("target")
protected String target;
public Event(UUID groupid, String exec, String target) {
this.groupid = groupid;
this.exec = exec;
this.target = target;
}
public void init(long version) {
if (this.version != 0) {
throw new BadArgumentException("Event wurde schon initialisiert. (" + type() + ")");
}
log.trace("Event wurde initialisiert. (" + type() + "," + version + ")");
this.version = version;
}
public Group 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());
log.trace("Event angewendet:\t{}", this);
group.update(version);
applyEvent(group);
return group;
}
private void checkGroupIdMatch(UUID groupId) {
private void checkGroupIdMatch(UUID groupId) throws IdMismatchException {
// CreateGroupEvents müssen die Id erst initialisieren
if (this instanceof CreateGroupEvent) {
return;
}
if (!this.groupId.equals(groupId)) {
throw new GroupIdMismatchException(getClass().toString());
if (!groupid.equals(groupId)) {
throw new IdMismatchException("Das Event gehört zu einer anderen Gruppe");
}
}
protected abstract void applyEvent(Group group) throws EventException;
@JsonIgnore
public abstract String type();
}

View File

@ -0,0 +1,15 @@
package mops.gruppen2.domain.event;
public enum EventType {
ADDMEMBER,
CREATEGROUP,
DESTROYGROUP,
KICKMEMBER,
SETDESCRIPTION,
SETLINK,
SETLIMIT,
SETPARENT,
SETTITLE,
SETTYPE,
UPDATEROLE
}

View File

@ -0,0 +1,35 @@
package mops.gruppen2.domain.event;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.LastAdminException;
import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.model.group.Group;
import java.util.UUID;
/**
* Entfernt ein einzelnes Mitglied einer Gruppe.
*/
@Log4j2
@Value
@AllArgsConstructor
public class KickMemberEvent extends Event {
public KickMemberEvent(UUID groupId, String exec, String target) {
super(groupId, exec, target);
}
@Override
protected void applyEvent(Group group) throws UserNotFoundException, LastAdminException {
group.kickMember(target);
log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers());
}
@Override
public String type() {
return EventType.KICKMEMBER.toString();
}
}

View File

@ -0,0 +1,41 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.wrapper.Description;
import javax.validation.Valid;
import java.util.UUID;
/**
* Ändert nur die Gruppenbeschreibung.
*/
@Log4j2
@Value
@AllArgsConstructor
public class SetDescriptionEvent extends Event {
@JsonProperty("desc")
Description description;
public SetDescriptionEvent(UUID groupId, String exec, @Valid Description description) {
super(groupId, exec, null);
this.description = description;
}
@Override
protected void applyEvent(Group group) throws NoAccessException {
group.setDescription(exec, description);
log.trace("\t\t\t\t\tNeue Beschreibung: {}", group.getDescription());
}
@Override
public String type() {
return EventType.SETDESCRIPTION.toString();
}
}

View File

@ -0,0 +1,38 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.wrapper.Link;
import javax.validation.Valid;
import java.util.UUID;
@Log4j2
@Value
@AllArgsConstructor
public class SetInviteLinkEvent extends Event {
@JsonProperty("link")
Link link;
public SetInviteLinkEvent(UUID groupId, String exec, @Valid Link link) {
super(groupId, exec, null);
this.link = link;
}
@Override
protected void applyEvent(Group group) throws NoAccessException {
group.setLink(exec, link);
log.trace("\t\t\t\t\tNeuer Link: {}", group.getLink());
}
@Override
public String type() {
return EventType.SETLINK.toString();
}
}

View File

@ -0,0 +1,39 @@
package mops.gruppen2.domain.event;
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.Limit;
import javax.validation.Valid;
import java.util.UUID;
@Log4j2
@Value
@AllArgsConstructor
public class SetLimitEvent extends Event {
@JsonProperty("limit")
Limit limit;
public SetLimitEvent(UUID groupId, String exec, @Valid Limit limit) {
super(groupId, exec, null);
this.limit = limit;
}
@Override
protected void applyEvent(Group group) throws BadArgumentException, NoAccessException {
group.setLimit(exec, limit);
log.trace("\t\t\t\t\tNeues UserLimit: {}", group.getLimit());
}
@Override
public String type() {
return EventType.SETLIMIT.toString();
}
}

View File

@ -0,0 +1,38 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.wrapper.Parent;
import javax.validation.Valid;
import java.util.UUID;
@Log4j2
@Value
@AllArgsConstructor
public class SetParentEvent extends Event {
@JsonProperty("parent")
Parent parent;
public SetParentEvent(UUID groupId, String exec, @Valid Parent parent) {
super(groupId, exec, null);
this.parent = parent;
}
@Override
protected void applyEvent(Group group) throws NoAccessException {
group.setParent(exec, parent);
log.trace("\t\t\t\t\tNeues Parent: {}", group.getParent());
}
@Override
public String type() {
return EventType.SETPARENT.toString();
}
}

View File

@ -0,0 +1,42 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.wrapper.Title;
import javax.validation.Valid;
import java.util.UUID;
/**
* Ändert nur den Gruppentitel.
*/
@Log4j2
@Value
@AllArgsConstructor
public class SetTitleEvent extends Event {
@JsonProperty("title")
Title title;
public SetTitleEvent(UUID groupId, String exec, @Valid Title title) {
super(groupId, exec, null);
this.title = title;
}
@Override
protected void applyEvent(Group group) throws NoAccessException {
group.setTitle(exec, title);
log.trace("\t\t\t\t\tNeuer Titel: {}", group.getTitle());
}
@Override
public String type() {
return EventType.SETTITLE.toString();
}
}

View File

@ -0,0 +1,37 @@
package mops.gruppen2.domain.event;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
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 javax.validation.Valid;
import java.util.UUID;
@Log4j2
@Value
@AllArgsConstructor
public class SetTypeEvent extends Event {
@JsonProperty("type")
Type type;
public SetTypeEvent(UUID groupId, String exec, @Valid Type type) {
super(groupId, exec, null);
this.type = type;
}
@Override
protected void applyEvent(Group group) throws EventException {
group.setType(exec, type);
}
@Override
public String type() {
return EventType.SETTYPE.toString();
}
}

View File

@ -1,44 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.BadParameterException;
import java.util.UUID;
/**
* Ändert nur die Gruppenbeschreibung.
*/
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
public class UpdateGroupDescriptionEvent extends Event {
private String newGroupDescription;
public UpdateGroupDescriptionEvent(UUID groupId, String userId, String newGroupDescription) {
super(groupId, userId);
this.newGroupDescription = newGroupDescription;
}
public UpdateGroupDescriptionEvent(Group group, User user, String newGroupDescription) {
super(group.getId(), user.getId());
this.newGroupDescription = newGroupDescription;
}
@Override
protected void applyEvent(Group group) {
if (newGroupDescription.isEmpty()) {
throw new BadParameterException("Die Beschreibung ist leer.");
}
group.setDescription(newGroupDescription);
log.trace("\t\t\t\t\tNeue Beschreibung: {}", group.getDescription());
}
}

View File

@ -1,45 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.BadParameterException;
import java.util.UUID;
/**
* Ändert nur den Gruppentitel.
*/
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
public class UpdateGroupTitleEvent extends Event {
private String newGroupTitle;
public UpdateGroupTitleEvent(UUID groupId, String userId, String newGroupTitle) {
super(groupId, userId);
this.newGroupTitle = newGroupTitle;
}
public UpdateGroupTitleEvent(Group group, User user, String newGroupTitle) {
super(group.getId(), user.getId());
this.newGroupTitle = newGroupTitle;
}
@Override
protected void applyEvent(Group group) {
if (newGroupTitle.isEmpty()) {
throw new BadParameterException("Der Titel ist leer.");
}
group.setTitle(newGroupTitle);
log.trace("\t\t\t\t\tNeuer Titel: {}", group.getTitle());
}
}

View File

@ -1,48 +1,42 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.LastAdminException;
import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.Role;
import java.util.UUID;
/**
* Aktualisiert die Gruppenrolle eines Teilnehmers.
*/
@Getter
@NoArgsConstructor // For Jackson
@ToString
@Log4j2
@Value
@AllArgsConstructor
public class UpdateRoleEvent extends Event {
private Role newRole;
@JsonProperty("role")
Role role;
public UpdateRoleEvent(UUID groupId, String userId, Role newRole) {
super(groupId, userId);
this.newRole = newRole;
}
public UpdateRoleEvent(Group group, User user, Role newRole) {
super(group.getId(), user.getId());
this.newRole = newRole;
public UpdateRoleEvent(UUID groupId, String exec, String target, Role role) {
super(groupId, exec, target);
this.role = role;
}
@Override
protected void applyEvent(Group group) throws UserNotFoundException {
if (group.getRoles().containsKey(userId)) {
group.getRoles().put(userId, newRole);
protected void applyEvent(Group group) throws UserNotFoundException, LastAdminException {
group.memberPutRole(target, role);
log.trace("\t\t\t\t\tNeue Rollen: {}", group.getRoles());
log.trace("\t\t\t\t\tNeue Admin: {}", group.getAdmins());
}
return;
}
throw new UserNotFoundException(getClass().toString());
@Override
public String type() {
return EventType.UPDATEROLE.toString();
}
}

View File

@ -1,42 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.BadParameterException;
import mops.gruppen2.domain.exception.EventException;
import java.util.UUID;
@Getter
@NoArgsConstructor
@ToString
@Log4j2
public class UpdateUserLimitEvent extends Event {
private long userLimit;
public UpdateUserLimitEvent(UUID groupId, String userId, long userLimit) {
super(groupId, userId);
this.userLimit = userLimit;
}
public UpdateUserLimitEvent(Group group, User user, long userLimit) {
super(group.getId(), user.getId());
this.userLimit = userLimit;
}
@Override
protected void applyEvent(Group group) throws EventException {
if (userLimit <= 0 || userLimit < group.getMembers().size()) {
throw new BadParameterException("Usermaximum zu klein.");
}
group.setUserLimit(userLimit);
log.trace("\t\t\t\t\tNeues UserLimit: {}", group.getUserLimit());
}
}

View File

@ -0,0 +1,12 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class BadArgumentException extends EventException {
private static final long serialVersionUID = -6757742013238625595L;
public BadArgumentException(String info) {
super(HttpStatus.BAD_REQUEST, "Fehlerhafter Parameter.", info);
}
}

View File

@ -1,10 +0,0 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class BadParameterException extends EventException {
public BadParameterException(String info) {
super(HttpStatus.BAD_REQUEST, "Fehlerhafter Parameter angegeben!", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class BadPayloadException extends EventException {
private static final long serialVersionUID = -3978242017847155629L;
public BadPayloadException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Die Payload konnte nicht übersetzt werden!", info);
super(HttpStatus.INTERNAL_SERVER_ERROR, "Payload konnte nicht übersetzt werden.", info);
}
}

View File

@ -5,8 +5,10 @@ import org.springframework.web.server.ResponseStatusException;
public class EventException extends ResponseStatusException {
private static final long serialVersionUID = 6784052016028094340L;
public EventException(HttpStatus status, String msg, String info) {
super(status, msg + " (" + info + ")");
super(status, info.isBlank() ? "" : msg + " (" + info + ")");
}
}

View File

@ -4,8 +4,10 @@ import org.springframework.http.HttpStatus;
public class GroupFullException extends EventException {
private static final long serialVersionUID = -4011141160467668713L;
public GroupFullException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Die Gruppe hat die maximale Midgliederanzahl bereits erreicht!", info);
super(HttpStatus.INTERNAL_SERVER_ERROR, "Gruppe hat maximale Teilnehmeranzahl bereits erreicht.", info);
}
}

View File

@ -1,10 +0,0 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class GroupIdMismatchException extends EventException {
public GroupIdMismatchException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Falsche Gruppe für Event.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class GroupNotFoundException extends EventException {
private static final long serialVersionUID = -4738218416842951106L;
public GroupNotFoundException(String info) {
super(HttpStatus.NOT_FOUND, "Die Gruppe existiert nicht oder wurde gelöscht.", info);
super(HttpStatus.NOT_FOUND, "Gruppe existiert nicht oder wurde gelöscht.", info);
}
}

View File

@ -0,0 +1,13 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class IdMismatchException extends EventException {
private static final long serialVersionUID = 7944077617758922089L;
public IdMismatchException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Ids stimmen nicht überein.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class InvalidInviteException extends EventException {
private static final long serialVersionUID = 2643001101459427944L;
public InvalidInviteException(String info) {
super(HttpStatus.NOT_FOUND, "Der Einladungslink ist ungültig.", info);
super(HttpStatus.NOT_FOUND, "Einladungslink ist ungültig oder Gruppe wurde gelöscht.", info);
}
}

View File

@ -0,0 +1,12 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class LastAdminException extends EventException {
private static final long serialVersionUID = 9059481382346544288L;
public LastAdminException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Gruppe braucht mindestens einen Admin.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class NoAccessException extends EventException {
private static final long serialVersionUID = 1696988497122834654L;
public NoAccessException(String info) {
super(HttpStatus.FORBIDDEN, "Hier hast du leider keinen Zugriff!", info);
super(HttpStatus.FORBIDDEN, "Kein Zugriff.", info);
}
}

View File

@ -1,10 +0,0 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class NoAdminAfterActionException extends EventException {
public NoAdminAfterActionException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Nach dieser Aktion hätte die Gruppe keinen Admin mehr", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class NoInviteExistException extends EventException {
private static final long serialVersionUID = -8092076461455840693L;
public NoInviteExistException(String info) {
super(HttpStatus.NOT_FOUND, "Für diese Gruppe existiert kein Link.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class PageNotFoundException extends EventException {
private static final long serialVersionUID = 2374509005158710104L;
public PageNotFoundException(String info) {
super(HttpStatus.NOT_FOUND, "Die Seite wurde nicht gefunden!", info);
super(HttpStatus.NOT_FOUND, "Seite wurde nicht gefunden.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class UserAlreadyExistsException extends EventException {
private static final long serialVersionUID = -8150634358760194625L;
public UserAlreadyExistsException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Der User existiert bereits.", info);
super(HttpStatus.INTERNAL_SERVER_ERROR, "User existiert bereits.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class UserNotFoundException extends EventException {
private static final long serialVersionUID = 8347442921199785291L;
public UserNotFoundException(String info) {
super(HttpStatus.NOT_FOUND, "Der User existiert nicht.", info);
super(HttpStatus.NOT_FOUND, "User existiert nicht.", info);
}
}

View File

@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
public class WrongFileException extends EventException {
private static final long serialVersionUID = -166192514348555116L;
public WrongFileException(String info) {
super(HttpStatus.BAD_REQUEST, "Die entsprechende Datei ist keine valide CSV-Datei!", info);
super(HttpStatus.BAD_REQUEST, "Datei ist keine valide CSV-Datei.", info);
}
}

View File

@ -1,16 +1,18 @@
package mops.gruppen2.domain.helper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.web.api.GroupRequestWrapper;
import java.util.List;
//TODO: sinnvolles format
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class APIHelper {
private APIHelper() {}
public static GroupRequestWrapper wrap(long status, List<Group> groupList) {
return new GroupRequestWrapper(status, groupList);
}

View File

@ -0,0 +1,41 @@
package mops.gruppen2.domain.helper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.EventType;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CommonHelper {
public static String[] eventTypesToString(EventType... types) {
String[] stringtypes = new String[types.length];
for (int i = 0; i < types.length; i++) {
stringtypes[i] = types[i].toString();
}
return stringtypes;
}
public static List<String> uuidsToString(List<UUID> ids) {
return ids.stream()
.map(UUID::toString)
.collect(Collectors.toList());
}
public static boolean uuidIsEmpty(UUID uuid) {
return "00000000-0000-0000-0000-000000000000".equals(uuid.toString());
}
public static List<UUID> stringsToUUID(List<String> groupids) {
return groupids.stream()
.map(UUID::fromString)
.collect(Collectors.toList());
}
}

View File

@ -3,10 +3,12 @@ package mops.gruppen2.domain.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.User;
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;
@ -16,10 +18,9 @@ import java.util.List;
import java.util.stream.Collectors;
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CsvHelper {
private CsvHelper() {}
public static List<User> readCsvFile(MultipartFile file) throws EventException {
if (file == null || file.isEmpty()) {
return Collections.emptyList();

View File

@ -1,49 +0,0 @@
package mops.gruppen2.domain.helper;
import lombok.extern.log4j.Log4j2;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Log4j2
public final class IdHelper {
private IdHelper() {}
public static List<UUID> stringsToUUID(List<String> groupIds) {
return groupIds.stream()
.map(IdHelper::stringToUUID)
.collect(Collectors.toList());
}
/**
* Wandelt einen String in eine UUID um.
* Dabei wird eine "leere" UUID generiert, falls der String leer ist.
*
* @param groupId Id als String
*
* @return Id als UUID
*/
public static UUID stringToUUID(String groupId) {
return groupId.isEmpty() ? emptyUUID() : UUID.fromString(groupId);
}
public static List<String> uuidsToString(List<UUID> groupIds) {
return groupIds.stream()
.map(UUID::toString)
.collect(Collectors.toList());
}
public static String uuidToString(UUID groupId) {
return groupId.toString();
}
public static boolean isEmpty(UUID id) {
return id == null || emptyUUID().equals(id);
}
public static UUID emptyUUID() {
return UUID.fromString("00000000-0000-0000-0000-000000000000");
}
}

View File

@ -2,6 +2,9 @@ package mops.gruppen2.domain.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;
@ -9,10 +12,9 @@ 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 {
private JsonHelper() {}
/**
* Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload.
*
@ -24,8 +26,10 @@ public final class JsonHelper {
*/
public static String serializeEvent(Event event) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(event);
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
String payload = mapper.writeValueAsString(event);
log.trace(payload);
return payload;
}
/**
@ -38,7 +42,9 @@ public final class JsonHelper {
* @throws JsonProcessingException Bei JSON Fehler
*/
public static Event deserializeEvent(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(json, Event.class);
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
Event event = mapper.readValue(json, Event.class);
log.trace(event);
return event;
}
}

View File

@ -1,118 +1,105 @@
package mops.gruppen2.domain.helper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.BadParameterException;
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.NoAdminAfterActionException;
import mops.gruppen2.domain.exception.UserAlreadyExistsException;
import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.web.form.CreateForm;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.Type;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import static mops.gruppen2.domain.Role.ADMIN;
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ValidationHelper {
private ValidationHelper() {}
// ######################################## CHECK ############################################
/**
* Überprüft, ob ein User in einer Gruppe teilnimmt.
*/
public static boolean checkIfMember(Group group, User user) {
return group.getMembers().contains(user);
public static boolean checkIfMember(Group group, String userid) {
return group.isMember(userid);
}
public static boolean checkIfLastMember(User user, Group group) {
return checkIfMember(group, user) && group.getMembers().size() == 1;
public static boolean checkIfLastMember(Group group, String userid) {
return checkIfMember(group, userid) && group.size() == 1;
}
/**
* Überprüft, ob eine Gruppe voll ist.
*/
public static boolean checkIfGroupFull(Group group) {
return group.getMembers().size() >= group.getUserLimit();
return group.isFull();
}
/**
* Überprüft, ob eine Gruppe leer ist.
*/
public static boolean checkIfGroupEmpty(Group group) {
return group.getMembers().isEmpty();
return group.isEmpty();
}
/**
* Überprüft, ob ein User in einer Gruppe Admin ist.
*/
public static boolean checkIfAdmin(Group group, User user) {
if (checkIfMember(group, user)) {
return group.getRoles().get(user.getId()) == ADMIN;
public static boolean checkIfAdmin(Group group, String userid) {
if (checkIfMember(group, userid)) {
return group.isAdmin(userid);
}
return false;
}
public static boolean checkIfLastAdmin(User user, Group group) {
return checkIfAdmin(group, user) && group.getRoles().values().stream()
.filter(role -> role == ADMIN)
.count() == 1;
public static boolean checkIfLastAdmin(Group group, String userid) {
return checkIfAdmin(group, userid) && group.getAdmins().size() == 1;
}
// ######################################## THROW ############################################
public static void throwIfMember(Group group, User user) {
if (checkIfMember(group, user)) {
log.error("Benutzer {} ist schon in Gruppe {}", user, group);
throw new UserAlreadyExistsException(user.toString());
public static void throwIfMember(Group group, String userid) throws UserAlreadyExistsException {
if (checkIfMember(group, userid)) {
log.error("Benutzer {} ist schon in Gruppe {}", userid, group);
throw new UserAlreadyExistsException(userid);
}
}
public static void throwIfNoMember(Group group, User user) {
if (!checkIfMember(group, user)) {
log.error("Benutzer {} ist nicht in Gruppe {}!", user, group);
throw new UserNotFoundException(user.toString());
public static void throwIfNoMember(Group group, String userid) throws UserNotFoundException {
if (!checkIfMember(group, userid)) {
log.error("Benutzer {} ist nicht in Gruppe {}!", userid, group);
throw new UserNotFoundException(userid);
}
}
public static void throwIfNoAdmin(Group group, User user) {
if (!checkIfAdmin(group, user)) {
log.error("User {} ist kein Admin in Gruppe {}!", user, group);
throw new NoAccessException(group.toString());
public static void throwIfNoAdmin(Group group, String userid) throws NoAccessException {
if (!checkIfAdmin(group, userid)) {
log.error("User {} ist kein Admin in Gruppe {}!", userid, group);
throw new NoAccessException(group.getId().toString());
}
}
/**
* Schmeißt keine Exception, wenn der User der letzte User ist.
*/
public static void throwIfLastAdmin(User user, Group group) {
if (!checkIfLastMember(user, group) && checkIfLastAdmin(user, group)) {
throw new NoAdminAfterActionException("Du bist letzter Admin!");
public static void throwIfLastAdmin(Group group, String userid) throws LastAdminException {
if (!checkIfLastMember(group, userid) && checkIfLastAdmin(group, userid)) {
throw new LastAdminException("Du bist letzter Admin!");
}
}
public static void throwIfGroupFull(Group group) {
public static void throwIfGroupFull(Group group) throws GroupFullException {
if (checkIfGroupFull(group)) {
log.error("Die Gruppe {} ist voll!", group);
throw new GroupFullException(group.toString());
throw new GroupFullException(group.getId().toString());
}
}
// ##################################### VALIDATE FIELDS #####################################
public static void validateCreateForm(KeycloakAuthenticationToken token, CreateForm form) {
if (!token.getAccount().getRoles().contains("orga")
&& form.getType() == GroupType.LECTURE) {
throw new BadParameterException("Eine Veranstaltung kann nur von ORGA erstellt werden.");
public static void validateCreateForm(KeycloakAuthenticationToken token, Type type) {
if (!token.getAccount().getRoles().contains("orga") && type == Type.LECTURE) {
throw new BadArgumentException("Nur Orga kann Veranstaltungen erstellen.");
}
}
}

View File

@ -0,0 +1,313 @@
package mops.gruppen2.domain.model.group;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.BadArgumentException;
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.UserNotFoundException;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.group.wrapper.Body;
import mops.gruppen2.domain.model.group.wrapper.Description;
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 javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Repräsentiert den aggregierten Zustand einer Gruppe.
*
* <p>
* Muss beim Start gesetzt werden: groupid, meta
*/
@Log4j2
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Group {
// Metainformationen
@EqualsAndHashCode.Include
private UUID groupid;
@Getter
private Type type = Type.PRIVATE;
private Parent parent = Parent.EMPTY();
private Limit limit = Limit.DEFAULT(); // Add initial user
private Link link = Link.RANDOM();
private GroupMeta meta = GroupMeta.EMPTY();
private GroupOptions options = GroupOptions.DEFAULT();
//@ToString.Exclude
//private LocalDateTime age;
// Inhalt
private Title title = Title.EMPTY();
private Description description = Description.EMPTY();
private Body body;
// Integrationen
// Teilnehmer
private Map<String, Membership> memberships = new HashMap<>();
// ####################################### Members ###########################################
public List<User> getMembers() {
return SortHelper.sortByMemberRole(new ArrayList<>(memberships.values())).stream()
.map(Membership::getUser)
.collect(Collectors.toList());
}
public List<User> getRegulars() {
return memberships.values().stream()
.map(Membership::getUser)
.filter(member -> isRegular(member.getId()))
.collect(Collectors.toList());
}
public List<User> getAdmins() {
return memberships.values().stream()
.map(Membership::getUser)
.filter(member -> isAdmin(member.getId()))
.collect(Collectors.toList());
}
public Role getRole(String userid) {
return memberships.get(userid).getRole();
}
public void addMember(String target, User user) throws UserAlreadyExistsException, GroupFullException {
ValidationHelper.throwIfMember(this, target);
ValidationHelper.throwIfGroupFull(this);
memberships.put(target, new Membership(user, Role.REGULAR));
}
public void kickMember(String target) throws UserNotFoundException, LastAdminException {
ValidationHelper.throwIfNoMember(this, target);
ValidationHelper.throwIfLastAdmin(this, target);
memberships.remove(target);
}
public void memberPutRole(String target, Role role) throws UserNotFoundException, LastAdminException {
ValidationHelper.throwIfNoMember(this, target);
if (role == Role.REGULAR) {
ValidationHelper.throwIfLastAdmin(this, target);
}
memberships.put(target, memberships.get(target).setRole(role));
}
public boolean memberHasRole(String target, Role role) {
ValidationHelper.throwIfNoMember(this, target);
return memberships.get(target).getRole() == role;
}
public boolean isMember(String target) {
return memberships.containsKey(target);
}
public boolean isAdmin(String target) throws UserNotFoundException {
ValidationHelper.throwIfNoMember(this, target);
return memberships.get(target).getRole() == Role.ADMIN;
}
public boolean isRegular(String target) throws UserNotFoundException {
ValidationHelper.throwIfNoMember(this, target);
return memberships.get(target).getRole() == Role.REGULAR;
}
// ######################################### Getters #########################################
public UUID getId() {
return groupid;
}
public UUID getParent() {
return parent.getValue();
}
public long getLimit() {
return limit.getValue();
}
public String getTitle() {
return title.toString();
}
public String getDescription() {
return description.getValue();
}
public String getLink() {
return link.getValue();
}
public String creator() {
return meta.getCreator();
}
public long version() {
return meta.getVersion();
}
public LocalDateTime creationDate() {
return meta.getCreationDate();
}
public int size() {
return memberships.size();
}
public boolean isFull() {
return size() >= limit.getValue();
}
public boolean isEmpty() {
return size() == 0;
}
public boolean exists() {
return groupid != null && !CommonHelper.uuidIsEmpty(groupid);
}
public boolean isPublic() {
return type == Type.PUBLIC;
}
public boolean isPrivate() {
return type == Type.PRIVATE;
}
public boolean isLecture() {
return type == Type.LECTURE;
}
// ######################################## Setters ##########################################
public void setId(UUID groupid) throws BadArgumentException {
if (this.groupid != null) {
throw new BadArgumentException("GruppenId bereits gesetzt.");
}
this.groupid = groupid;
}
public void setType(String exec, Type type) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, exec);
this.type = type;
}
public void setTitle(String exec, @Valid Title title) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, exec);
this.title = title;
}
public void setDescription(String exec, @Valid Description description) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, exec);
this.description = description;
}
public void setLimit(String exec, @Valid Limit limit) throws NoAccessException, BadArgumentException {
ValidationHelper.throwIfNoAdmin(this, exec);
if (size() > limit.getValue()) {
throw new BadArgumentException("Das Userlimit ist zu klein für die Gruppe.");
}
this.limit = limit;
}
public void setParent(String exec, @Valid Parent parent) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, exec);
this.parent = parent;
}
public void setLink(String exec, @Valid Link link) throws NoAccessException {
ValidationHelper.throwIfNoAdmin(this, exec);
this.link = link;
}
public void update(long version) throws IdMismatchException {
meta = meta.setVersion(version);
}
public void setCreator(String target) throws BadArgumentException {
meta = meta.setCreator(target);
}
public void setCreationDate(LocalDateTime date) throws BadArgumentException {
meta = meta.setCreationDate(date);
}
// ######################################### Util ############################################
public void destroy(String userid) throws NoAccessException {
if (!isEmpty()) {
ValidationHelper.throwIfNoAdmin(this, userid);
}
groupid = null;
type = null;
parent = null;
limit = null;
link = null;
meta = null;
options = null;
title = null;
description = null;
body = null;
memberships = null;
}
public String format() {
return title + " " + description;
}
@Override
public String toString() {
return "group("
+ (groupid == null ? "groupid: null" : groupid.toString())
+ ", "
+ (parent == null ? "parent: null" : parent.toString())
+ ", "
+ (meta == null ? "meta: null" : meta.toString())
+ ")";
}
}

View File

@ -0,0 +1,47 @@
package mops.gruppen2.domain.model.group;
import lombok.ToString;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.exception.IdMismatchException;
import java.time.LocalDateTime;
@Log4j2
@Value
@ToString
public class GroupMeta {
long version;
String creator;
LocalDateTime creationDate;
public GroupMeta setVersion(long version) throws IdMismatchException {
if (this.version >= version) {
throw new IdMismatchException("Die Gruppe ist bereits auf einem neueren Stand.");
}
return new GroupMeta(version, creator, creationDate);
}
public GroupMeta setCreator(String userid) throws BadArgumentException {
if (creator != null) {
throw new BadArgumentException("Gruppe hat schon einen Ersteller.");
}
return new GroupMeta(version, userid, creationDate);
}
public GroupMeta setCreationDate(LocalDateTime date) throws BadArgumentException {
if (creationDate != null) {
throw new BadArgumentException("Gruppe hat schon ein Erstellungsdatum.");
}
return new GroupMeta(version, creator, date);
}
public static GroupMeta EMPTY() {
return new GroupMeta(0, null, null);
}
}

View File

@ -0,0 +1,36 @@
package mops.gruppen2.domain.model.group;
import lombok.Value;
//TODO: doooooodododo
@Value
class GroupOptions {
// Gruppe
boolean showClearname;
boolean hasBody;
boolean isLeavable;
boolean hasLink;
String customLogo;
String customBackground;
String customTitle;
// Integrations
boolean hasMaterialIntegration;
boolean hasTermineIntegration;
boolean hasPortfolioIntegration;
static GroupOptions DEFAULT() {
return new GroupOptions(true,
false,
true,
false,
null,
null,
null,
true,
true,
true);
}
}

View File

@ -0,0 +1,23 @@
package mops.gruppen2.domain.model.group;
import lombok.EqualsAndHashCode;
import lombok.Value;
@Value
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Membership {
User user;
Role role;
// LocalDateTime age;
@Override
public String toString() {
return user.format() + ": " + role;
}
public Membership setRole(Role role) {
return new Membership(user, role);
}
}

View File

@ -0,0 +1,10 @@
package mops.gruppen2.domain.model.group;
public enum Role {
ADMIN,
REGULAR;
public Role toggle() {
return this == ADMIN ? REGULAR : ADMIN;
}
}

View File

@ -0,0 +1,45 @@
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 List<Group> sortByGroupType(List<Group> groups) {
groups.sort((Group g1, Group g2) -> {
if (g1.getType() == Type.LECTURE) {
return -1;
}
if (g2.getType() == Type.LECTURE) {
return 1;
}
return 0;
});
return groups;
}
public static List<Membership> sortByMemberRole(List<Membership> 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;
}
}

View File

@ -0,0 +1,7 @@
package mops.gruppen2.domain.model.group;
public enum Type {
PUBLIC,
PRIVATE,
LECTURE
}

View File

@ -1,32 +1,37 @@
package mops.gruppen2.domain;
package mops.gruppen2.domain.model.group;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@Getter
@Log4j2
@Value
@AllArgsConstructor
@NoArgsConstructor // Für Jackson: CSV-Import
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString
public class User {
@EqualsAndHashCode.Include
private String id;
@Getter(AccessLevel.NONE)
@JsonProperty("id")
String userid;
private String givenname;
@ToString.Exclude
private String familyname;
@ToString.Exclude
private String email;
@JsonProperty("givenname")
String givenname;
@JsonProperty("familyname")
String familyname;
@JsonProperty("email")
String email;
public User(KeycloakAuthenticationToken token) {
KeycloakPrincipal principal = (KeycloakPrincipal) token.getPrincipal();
id = principal.getName();
userid = principal.getName();
givenname = principal.getKeycloakSecurityContext().getIdToken().getGivenName();
familyname = principal.getKeycloakSecurityContext().getIdToken().getFamilyName();
email = principal.getKeycloakSecurityContext().getIdToken().getEmail();
@ -35,12 +40,20 @@ public class User {
/**
* User identifizieren sich über die Id, mehr wird also manchmal nicht benötigt.
*
* @param userId Die User Id
* @param userid Die User Id
*/
public User(String userId) {
id = userId;
public User(String userid) {
this.userid = userid;
givenname = "";
familyname = "";
email = "";
}
public String getId() {
return userid;
}
public String format() {
return givenname + " " + familyname;
}
}

View File

@ -0,0 +1,8 @@
package mops.gruppen2.domain.model.group.wrapper;
import lombok.Value;
//TODO: do it
@Value
public class Body {
}

View File

@ -0,0 +1,27 @@
package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Value
public class Description {
@NotNull
@Size(min = 4, max = 512)
@JsonProperty("value")
String value;
@Override
public String toString() {
return value;
}
@JsonIgnore
public static Description EMPTY() {
return new Description("EMPTY");
}
}

View File

@ -0,0 +1,27 @@
package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Value
public class Limit {
@NotNull
@Min(1)
@Max(999_999)
@JsonProperty("value")
long value;
public static Limit DEFAULT() {
return new Limit(1);
}
@Override
public String toString() {
return String.valueOf(value);
}
}

View File

@ -0,0 +1,21 @@
package mops.gruppen2.domain.model.group.wrapper;
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;
public static Link RANDOM() {
return new Link(UUID.randomUUID().toString());
}
}

View File

@ -0,0 +1,36 @@
package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.ToString;
import lombok.Value;
import mops.gruppen2.domain.helper.CommonHelper;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.beans.ConstructorProperties;
import java.util.UUID;
@Value
@ToString
public class Parent {
@NotNull
@JsonProperty("id")
UUID value;
@ConstructorProperties("id")
public Parent(@NotBlank @Size(min = 36, max = 36) String parentid) {
value = UUID.fromString(parentid);
}
public static Parent EMPTY() {
return new Parent("00000000-0000-0000-0000-000000000000");
}
@JsonIgnore
public boolean isEmpty() {
return CommonHelper.uuidIsEmpty(value);
}
}

View File

@ -0,0 +1,27 @@
package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Value
public class Title {
@NotNull
@Size(min = 4, max = 128)
@JsonProperty("value")
String value;
@Override
public String toString() {
return value;
}
@JsonIgnore
public static Title EMPTY() {
return new Title("EMPTY");
}
}

View File

@ -1,14 +1,19 @@
package mops.gruppen2.domain.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.event.AddUserEvent;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.EventType;
import mops.gruppen2.domain.event.SetTypeEvent;
import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.helper.JsonHelper;
import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.stereotype.Service;
@ -20,16 +25,21 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
import static mops.gruppen2.domain.event.EventType.CREATEGROUP;
import static mops.gruppen2.domain.event.EventType.DESTROYGROUP;
import static mops.gruppen2.domain.event.EventType.SETLINK;
import static mops.gruppen2.domain.event.EventType.SETTYPE;
import static mops.gruppen2.domain.helper.CommonHelper.eventTypesToString;
import static mops.gruppen2.domain.helper.CommonHelper.uuidsToString;
@Log4j2
@RequiredArgsConstructor
@Service
@TraceMethodCalls
public class EventStoreService {
private final EventRepository eventStore;
public EventStoreService(EventRepository eventStore) {
this.eventStore = eventStore;
}
//########################################### SAVE ###########################################
@ -67,7 +77,7 @@ public class EventStoreService {
//########################################### DTOs ###########################################
static List<EventDTO> getDTOsFromEvents(List<Event> events) {
private static List<EventDTO> getDTOsFromEvents(List<Event> events) {
return events.stream()
.map(EventStoreService::getDTOFromEvent)
.collect(Collectors.toList());
@ -80,13 +90,15 @@ public class EventStoreService {
*
* @return EventDTO (Neues DTO)
*/
static EventDTO getDTOFromEvent(Event event) {
private static EventDTO getDTOFromEvent(Event event) {
try {
String payload = JsonHelper.serializeEvent(event);
return new EventDTO(null,
event.getGroupId().toString(),
event.getUserId(),
getEventType(event),
event.getGroupid().toString(),
event.getVersion(),
event.getExec(),
event.getTarget(),
event.type(),
payload);
} catch (JsonProcessingException e) {
log.error("Event ({}) konnte nicht serialisiert werden!", event, e);
@ -116,19 +128,6 @@ public class EventStoreService {
}
}
/**
* Gibt den Eventtyp als String wieder.
*
* @param event Event dessen Typ abgefragt werden soll
*
* @return Der Name des Typs des Events
*/
private static String getEventType(Event event) {
int lastDot = event.getClass().getName().lastIndexOf('.');
return event.getClass().getName().substring(lastDot + 1);
}
// ######################################## QUERIES ##########################################
@ -161,13 +160,12 @@ public class EventStoreService {
*
* @return Liste von neuen und alten Events
*/
List<Event> findChangedGroupEvents(long status) {
List<UUID> findChangedGroups(long status) {
List<String> changedGroupIds = eventStore.findGroupIdsWhereEventIdGreaterThanStatus(status);
List<EventDTO> groupEventDTOS = eventStore.findEventDTOsByGroup(changedGroupIds);
log.debug("Seit Event {} haben sich {} Gruppen geändert!", status, changedGroupIds.size());
return getEventsFromDTOs(groupEventDTOS);
return CommonHelper.stringsToUUID(changedGroupIds);
}
/**
@ -176,35 +174,80 @@ public class EventStoreService {
* @return GruppenIds (UUID) als Liste
*/
List<UUID> findExistingGroupIds() {
List<Event> createEvents = findLatestEventsFromGroupsByType("CreateGroupEvent",
"DeleteGroupEvent");
List<Event> createEvents = findLatestEventsFromGroupsByType(CREATEGROUP,
DESTROYGROUP);
return createEvents.stream()
.filter(event -> event instanceof CreateGroupEvent)
.map(Event::getGroupId)
.map(Event::getGroupid)
.collect(Collectors.toList());
}
public List<UUID> findPublicGroupIds() {
List<UUID> groups = findExistingGroupIds();
List<Event> typeEvents = findLatestEventsFromGroupsByType(SETTYPE);
typeEvents.removeIf(event -> ((SetTypeEvent) event).getType() == Type.PRIVATE);
typeEvents.removeIf(event -> !groups.contains(event.getGroupid()));
return typeEvents.stream()
.map(Event::getGroupid)
.collect(Collectors.toList());
}
public List<UUID> findLectureGroupIds() {
List<UUID> groups = findExistingGroupIds();
List<Event> typeEvents = findLatestEventsFromGroupsByType(SETTYPE);
typeEvents.removeIf(event -> ((SetTypeEvent) event).getType() != Type.LECTURE);
typeEvents.removeIf(event -> !groups.contains(event.getGroupid()));
return typeEvents.stream()
.map(Event::getGroupid)
.collect(Collectors.toList());
}
/**
* Liefert Gruppen-Ids von existierenden (ungelöschten) Gruppen, in welchen der User teilnimmt.
*
* <p>
* Vorgang:
* Finde für jede Gruppe das letzte Add- oder Kick-Event, welches den User betrifft
* Finde für jede Gruppe das letzte Destroy-Event
* Entferne alle alle Events von Gruppen, welche ein Destroy-Event haben
* Gebe die Gruppen zurück, auf welche sich die Add-Events beziehen
*
* @return GruppenIds (UUID) als Liste
*/
public List<UUID> findExistingUserGroups(User user) {
List<Event> userEvents = findLatestEventsFromGroupsByUser(user);
List<UUID> deletedIds = findLatestEventsFromGroupsByType("DeleteGroupEvent")
public List<UUID> findExistingUserGroups(String userid) {
List<Event> userEvents = findLatestEventsFromGroupsByUser(userid);
List<UUID> deletedIds = findLatestEventsFromGroupsByType(DESTROYGROUP)
.stream()
.map(Event::getGroupId)
.map(Event::getGroupid)
.collect(Collectors.toList());
userEvents.removeIf(event -> deletedIds.contains(event.getGroupId()));
userEvents.removeIf(event -> deletedIds.contains(event.getGroupid()));
return userEvents.stream()
.filter(event -> event instanceof AddUserEvent)
.map(Event::getGroupId)
.filter(event -> event instanceof AddMemberEvent)
.map(Event::getGroupid)
.collect(Collectors.toList());
}
public UUID findGroupByLink(String link) {
List<Event> groupEvents = findEventsByType(eventTypesToString(SETLINK));
if (groupEvents.size() > 1) {
throw new InvalidInviteException("Es existieren mehrere Gruppen mit demselben Link.");
}
if (groupEvents.isEmpty()) {
throw new InvalidInviteException("Link nicht gefunden.");
}
return groupEvents.get(0).getGroupid();
}
// #################################### SIMPLE QUERIES #######################################
@ -219,7 +262,7 @@ public class EventStoreService {
return eventStore.findMaxEventId();
} catch (NullPointerException e) {
log.debug("Keine Events vorhanden!");
return 0;
return 1;
}
}
@ -232,19 +275,19 @@ public class EventStoreService {
}
List<Event> findEventsByGroupAndType(List<UUID> groupIds, String... types) {
return getEventsFromDTOs(eventStore.findEventDTOsByGroupAndType(Arrays.asList(types),
IdHelper.uuidsToString(groupIds)));
return getEventsFromDTOs(eventStore.findEventDTOsByGroupAndType(uuidsToString(groupIds),
Arrays.asList(types)));
}
/**
* Sucht zu jeder Gruppe das letzte Add- oder DeleteUserEvent heraus, welches den übergebenen User betrifft.
*
* @param user User, zu welchem die Events gesucht werden
* @param userid User, zu welchem die Events gesucht werden
*
* @return Eine Liste von einem Add- oder DeleteUserEvent pro Gruppe
*/
private List<Event> findLatestEventsFromGroupsByUser(User user) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByUser(user.getId()));
private List<Event> findLatestEventsFromGroupsByUser(String userid) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupTarget(userid));
}
@ -255,7 +298,7 @@ public class EventStoreService {
*
* @return Eine Liste von einem Event pro Gruppe
*/
private List<Event> findLatestEventsFromGroupsByType(String... types) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(Arrays.asList(types)));
private List<Event> findLatestEventsFromGroupsByType(EventType... types) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(Arrays.asList(eventTypesToString(types))));
}
}

View File

@ -1,23 +1,34 @@
package mops.gruppen2.domain.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.event.AddUserEvent;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.DeleteGroupEvent;
import mops.gruppen2.domain.event.DeleteUserEvent;
import mops.gruppen2.domain.event.DestroyGroupEvent;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.UpdateGroupDescriptionEvent;
import mops.gruppen2.domain.event.UpdateGroupTitleEvent;
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.SetParentEvent;
import mops.gruppen2.domain.event.SetTitleEvent;
import mops.gruppen2.domain.event.SetTypeEvent;
import mops.gruppen2.domain.event.UpdateRoleEvent;
import mops.gruppen2.domain.event.UpdateUserLimitEvent;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.helper.ValidationHelper;
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.Parent;
import mops.gruppen2.domain.model.group.wrapper.Title;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
@ -25,51 +36,48 @@ import java.util.UUID;
* Behandelt Aufgaben, welche sich auf eine Gruppe beziehen.
* Es werden übergebene Gruppen bearbeitet und dementsprechend Events erzeugt und gespeichert.
*/
@Service
@Log4j2
@TraceMethodCalls
@RequiredArgsConstructor
@Service
public class GroupService {
private final EventStoreService eventStoreService;
private final InviteService inviteService;
public GroupService(EventStoreService eventStoreService, InviteService inviteService) {
this.eventStoreService = eventStoreService;
this.inviteService = inviteService;
}
// ################################# GRUPPE ERSTELLEN ########################################
/**
* Erzeugt eine neue Gruppe und erzeugt nötige Events für die Initiale Setzung der Attribute.
*
* @param user Keycloak-Account
* @param title Gruppentitel
* @param description Gruppenbeschreibung
*/
public Group createGroup(User user,
String title,
String description,
GroupType groupType,
long userLimit,
UUID parent) {
public Group createGroup(String exec) {
return createGroup(UUID.randomUUID(), exec, LocalDateTime.now());
}
// Regeln:
// isPrivate -> !isLecture
// isLecture -> !isPrivate
Group group = createGroup(user, parent, groupType);
public void initGroupMembers(Group group,
String exec,
String target,
User user,
Limit limit) {
// Die Reihenfolge ist wichtig, da der ausführende User Admin sein muss
addUser(user, group);
updateRole(user, group, Role.ADMIN);
updateTitle(user, group, title);
updateDescription(user, group, description);
updateUserLimit(user, group, userLimit);
addMember(group, exec, target, user);
updateRole(group, exec, target, Role.ADMIN);
setLimit(group, exec, limit);
}
inviteService.createLink(group);
public void initGroupMeta(Group group,
String exec,
Type type,
Parent parent) {
return group;
setType(group, exec, type);
setParent(group, exec, parent);
}
public void initGroupText(Group group,
String exec,
Title title,
Description description) {
setTitle(group, exec, title);
setDescription(group, exec, description);
}
@ -84,12 +92,12 @@ public class GroupService {
*
* @param newUsers Userliste
* @param group Gruppe
* @param user Ausführender User
* @param exec Ausführender User
*/
public void addUsersToGroup(List<User> newUsers, Group group, User user) {
updateUserLimit(user, group, getAdjustedUserLimit(newUsers, group));
public void addUsersToGroup(Group group, String exec, List<User> newUsers) {
setLimit(group, exec, getAdjustedUserLimit(newUsers, group));
newUsers.forEach(newUser -> addUserSilent(newUser, group));
newUsers.forEach(newUser -> addUserSilent(group, exec, newUser.getId(), newUser));
}
/**
@ -102,25 +110,21 @@ public class GroupService {
*
* @return Das neue Teilnehmermaximum
*/
private static long getAdjustedUserLimit(List<User> newUsers, Group group) {
return Math.max((long) group.getMembers().size() + newUsers.size(), group.getUserLimit());
private static Limit getAdjustedUserLimit(List<User> newUsers, Group group) {
return new Limit(Math.max((long) group.size() + newUsers.size(), group.getLimit()));
}
/**
* Wechselt die Rolle eines Teilnehmers von Admin zu Member oder andersherum.
* Überprüft, ob der User Mitglied ist und ob er der letzte Admin ist.
*
* @param user Teilnehmer, welcher geändert wird
* @param group Gruppe, in welcher sih der Teilnehmer befindet
* @param target Teilnehmer, welcher geändert wird
* @param group Gruppe, in welcher sih der Teilnehmer befindet
*
* @throws EventException Falls der User nicht gefunden wird
*/
public void toggleMemberRole(User user, Group group) throws EventException {
ValidationHelper.throwIfNoMember(group, user);
ValidationHelper.throwIfLastAdmin(user, group);
Role role = group.getRoles().get(user.getId());
updateRole(user, group, role.toggle());
public void toggleMemberRole(Group group, String exec, String target) {
updateRole(group, exec, target, group.getRole(target).toggle());
}
@ -131,59 +135,44 @@ public class GroupService {
/**
* Erzeugt eine Gruppe, speichert diese und gibt diese zurück.
*/
private Group createGroup(User user, UUID parent, GroupType groupType) {
Event event = new CreateGroupEvent(UUID.randomUUID(),
user.getId(),
parent,
groupType);
private Group createGroup(UUID groupid, String exec, LocalDateTime date) {
Event event = new CreateGroupEvent(groupid,
exec,
date);
Group group = new Group();
event.apply(group);
eventStoreService.saveEvent(event);
applyAndSave(group, event);
return group;
}
/**
* Erzeugt, speichert ein AddUserEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer schon Mitglied ist und ob Gruppe voll ist.
*/
public void addUser(User user, Group group) {
ValidationHelper.throwIfMember(group, user);
ValidationHelper.throwIfGroupFull(group);
Event event = new AddUserEvent(group, user);
event.apply(group);
eventStoreService.saveEvent(event);
}
/**
* Dasselbe wie addUser(), aber exceptions werden abgefangen und nicht geworfen.
*/
private void addUserSilent(User user, Group group) {
private void addUserSilent(Group group, String exec, String target, User user) {
try {
addUser(user, group);
addMember(group, exec, target, user);
} catch (Exception e) {
log.debug("Doppelter User {} wurde nicht zu Gruppe {} hinzugefügt!", user, group);
}
}
/**
* Erzeugt, speichert ein AddUserEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer schon Mitglied ist und ob Gruppe voll ist.
*/
public void addMember(Group group, String exec, String target, User user) {
applyAndSave(group, new AddMemberEvent(group.getId(), exec, target, user));
}
/**
* Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist.
*/
public void deleteUser(User user, Group group) throws EventException {
ValidationHelper.throwIfNoMember(group, user);
ValidationHelper.throwIfLastAdmin(user, group);
public void kickMember(Group group, String exec, String target) {
applyAndSave(group, new KickMemberEvent(group.getId(), exec, target));
if (ValidationHelper.checkIfGroupEmpty(group)) {
deleteGroup(user, group);
} else {
Event event = new DeleteUserEvent(group, user);
event.apply(group);
eventStoreService.saveEvent(event);
deleteGroup(group, exec);
}
}
@ -191,14 +180,12 @@ public class GroupService {
* Erzeugt, speichert ein DeleteGroupEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Admin ist.
*/
public void deleteGroup(User user, Group group) {
ValidationHelper.throwIfNoAdmin(group, user);
public void deleteGroup(Group group, String exec) {
if (!group.exists()) {
return;
}
Event event = new DeleteGroupEvent(group, user);
event.apply(group);
inviteService.destroyLink(group);
eventStoreService.saveEvent(event);
applyAndSave(group, new DestroyGroupEvent(group.getId(), exec));
}
/**
@ -206,17 +193,12 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist und ob der Titel valide ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
public void updateTitle(User user, Group group, String title) {
ValidationHelper.throwIfNoAdmin(group, user);
if (title.trim().equals(group.getTitle())) {
public void setTitle(Group group, String exec, Title title) {
if (group.getTitle().equals(title.getValue())) {
return;
}
Event event = new UpdateGroupTitleEvent(group, user, title.trim());
event.apply(group);
eventStoreService.saveEvent(event);
applyAndSave(group, new SetTitleEvent(group.getId(), exec, title));
}
/**
@ -224,17 +206,12 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist und ob die Beschreibung valide ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
public void updateDescription(User user, Group group, String description) {
ValidationHelper.throwIfNoAdmin(group, user);
if (description.trim().equals(group.getDescription())) {
public void setDescription(Group group, String exec, Description description) {
if (group.getDescription().equals(description.getValue())) {
return;
}
Event event = new UpdateGroupDescriptionEvent(group, user, description.trim());
event.apply(group);
eventStoreService.saveEvent(event);
applyAndSave(group, new SetDescriptionEvent(group.getId(), exec, description));
}
/**
@ -242,17 +219,12 @@ public class GroupService {
* Prüft, ob der Nutzer Mitglied ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
private void updateRole(User user, Group group, Role role) {
ValidationHelper.throwIfNoMember(group, user);
if (role == group.getRoles().get(user.getId())) {
private void updateRole(Group group, String exec, String target, Role role) {
if (group.memberHasRole(target, role)) {
return;
}
Event event = new UpdateRoleEvent(group, user, role);
event.apply(group);
eventStoreService.saveEvent(event);
applyAndSave(group, new UpdateRoleEvent(group.getId(), exec, target, role));
}
/**
@ -260,20 +232,40 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist und ob das Limit valide ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
public void updateUserLimit(User user, Group group, long userLimit) {
ValidationHelper.throwIfNoAdmin(group, user);
if (userLimit == group.getUserLimit()) {
public void setLimit(Group group, String exec, Limit userLimit) {
if (userLimit.getValue() == group.getLimit()) {
return;
}
Event event;
if (userLimit < group.getMembers().size()) {
event = new UpdateUserLimitEvent(group, user, group.getMembers().size());
} else {
event = new UpdateUserLimitEvent(group, user, userLimit);
applyAndSave(group, new SetLimitEvent(group.getId(), exec, userLimit));
}
public void setParent(Group group, String exec, Parent parent) {
if (parent.getValue() == group.getParent()) {
return;
}
applyAndSave(group, new SetParentEvent(group.getId(), exec, parent));
}
public void setLink(Group group, String exec, Link link) {
if (group.getLink().equals(link.getValue())) {
return;
}
applyAndSave(group, new SetInviteLinkEvent(group.getId(), exec, link));
}
private void setType(Group group, String exec, Type type) {
if (group.getType() == type) {
return;
}
applyAndSave(group, new SetTypeEvent(group.getId(), exec, type));
}
private void applyAndSave(Group group, Event event) throws EventException {
event.init(group.version() + 1);
event.apply(group);
eventStoreService.saveEvent(event);

View File

@ -1,54 +0,0 @@
package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.exception.NoInviteExistException;
import mops.gruppen2.persistance.InviteRepository;
import mops.gruppen2.persistance.dto.InviteLinkDTO;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
@Log4j2
public class InviteService {
private final InviteRepository inviteRepository;
public InviteService(InviteRepository inviteRepository) {
this.inviteRepository = inviteRepository;
}
void createLink(Group group) {
inviteRepository.save(new InviteLinkDTO(null,
group.getId().toString(),
UUID.randomUUID().toString()));
log.debug("Link wurde erzeugt! (Gruppe: {})", group.getId());
}
void destroyLink(Group group) {
inviteRepository.deleteLinkOfGroup(group.getId().toString());
log.debug("Link wurde zerstört! (Gruppe: {})", group.getId());
}
public UUID getGroupIdFromLink(String link) {
try {
return UUID.fromString(inviteRepository.findGroupIdByLink(link));
} catch (Exception e) {
log.error("Gruppe zu Link ({}) konnte nicht gefunden werden!", link, e);
throw new InvalidInviteException(link);
}
}
public String getLinkByGroup(Group group) {
try {
return inviteRepository.findLinkByGroupId(group.getId().toString());
} catch (Exception e) {
log.error("Link zu Gruppe ({}) konnte nicht gefunden werden!", group.getId(), e);
throw new NoInviteExistException(group.getId().toString());
}
}
}

View File

@ -1,58 +1,37 @@
package mops.gruppen2.domain.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.model.group.Group;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Liefert verschiedene Projektionen auf Gruppen.
* Benötigt ausschließlich den EventStoreService.
*/
@Service
@Log4j2
@RequiredArgsConstructor
@Service
public class ProjectionService {
private final EventStoreService eventStoreService;
public ProjectionService(EventStoreService eventStoreService) {
this.eventStoreService = eventStoreService;
}
// ################################## STATISCHE PROJEKTIONEN #################################
/**
* Konstruiert Gruppen aus einer Liste von Events.
*
* @param events Liste an Events
*
* @return Liste an Projizierten Gruppen
*
* @throws EventException Projektionsfehler
*/
static List<Group> projectGroups(List<Event> events) throws EventException {
Map<UUID, Group> groupMap = new HashMap<>();
events.forEach(event -> event.apply(getOrCreateGroup(groupMap, event.getGroupId())));
return new ArrayList<>(groupMap.values());
}
/**
* Projiziert Events, geht aber davon aus, dass alle zu derselben Gruppe gehören.
*
@ -62,7 +41,7 @@ public class ProjectionService {
*
* @throws EventException Projektionsfehler, z.B. falls Events von verschiedenen Gruppen übergeben werden
*/
static Group projectSingleGroup(List<Event> events) throws EventException {
private static Group projectGroupByEvents(List<Event> events) throws EventException {
if (events.isEmpty()) {
throw new GroupNotFoundException(ProjectionService.class.toString());
}
@ -74,6 +53,23 @@ public class ProjectionService {
return group;
}
/**
* Konstruiert Gruppen aus einer Liste von Events.
*
* @param events Liste an Events
*
* @return Liste an Projizierten Gruppen
*
* @throws EventException Projektionsfehler
*/
public static List<Group> projectGroupsByEvents(List<Event> events) throws EventException {
Map<UUID, Group> groupMap = new HashMap<>();
events.forEach(event -> event.apply(getOrCreateGroup(groupMap, event.getGroupid())));
return new ArrayList<>(groupMap.values());
}
/**
* Gibt die Gruppe mit der richtigen Id aus der übergebenen Map wieder, existiert diese nicht
* wird die Gruppe erstellt und der Map hizugefügt.
@ -95,6 +91,41 @@ public class ProjectionService {
// ############################### PROJEKTIONEN MIT DATENBANK ################################
/**
* Gibt die Gruppe zurück, die zu der übergebenen Id passt.
* Enthält alle verfügbaren Informationen, also auch User (langsam).
* Gibt eine leere Gruppe zurück, falls die Id leer ist.
*
* @param groupId Die Id der gesuchten Gruppe
*
* @return Die gesuchte Gruppe
*
* @throws GroupNotFoundException Wenn die Gruppe nicht gefunden wird
*/
public Group projectGroupById(UUID groupId) throws GroupNotFoundException {
try {
List<Event> events = eventStoreService.findGroupEvents(groupId);
return projectGroupByEvents(events);
} catch (Exception e) {
log.error("Gruppe {} wurde nicht gefunden!", groupId.toString(), e);
throw new GroupNotFoundException(groupId + ": " + ProjectionService.class);
}
}
public Group projectParent(UUID parent) {
if (CommonHelper.uuidIsEmpty(parent)) {
return new Group();
}
return projectGroupById(parent);
}
public List<Group> projectGroupsByIds(List<UUID> groupids) {
List<Event> events = eventStoreService.findGroupEvents(groupids);
return projectGroupsByEvents(events);
}
/**
* Projiziert Gruppen, welche sich seit einer übergebenen eventId geändert haben.
* Die Gruppen werden dabei vollständig konstruiert.
@ -103,10 +134,10 @@ public class ProjectionService {
*
* @return Liste an Gruppen
*/
public List<Group> projectNewGroups(long status) {
List<Event> events = eventStoreService.findChangedGroupEvents(status);
public List<Group> projectChangedGroups(long status) {
List<UUID> changedids = eventStoreService.findChangedGroups(status);
return projectGroups(events);
return projectGroupsByIds(changedids);
}
/**
@ -119,20 +150,14 @@ public class ProjectionService {
* @throws EventException Projektionsfehler
*/
@Cacheable("groups")
//TODO: remove userID param
public List<Group> projectPublicGroups() throws EventException {
List<UUID> groupIds = eventStoreService.findExistingGroupIds();
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds,
"CreateGroupEvent",
"UpdateGroupDescriptionEvent",
"UpdateGroupTitleEvent",
"UpdateUserMaxEvent");
List<UUID> groupIds = eventStoreService.findPublicGroupIds();
List<Group> groups = projectGroups(events);
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
return groups.stream()
.filter(group -> group.getType() == GroupType.PUBLIC)
.collect(Collectors.toList());
return projectGroupsByIds(groupIds);
}
/**
@ -143,83 +168,48 @@ public class ProjectionService {
*/
@Cacheable("groups")
public List<Group> projectLectures() {
List<UUID> groupIds = eventStoreService.findExistingGroupIds();
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds,
"CreateGroupEvent",
"UpdateGroupTitleEvent");
List<UUID> groupIds = eventStoreService.findLectureGroupIds();
List<Group> lectures = projectGroups(events);
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
return lectures.stream()
.filter(group -> group.getType() == GroupType.LECTURE)
.collect(Collectors.toList());
return projectGroupsByIds(groupIds);
}
/**
* Projiziert Gruppen, in welchen der User aktuell teilnimmt.
* Die Gruppen enthalten nur Metainformationen: Titel und Beschreibung.
*
* @param user Die Id
* @param userid Die Id
*
* @return Liste aus Gruppen
*/
@Cacheable("groups")
public List<Group> projectUserGroups(User user) {
List<UUID> groupIds = eventStoreService.findExistingUserGroups(user);
List<Event> groupEvents = eventStoreService.findEventsByGroupAndType(groupIds,
"CreateGroupEvent",
"UpdateGroupTitleEvent",
"UpdateGroupDescriptionEvent");
public List<Group> projectUserGroups(String userid) {
List<UUID> groupIds = eventStoreService.findExistingUserGroups(userid);
return projectGroups(groupEvents);
}
/**
* Gibt die Gruppe zurück, die zu der übergebenen Id passt.
* Enthält alle verfügbaren Informationen, also auch User (langsam).
* Gibt eine leere Gruppe zurück, falls die Id leer ist.
*
* @param groupId Die Id der gesuchten Gruppe
*
* @return Die gesuchte Gruppe
*
* @throws GroupNotFoundException Wenn die Gruppe nicht gefunden wird
*/
public Group projectSingleGroup(UUID groupId) throws GroupNotFoundException {
if (IdHelper.isEmpty(groupId)) {
throw new GroupNotFoundException(groupId + ": " + ProjectionService.class);
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
try {
List<Event> events = eventStoreService.findGroupEvents(groupId);
return projectSingleGroup(events);
} catch (Exception e) {
log.error("Gruppe {} wurde nicht gefunden!", groupId.toString(), e);
throw new GroupNotFoundException(groupId + ": " + ProjectionService.class);
}
}
/**
* Projiziert eine einzelne Gruppe, welche leer sein darf.
*/
public Group projectParent(UUID parentId) {
if (IdHelper.isEmpty(parentId)) {
return new Group();
}
return projectSingleGroup(parentId);
return projectGroupsByIds(groupIds);
}
/**
* Entfernt alle Gruppen, in welchen ein User teilnimmt, aus einer Gruppenliste.
*
* @param groups Gruppenliste, aus der entfernt wird
* @param user User, welcher teilnimmt
* @param userid User, welcher teilnimmt
*/
void removeUserGroups(List<Group> groups, User user) {
List<UUID> userGroups = eventStoreService.findExistingUserGroups(user);
void removeUserGroups(List<Group> groups, String userid) {
List<UUID> userGroups = eventStoreService.findExistingUserGroups(userid);
groups.removeIf(group -> userGroups.contains(group.getId()));
}
public Group projectGroupByLink(String link) {
return projectGroupById(eventStoreService.findGroupByLink(link));
}
}

View File

@ -1,10 +1,9 @@
package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.SortHelper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@ -33,10 +32,12 @@ public class SearchService {
* @throws EventException Projektionsfehler
*/
@Cacheable("groups")
public List<Group> searchPublicGroups(String search, User user) throws EventException {
public List<Group> searchPublicGroups(String search, String principal) {
List<Group> groups = projectionService.projectPublicGroups();
projectionService.removeUserGroups(groups, user);
sortByGroupType(groups);
System.out.println(groups);
projectionService.removeUserGroups(groups, principal);
System.out.println(groups);
SortHelper.sortByGroupType(groups);
if (search.isEmpty()) {
return groups;
@ -45,25 +46,8 @@ public class SearchService {
log.debug("Es wurde gesucht nach: {}", search);
return groups.stream()
.filter(group -> group.toString().toLowerCase().contains(search.toLowerCase()))
.filter(group -> group.format().toLowerCase().contains(search.toLowerCase()))
.collect(Collectors.toList());
}
/**
* Sortiert die übergebene Liste an Gruppen, sodass Veranstaltungen am Anfang der Liste sind.
*
* @param groups Die Liste von Gruppen die sortiert werden soll
*/
private static void sortByGroupType(List<Group> groups) {
groups.sort((Group g1, Group g2) -> {
if (g1.getType() == GroupType.LECTURE) {
return -1;
}
if (g2.getType() == GroupType.LECTURE) {
return 0;
}
return 1;
});
}
}

View File

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

View File

@ -1,22 +0,0 @@
package mops.gruppen2.persistance;
import mops.gruppen2.persistance.dto.InviteLinkDTO;
import org.springframework.data.jdbc.repository.query.Modifying;
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;
@Repository
public interface InviteRepository extends CrudRepository<InviteLinkDTO, Long> {
@Query("SELECT group_id FROM invite WHERE invite_link = :link")
String findGroupIdByLink(@Param("link") String link);
@Modifying
@Query("DELETE FROM invite WHERE group_id = :group")
void deleteLinkOfGroup(@Param("group") String group);
@Query("SELECT invite_link FROM invite WHERE group_id = :group")
String findLinkByGroupId(@Param("group") String group);
}

View File

@ -12,8 +12,13 @@ public class EventDTO {
@Id
Long event_id;
String group_id;
String user_id;
long group_version;
String exec_id;
String target_id;
String event_type;
String event_payload;
}

View File

@ -1,17 +0,0 @@
package mops.gruppen2.persistance.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
@Table("invite")
@Getter
@AllArgsConstructor
public class InviteLinkDTO {
@Id
Long invite_id;
String group_id;
String invite_link;
}

View File

@ -3,12 +3,12 @@ package mops.gruppen2.web;
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.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.helper.APIHelper;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.service.EventStoreService;
import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.web.api.GroupRequestWrapper;
@ -26,6 +26,7 @@ import java.util.UUID;
*/
@Log4j2
@TraceMethodCalls
@RequiredArgsConstructor
@RestController
@RequestMapping("/gruppen2/api")
public class APIController {
@ -33,11 +34,6 @@ public class APIController {
private final EventStoreService eventStoreService;
private final ProjectionService projectionService;
public APIController(EventStoreService eventStoreService, ProjectionService projectionService) {
this.eventStoreService = eventStoreService;
this.projectionService = projectionService;
}
/**
* Erzeugt eine Liste aus Gruppen, welche sich seit einer übergebenen Event-Id geändert haben.
* Die Gruppen werden vollständig projiziert, enthalten also alle Informationen zum entsprechenden Zeitpunkt.
@ -51,7 +47,7 @@ public class APIController {
@PathVariable("id") long eventId) {
return APIHelper.wrap(eventStoreService.findMaxEventId(),
projectionService.projectNewGroups(eventId));
projectionService.projectChangedGroups(eventId));
}
/**
@ -63,7 +59,7 @@ public class APIController {
public List<String> getApiUserGroups(@ApiParam("Nutzer-Id")
@PathVariable("id") String userId) {
return IdHelper.uuidsToString(eventStoreService.findExistingUserGroups(new User(userId)));
return CommonHelper.uuidsToString(eventStoreService.findExistingUserGroups(userId));
}
/**
@ -75,7 +71,7 @@ public class APIController {
public Group getApiGroup(@ApiParam("Gruppen-Id der gefordeten Gruppe")
@PathVariable("id") String groupId) {
return projectionService.projectSingleGroup(UUID.fromString(groupId));
return projectionService.projectGroupById(UUID.fromString(groupId));
}
}

View File

@ -1,17 +1,19 @@
package mops.gruppen2.web;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.helper.CsvHelper;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.helper.ValidationHelper;
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.Parent;
import mops.gruppen2.domain.model.group.wrapper.Title;
import mops.gruppen2.domain.service.GroupService;
import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.web.form.CreateForm;
import mops.gruppen2.web.form.MetaForm;
import mops.gruppen2.web.form.UserLimitForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
@ -19,6 +21,8 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.security.RolesAllowed;
import javax.validation.Valid;
@ -26,6 +30,7 @@ import javax.validation.Valid;
@SuppressWarnings("SameReturnValue")
@Log4j2
@TraceMethodCalls
@RequiredArgsConstructor
@Controller
@RequestMapping("/gruppen2")
public class GroupCreationController {
@ -33,11 +38,6 @@ public class GroupCreationController {
private final GroupService groupService;
private final ProjectionService projectionService;
public GroupCreationController(GroupService groupService, ProjectionService projectionService) {
this.groupService = groupService;
this.projectionService = projectionService;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/create")
public String getCreate(Model model) {
@ -51,26 +51,27 @@ public class GroupCreationController {
@PostMapping("/create")
@CacheEvict(value = "groups", allEntries = true)
public String postCreateOrga(KeycloakAuthenticationToken token,
@Valid CreateForm create,
@Valid MetaForm meta,
@Valid UserLimitForm limit) {
@RequestParam("type") Type type,
@RequestParam("parent") @Valid Parent parent,
@RequestParam("title") @Valid Title title,
@RequestParam("description") @Valid Description description,
@RequestParam("limit") @Valid Limit limit,
@RequestParam(value = "file", required = false) MultipartFile file) {
// Zusätzlicher check: studentin kann keine lecture erstellen
ValidationHelper.validateCreateForm(token, create);
ValidationHelper.validateCreateForm(token, type);
User user = new User(token);
Group group = groupService.createGroup(user,
meta.getTitle(),
meta.getDescription(),
create.getType(),
limit.getUserlimit(),
create.getParent());
String principal = token.getName();
Group group = groupService.createGroup(principal);
groupService.initGroupMembers(group, principal, principal, new User(token), limit);
groupService.initGroupMeta(group, principal, type, parent);
groupService.initGroupText(group, principal, title, description);
// ROLE_studentin kann kein CSV importieren
if (token.getAccount().getRoles().contains("orga")) {
groupService.addUsersToGroup(CsvHelper.readCsvFile(create.getFile()), group, user);
groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file));
}
return "redirect:/gruppen2/details/" + IdHelper.uuidToString(group.getId());
return "redirect:/gruppen2/details/" + group.getId();
}
}

View File

@ -1,17 +1,17 @@
package mops.gruppen2.web;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.helper.CsvHelper;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.group.Group;
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.GroupService;
import mops.gruppen2.domain.service.InviteService;
import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.web.form.MetaForm;
import mops.gruppen2.web.form.UserLimitForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
@ -31,39 +31,31 @@ import java.util.UUID;
@SuppressWarnings("SameReturnValue")
@Log4j2
@TraceMethodCalls
@RequiredArgsConstructor
@Controller
@RequestMapping("/gruppen2")
public class GroupDetailsController {
private final InviteService inviteService;
private final GroupService groupService;
private final ProjectionService projectionService;
public GroupDetailsController(InviteService inviteService, GroupService groupService, ProjectionService projectionService) {
this.inviteService = inviteService;
this.groupService = groupService;
this.projectionService = projectionService;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/{id}")
public String getDetailsPage(KeycloakAuthenticationToken token,
Model model,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
// Parent Badge
UUID parentId = group.getParent();
Group parent = projectionService.projectParent(parentId);
Group parent = projectionService.projectParent(group.getParent());
model.addAttribute("group", group);
model.addAttribute("parent", parent);
// Detailseite für nicht-Mitglieder
if (!ValidationHelper.checkIfMember(group, user)) {
if (!ValidationHelper.checkIfMember(group, principal)) {
return "preview";
}
@ -76,14 +68,14 @@ public class GroupDetailsController {
public String postDetailsJoin(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
if (ValidationHelper.checkIfMember(group, user)) {
if (ValidationHelper.checkIfMember(group, principal)) {
return "redirect:/gruppen2/details/" + groupId;
}
groupService.addUser(user, group);
groupService.addMember(group, principal, principal, new User(token));
return "redirect:/gruppen2/details/" + groupId;
}
@ -94,12 +86,10 @@ public class GroupDetailsController {
public String postDetailsLeave(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
ValidationHelper.throwIfNoMember(group, user);
groupService.deleteUser(user, group);
groupService.kickMember(group, principal, principal);
return "redirect:/gruppen2";
}
@ -111,15 +101,15 @@ public class GroupDetailsController {
HttpServletRequest request,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
// Invite Link
String actualURL = request.getRequestURL().toString();
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
String link = serverURL + "gruppen2/join/" + inviteService.getLinkByGroup(group);
String link = serverURL + "gruppen2/join/" + group.getLink();
ValidationHelper.throwIfNoAdmin(group, user);
ValidationHelper.throwIfNoAdmin(group, principal);
model.addAttribute("group", group);
model.addAttribute("link", link);
@ -132,13 +122,14 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditMeta(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@Valid MetaForm form) {
@Valid Title title,
@Valid Description description) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.updateTitle(user, group, form.getTitle());
groupService.updateDescription(user, group, form.getDescription());
groupService.setTitle(group, principal, title);
groupService.setDescription(group, principal, description);
return "redirect:/gruppen2/details/" + groupId + "/edit";
}
@ -148,11 +139,11 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditUserLimit(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@Valid UserLimitForm form) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
@Valid Limit limit) {
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.updateUserLimit(user, group, form.getUserlimit());
groupService.setLimit(group, principal, limit);
return "redirect:/gruppen2/details/" + groupId + "/edit";
}
@ -164,10 +155,10 @@ public class GroupDetailsController {
@PathVariable("id") String groupId,
@RequestParam(value = "file", required = false) MultipartFile file) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(IdHelper.stringToUUID(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
groupService.addUsersToGroup(CsvHelper.readCsvFile(file), group, user);
groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file));
return "redirect:/gruppen2/details/" + groupId + "/edit";
}
@ -177,17 +168,17 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditRole(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@PathVariable("userid") String userId) {
@PathVariable("userid") String target) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
ValidationHelper.throwIfNoAdmin(group, user);
ValidationHelper.throwIfNoAdmin(group, principal);
groupService.toggleMemberRole(new User(userId), group);
groupService.toggleMemberRole(group, principal, target);
// Falls sich der User selbst die Rechte genommen hat
if (!ValidationHelper.checkIfAdmin(group, user)) {
if (!ValidationHelper.checkIfAdmin(group, principal)) {
return "redirect:/gruppen2/details/" + groupId;
}
@ -199,16 +190,16 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditDelete(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId,
@PathVariable("userid") String userId) {
@PathVariable("userid") String target) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupId));
ValidationHelper.throwIfNoAdmin(group, user);
ValidationHelper.throwIfNoAdmin(group, principal);
// Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen)
if (!userId.equals(user.getId())) {
groupService.deleteUser(new User(userId), group);
if (!principal.equals(target)) {
groupService.kickMember(group, principal, target);
}
return "redirect:/gruppen2/details/" + groupId + "/edit";
@ -218,12 +209,12 @@ public class GroupDetailsController {
@PostMapping("/details/{id}/edit/destroy")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditDestroy(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) {
@PathVariable("id") String groupid) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
String principal = token.getName();
Group group = projectionService.projectGroupById(UUID.fromString(groupid));
groupService.deleteGroup(user, group);
groupService.deleteGroup(group, principal);
return "redirect:/gruppen2";
}

View File

@ -1,8 +1,8 @@
package mops.gruppen2.web;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCall;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.PageNotFoundException;
import mops.gruppen2.domain.service.ProjectionService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@ -16,15 +16,12 @@ import javax.servlet.http.HttpServletRequest;
@SuppressWarnings("SameReturnValue")
@Log4j2
@RequiredArgsConstructor
@Controller
public class GruppenfindungController {
private final ProjectionService projectionService;
public GruppenfindungController(ProjectionService projectionService) {
this.projectionService = projectionService;
}
// For convenience
//@GetMapping("")
//public String redirect() {
@ -37,9 +34,8 @@ public class GruppenfindungController {
public String getIndexPage(KeycloakAuthenticationToken token,
Model model) {
User user = new User(token);
model.addAttribute("groups", projectionService.projectUserGroups(user));
String principal = token.getName();
model.addAttribute("groups", projectionService.projectUserGroups(principal));
return "index";
}

View File

@ -1,9 +1,9 @@
package mops.gruppen2.web;
import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.model.group.Role;
import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.group.User;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ -20,15 +20,15 @@ public class ModelAttributeControllerAdvice {
// Prevent NullPointerException if not logged in
if (token != null) {
model.addAttribute("account", new Account(token));
model.addAttribute("user", new User(token));
model.addAttribute("principal", new User(token));
}
// Add enums
model.addAttribute("member", Role.MEMBER);
model.addAttribute("admin", Role.ADMIN);
model.addAttribute("public", GroupType.PUBLIC);
model.addAttribute("private", GroupType.PRIVATE);
model.addAttribute("lecture", GroupType.LECTURE);
model.addAttribute("REGULAR", Role.REGULAR);
model.addAttribute("ADMIN", Role.ADMIN);
model.addAttribute("PUBLIC", Type.PUBLIC);
model.addAttribute("PRIVATE", Type.PRIVATE);
model.addAttribute("LECTURE", Type.LECTURE);
}
}

View File

@ -1,12 +1,11 @@
package mops.gruppen2.web;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.service.InviteService;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.domain.service.SearchService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@ -25,20 +24,14 @@ import java.util.List;
@SuppressWarnings("SameReturnValue")
@Log4j2
@TraceMethodCalls
@RequiredArgsConstructor
@Controller
@RequestMapping("/gruppen2")
public class SearchAndInviteController {
private final InviteService inviteService;
private final ProjectionService projectionService;
private final SearchService searchService;
public SearchAndInviteController(InviteService inviteService, ProjectionService projectionService, SearchService searchService) {
this.inviteService = inviteService;
this.projectionService = projectionService;
this.searchService = searchService;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/search")
public String getSearch(Model model) {
@ -54,8 +47,8 @@ public class SearchAndInviteController {
Model model,
@RequestParam("string") String search) {
User user = new User(token);
List<Group> groups = searchService.searchPublicGroups(search, user);
String principal = token.getName();
List<Group> groups = searchService.searchPublicGroups(search, principal);
model.addAttribute("groups", groups);
@ -68,18 +61,18 @@ public class SearchAndInviteController {
Model model,
@PathVariable("link") String link) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(inviteService.getGroupIdFromLink(link));
String principal = token.getName();
Group group = projectionService.projectGroupByLink(link);
model.addAttribute("group", group);
// Gruppe öffentlich
if (group.getType() == GroupType.PUBLIC) {
if (group.getType() == Type.PUBLIC) {
return "redirect:/gruppen2/details/" + group.getId();
}
// Bereits Mitglied
if (ValidationHelper.checkIfMember(group, user)) {
if (ValidationHelper.checkIfMember(group, principal)) {
return "redirect:/gruppen2/details/" + group.getId();
}

View File

@ -2,7 +2,7 @@ package mops.gruppen2.web.api;
import lombok.AllArgsConstructor;
import lombok.Getter;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.model.group.Group;
import java.util.List;

View File

@ -1,29 +0,0 @@
package mops.gruppen2.web.form;
import lombok.Data;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.helper.IdHelper;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
import java.util.UUID;
@Data
public class CreateForm {
@NotBlank
String type;
@NotBlank
String parent;
MultipartFile file;
public GroupType getType() {
return GroupType.valueOf(type);
}
public UUID getParent() {
return getType() == GroupType.LECTURE ? IdHelper.emptyUUID() : IdHelper.stringToUUID(parent);
}
}

View File

@ -1,18 +0,0 @@
package mops.gruppen2.web.form;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@Data
public class MetaForm {
@NotBlank
@Size(min = 4, max = 128)
String title;
@NotBlank
@Size(min = 4, max = 512)
String description;
}

View File

@ -1,14 +0,0 @@
package mops.gruppen2.web.form;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
@Data
public class UserLimitForm {
@Min(1)
@Max(999_999)
long userlimit;
}

View File

@ -2,7 +2,7 @@
logging.application.name = gruppen2
logging.pattern.console = [${logging.application.name}], %magenta(%-5level), %d{dd-MM-yyyy HH:mm:ss.SSS},\t%blue(%msg)\t%thread,%logger.%M%n
spring.output.ansi.enabled = always
logging.level.mops.gruppen2 = info
logging.level.mops.gruppen2 = trace
logging.level.org.springframework.jdbc.core = info
# Database

View File

@ -1,158 +0,0 @@
INSERT INTO event
VALUES (1, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '07FEA694', 'CreateGroupEvent',
'{"type":"CreateGroupEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"07FEA694","groupParent":null,"groupType":"PUBLIC"}'),
(2, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '2', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"2","givenname":"Marcos","familyname":"Glover","email":"Marcos.Glover@mail.de"}'),
(3, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '2', 'UpdateUserLimitEvent',
'{"type":"UpdateUserLimitEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"2","userLimit":"1000"}'),
(4, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '5CFBF1AF', 'UpdateGroupTitleEvent',
'{"type":"UpdateGroupTitleEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"5CFBF1AF","newGroupTitle":"Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra"}'),
(5, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '41E11CBC', 'UpdateGroupDescriptionEvent',
'{"type":"UpdateGroupDescriptionEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"41E11CBC","newGroupDescription":"Have you seen my Bear Tibbers? Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyra Zyrav"}'),
(6, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '3', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"3","givenname":"Antwan","familyname":"Smitham","email":"Antwan.Smitham@mail.de"}'),
(7, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '4', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"4","givenname":"Numbers","familyname":"Rice","email":"Numbers.Rice@mail.de"}'),
(8, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '5', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"5","givenname":"Charlette","familyname":"Brakus","email":"Charlette.Brakus@mail.de"}'),
(9, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '6', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"6","givenname":"Alethea","familyname":"Kessler","email":"Alethea.Kessler@mail.de"}'),
(10, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '7', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"7","givenname":"Daren","familyname":"Kreiger","email":"Daren.Kreiger@mail.de"}'),
(11, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '8', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"8","givenname":"Gabriel","familyname":"Feeney","email":"Gabriel.Feeney@mail.de"}'),
(12, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '9', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"9","givenname":"Roosevelt","familyname":"Buckridge","email":"Roosevelt.Buckridge@mail.de"}'),
(13, '2d90ae27-1129-4b61-84a5-6c7d585e777e', '10', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"2d90ae27-1129-4b61-84a5-6c7d585e777e","userId":"10","givenname":"Eduardo","familyname":"Lowe","email":"Eduardo.Lowe@mail.de"}'),
(14, '555a531c-8b80-491c-88b4-8fc18170d318', '2D7D9CE0', 'CreateGroupEvent',
'{"type":"CreateGroupEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"2D7D9CE0","groupParent":null,"groupType":"PRIVATE"}'),
(15, '555a531c-8b80-491c-88b4-8fc18170d318', 'orga', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"orga","givenname":"Jerald","familyname":"Ward","email":"Jerald.Ward@mail.de"}'),
(16, '555a531c-8b80-491c-88b4-8fc18170d318', 'orga', 'UpdateRoleEvent',
'{"type":"UpdateRoleEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"orga","newRole":"ADMIN"}'),
(17, '555a531c-8b80-491c-88b4-8fc18170d318', '2', 'UpdateUserLimitEvent',
'{"type":"UpdateUserLimitEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"2","userLimit":"1000"}'),
(18, '555a531c-8b80-491c-88b4-8fc18170d318', '5CFBF1AF', 'UpdateGroupTitleEvent',
'{"type":"UpdateGroupTitleEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"5CFBF1AF","newGroupTitle":"Zyra"}'),
(19, '555a531c-8b80-491c-88b4-8fc18170d318', 'EF050D58', 'UpdateGroupDescriptionEvent',
'{"type":"UpdateGroupDescriptionEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"EF050D58","newGroupDescription":"Not Draven Draaaaven."}'),
(20, '555a531c-8b80-491c-88b4-8fc18170d318', '3', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"3","givenname":"Jesica","familyname":"OKeefe","email":"Jesica.OKeefe@mail.de"}'),
(21, '555a531c-8b80-491c-88b4-8fc18170d318', '4', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"555a531c-8b80-491c-88b4-8fc18170d318","userId":"4","givenname":"Jess","familyname":"Schamberger","email":"Jess.Schamberger@mail.de"}'),
(22, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', 'E7E2EC5F', 'CreateGroupEvent',
'{"type":"CreateGroupEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"E7E2EC5F","groupParent":null,"groupType":"LECTURE"}'),
(23, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', 'orga', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"orga","givenname":"Hana","familyname":"Connelly","email":"Hana.Connelly@mail.de"}'),
(24, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '2', 'UpdateUserLimitEvent',
'{"type":"UpdateUserLimitEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"2","userLimit":"1000"}'),
(25, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '4EDE191D', 'UpdateGroupTitleEvent',
'{"type":"UpdateGroupTitleEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"4EDE191D","newGroupTitle":"Vi"}'),
(26, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '6EF0B5F9', 'UpdateGroupDescriptionEvent',
'{"type":"UpdateGroupDescriptionEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"6EF0B5F9","newGroupDescription":"Ready to set the world on fire..."}'),
(27, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '3', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"3","givenname":"Steve","familyname":"Swaniawski","email":"Steve.Swaniawski@mail.de"}'),
(28, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '4', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"4","givenname":"Natalie","familyname":"Anderson","email":"Natalie.Anderson@mail.de"}'),
(29, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '5', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"5","givenname":"Asuncion","familyname":"Koss","email":"Asuncion.Koss@mail.de"}'),
(30, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '6', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"6","givenname":"Dana","familyname":"Kemmer","email":"Dana.Kemmer@mail.de"}'),
(31, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '7', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"7","givenname":"Herbert","familyname":"Moen","email":"Herbert.Moen@mail.de"}'),
(32, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '8', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"8","givenname":"Dick","familyname":"Goodwin","email":"Dick.Goodwin@mail.de"}'),
(33, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '9', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"9","givenname":"Deangelo","familyname":"Hoeger","email":"Deangelo.Hoeger@mail.de"}'),
(34, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '10', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"10","givenname":"Zina","familyname":"Abernathy","email":"Zina.Abernathy@mail.de"}'),
(35, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '11', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"11","givenname":"Karima","familyname":"Adams","email":"Karima.Adams@mail.de"}'),
(36, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '12', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"12","givenname":"Evette","familyname":"Price","email":"Evette.Price@mail.de"}'),
(37, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '13', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"13","givenname":"Chester","familyname":"Mayert","email":"Chester.Mayert@mail.de"}'),
(38, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '14', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"14","givenname":"Trang","familyname":"Watsica","email":"Trang.Watsica@mail.de"}'),
(39, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '15', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"15","givenname":"Cameron","familyname":"Terry","email":"Cameron.Terry@mail.de"}'),
(40, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '16', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"16","givenname":"Tynisha","familyname":"Grant","email":"Tynisha.Grant@mail.de"}'),
(41, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '17', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"17","givenname":"Leila","familyname":"Thiel","email":"Leila.Thiel@mail.de"}'),
(42, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '18', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"18","givenname":"Lorenzo","familyname":"Weimann","email":"Lorenzo.Weimann@mail.de"}'),
(43, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '19', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"19","givenname":"Alonzo","familyname":"Keebler","email":"Alonzo.Keebler@mail.de"}'),
(44, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '20', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"20","givenname":"Maximo","familyname":"Olson","email":"Maximo.Olson@mail.de"}'),
(45, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '21', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"21","givenname":"Lisa","familyname":"Konopelski","email":"Lisa.Konopelski@mail.de"}'),
(46, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '22', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"22","givenname":"Samuel","familyname":"Parisian","email":"Samuel.Parisian@mail.de"}'),
(47, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '23', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"23","givenname":"Irish","familyname":"Grant","email":"Irish.Grant@mail.de"}'),
(48, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '24', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"24","givenname":"Donnell","familyname":"Olson","email":"Donnell.Olson@mail.de"}'),
(49, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '25', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"25","givenname":"Jeana","familyname":"MacGyver","email":"Jeana.MacGyver@mail.de"}'),
(50, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '26', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"26","givenname":"Mark","familyname":"Stroman","email":"Mark.Stroman@mail.de"}'),
(51, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '27', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"27","givenname":"Genevive","familyname":"Christiansen","email":"Genevive.Christiansen@mail.de"}'),
(52, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '28', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"28","givenname":"Marshall","familyname":"Wisoky","email":"Marshall.Wisoky@mail.de"}'),
(53, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '29', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"29","givenname":"Hugo","familyname":"Krajcik","email":"Hugo.Krajcik@mail.de"}'),
(54, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '30', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"30","givenname":"Genevive","familyname":"Weber","email":"Genevive.Weber@mail.de"}'),
(55, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '31', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"31","givenname":"Cleotilde","familyname":"Baumbach","email":"Cleotilde.Baumbach@mail.de"}'),
(56, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '32', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"32","givenname":"Kaitlyn","familyname":"Bins","email":"Kaitlyn.Bins@mail.de"}'),
(57, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '33', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"33","givenname":"Sergio","familyname":"Hettinger","email":"Sergio.Hettinger@mail.de"}'),
(58, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '34', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"34","givenname":"Beckie","familyname":"Heaney","email":"Beckie.Heaney@mail.de"}'),
(59, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '35', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"35","givenname":"Kareem","familyname":"Considine","email":"Kareem.Considine@mail.de"}'),
(60, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '36', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"36","givenname":"Kenyatta","familyname":"Wolff","email":"Kenyatta.Wolff@mail.de"}'),
(61, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '37', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"37","givenname":"Brock","familyname":"Romaguera","email":"Brock.Romaguera@mail.de"}'),
(62, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '38', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"38","givenname":"Carey","familyname":"Reynolds","email":"Carey.Reynolds@mail.de"}'),
(63, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '39', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"39","givenname":"Gabriel","familyname":"Rath","email":"Gabriel.Rath@mail.de"}'),
(64, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '40', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"40","givenname":"Tristan","familyname":"Dare","email":"Tristan.Dare@mail.de"}'),
(65, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '41', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"41","givenname":"Augustus","familyname":"Reilly","email":"Augustus.Reilly@mail.de"}'),
(66, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '42', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"42","givenname":"Dwayne","familyname":"Lynch","email":"Dwayne.Lynch@mail.de"}'),
(67, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '43', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"43","givenname":"Ben","familyname":"Corkery","email":"Ben.Corkery@mail.de"}'),
(68, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '44', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"44","givenname":"Alfredo","familyname":"Towne","email":"Alfredo.Towne@mail.de"}'),
(69, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '45', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"45","givenname":"Shante","familyname":"Becker","email":"Shante.Becker@mail.de"}'),
(70, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '46', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"46","givenname":"Elinor","familyname":"Bernier","email":"Elinor.Bernier@mail.de"}'),
(71, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '47', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"47","givenname":"Lee","familyname":"Dibbert","email":"Lee.Dibbert@mail.de"}'),
(72, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '48', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"48","givenname":"Augustina","familyname":"Friesen","email":"Augustina.Friesen@mail.de"}'),
(73, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '49', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"49","givenname":"Kanesha","familyname":"Olson","email":"Kanesha.Olson@mail.de"}'),
(74, '5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3', '50', 'AddUserEvent',
'{"type":"AddUserEvent","groupId":"5e1c2b9a-bf3a-4c9e-9bf0-f5beecb69eb3","userId":"50","givenname":"Era","familyname":"Willms","email":"Era.Willms@mail.de"}');

View File

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

View File

@ -40,11 +40,11 @@
<!--Anzahl Text-->
<div class="mb-2">
<span>Teilnehmer: </span>
<span th:text="${group.getMembers().size() + ' von ' + group.getUserLimit()}"></span>
<span th:text="${group.size() + ' von ' + group.getLimit()}"></span>
</div>
<!--Bearbeiten-Button-->
<div class="mb-2" th:if="${group.getRoles().get(user.getId()) == admin}">
<div class="mb-2" th:if="${group.isAdmin(principal.getId())}">
<form method="get"
th:action="@{/gruppen2/details/{id}/edit(id=${group.getId()})}">
<button class="btn btn-secondary btn-block">Gruppe verwalten</button>
@ -56,7 +56,7 @@
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between"
th:each="member : ${group.getMembers()}">
<span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span>
<span th:text="${member.format()}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span>
</li>
</ul>

View File

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

View File

@ -1,7 +1,12 @@
<!DOCTYPE HTML>
<!--suppress ALL -->
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--/*@thymesVar id="parent" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="group" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="member" type="mops.gruppen2.domain.model.group.User"*/-->
<!--/*@thymesVar id="account" type="mops.gruppen2.domain.Account"*/-->
<!--/*@thymesVar id="lectures" type="java.util.List<mops.gruppen2.domain.model.group.Group>"*/-->
<!--Meta-->
<th:block th:fragment="meta">
<!--Gruppentitel-->
@ -10,18 +15,19 @@
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Gruppentitel:</span>
</div>
<input type="text" class="form-control" th:name="title" th:value="${group?.getTitle()}"
required minlength="4" maxlength="128">
<input type="text" class="form-control" name="title" minlength="4" maxlength="128"
th:value="${group?.getTitle()}" required>
</div>
<!--Gruppenbeschreibung-->
<div class="input-group"
title="Eine kurze Gruppenbeschreibung zwischen 4 und 512 Zeichen. Die Beschreibung ist öffentlich.">
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Beschreibung:</span>
</div>
<textarea class="form-control" th:name="description" th:text="${group?.getDescription()}"
required minlength="4" maxlength="512"></textarea>
<textarea class="form-control" name="description" minlength="4" maxlength="512"
th:text="${group?.getDescription()}" required></textarea>
</div>
</th:block>
@ -47,7 +53,7 @@
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Gehört zu:</span>
</div>
<input type="hidden" id="parentdummy" value="00000000-0000-0000-0000-000000000000" disabled>
<input type="hidden" id="parentdummy" name="parent" value="00000000-0000-0000-0000-000000000000" disabled>
<select class="custom-select" id="parentselect" name="parent">
<option value="00000000-0000-0000-0000-000000000000" selected>Keiner</option>
<option th:each="lecture : ${lectures}" th:value="${lecture.getId()}"
@ -61,17 +67,17 @@
<th:block th:fragment="userlimit">
<label for="userlimit">Teilnehmeranzahl:</label>
<div class="btn-toolbar row mx-0" id="userlimit">
<input type="hidden" name="userlimit" id="limit" value="999999"
th:disabled="${group != null && group.getUserLimit() < 999999} ? 'disabled' : 'false'">
<input type="hidden" name="limit" id="limit" value="999999"
th:disabled="${group != null && group.getLimit() < 999999} ? 'disabled' : 'false'">
<div class="btn-group btn-group-toggle col-sm-4 px-0" data-toggle="buttons">
<label class="btn btn-secondary active" onclick="disable('#limitselect'); enable('#limit')">
<input type="radio"
th:checked="${group != null && group.getUserLimit() < 999999} ? 'false' : 'checked'">
th:checked="${group != null && group.getLimit() < 999999} ? 'false' : 'checked'">
Unbegrenzt
</label>
<label class="btn btn-secondary" onclick="enable('#limitselect'); disable('#limit')">
<input type="radio"
th:checked="${group != null && group.getUserLimit() < 999999} ? 'checked' : 'false'">
th:checked="${group != null && group.getLimit() < 999999} ? 'checked' : 'false'">
Begrenzt
</label>
</div>
@ -81,10 +87,10 @@
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Limit:</span>
</div>
<input type="number" class="form-control" name="userlimit"
th:value="${group != null} ? ${group.getUserLimit()} : '999999'"
<input type="number" class="form-control" name="limit"
th:value="${group != null} ? ${group.getLimit()} : '999999'"
min="1" max="999999" id="limitselect" required
th:disabled="${group != null && group.getUserLimit() < 999999} ? 'false' : 'disabled'">
th:disabled="${group != null && group.getLimit() < 999999} ? 'false' : 'disabled'">
<div class="input-group-append">
<span class="input-group-text text-monospace">Teilnehmer</span>
</div>
@ -99,7 +105,7 @@
<span class="input-group-text text-monospace">CSV:</span>
</div>
<div class="custom-file">
<input type="file" class="custom-file-input" id="file" th:name="file">
<input type="file" class="custom-file-input" id="file" name="file">
<label class="custom-file-label" for="file">Datei auswählen</label>
</div>
<script>

View File

@ -1,21 +1,24 @@
<!DOCTYPE HTML>
<!--suppress ALL -->
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<!--/*@thymesVar id="parent" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="group" type="mops.gruppen2.domain.model.group.Group"*/-->
<!--/*@thymesVar id="member" type="mops.gruppen2.domain.model.group.User"*/-->
<!--Grouptype Badges-->
<th:block th:fragment="badges">
<span class="badge badge-pill private"
title="Kann nicht über die Suche gefunden werden, beitritt ist per Einladungslink möglich."
th:if='${group.getType() == private}'>Privat</span>
th:if='${group.isPrivate()}'>Privat</span>
<span class="badge badge-pill public"
title="Kann über die Suche gefunden werden, jeder kann beitreten."
th:if="${group.getType() == public}">Öffentlich</span>
th:if="${group.isPublic()}">Öffentlich</span>
<span class="badge badge-pill lecture"
title="Offizielle Veranstaltung"
th:if='${group.getType() == lecture}'>Veranstaltung</span>
th:if='${group.isLecture()}'>Veranstaltung</span>
<span class="badge badge-pill parent"
th:unless="${parent == null || parent.getTitle() == null|| parent.getTitle() == ''}"
th:if="${parent?.exists()}"
th:title="${'Die Gruppe gehört zur Veranstaltung ' + parent.getTitle() + '.'}"
th:text="${parent.getTitle()}">Parent</span>
@ -28,7 +31,7 @@
<!--User Badges-->
<th:block th:fragment="userbadges">
<span class="badge badge-success align-self-start ml-2"
th:if="${group.getRoles().get(member.getId()) == admin}">Admin</span>
th:if="${group.isAdmin(member.getId())}">Admin</span>
</th:block>
<th:block th:fragment="groupcontent">
@ -42,27 +45,27 @@
<span th:text="${group.getDescription()}"></span>
</div>
<!--<div class="content-text-in" th:if="${group.getMembers().contains(user.getId())}"></div>-->
<!--<div class="body-text-in" th:if="${group.getMembers().contains(user.getId())}"></div>-->
</th:block>
<!--Buttonbar zum Gruppe beitreten-->
<th:block th:fragment="joingroup">
<div class="content-heading">
<span th:if="${group.getMembers().size() < group.getUserLimit()}">
<span th:unless="${group.isFull()}">
Möchtest du dieser Gruppe beitreten?
</span>
<span th:unless="${group.getMembers().size() < group.getUserLimit()}">
<span th:if="${group.isFull()}">
Diese Gruppe hat ihre maximale Teilnehmeranzahl erreicht.
</span>
</div>
<div class="row">
<form method="post" th:action="@{/gruppen2/details/{id}/join(id = ${group.getId()})}"
th:if="${group.getMembers().size() < group.getUserLimit()}">
th:unless="${group.isFull()}">
<button class="btn btn-success" type="submit">Gruppe beitreten.</button>
</form>
<div class="col" th:if="${group.getMembers().size() < group.getUserLimit()}"></div>
<div class="col" th:unless="${group.isFull()}"></div>
<a class="btn btn-primary" href="/gruppen2"
type="submit">Startseite.</a>

View File

@ -10,7 +10,6 @@
<main th:fragment="bodycontent">
<div class="container-fluid">
<h1 class="def-cursor" th:text="${group.getTitle()}"></h1>
<div class="content" th:insert="~{fragments/groups :: groupcontent}"></div>

View File

@ -14,10 +14,10 @@
<div class="content" th:insert="~{fragments/groups :: groupcontent}"></div>
<div class="content" th:unless="${group.getType() == private}"
<div class="content" th:unless="${group.isPrivate()}"
th:insert="~{fragments/groups :: joingroup}"></div>
<div class="content row" th:if="${group.getType() == private}">
<div class="content row" th:if="${group.isPrivate()}">
<span class="col"></span>
<a class="btn btn-primary" href="/gruppen2">Startseite.</a>

View File

@ -20,7 +20,7 @@
<div class="input-group-prepend">
<span class="input-group-text text-monospace">Suchbegriff:</span>
</div>
<input class="form-control" th:name="string" type="text">
<input class="form-control" name="string" type="text">
</div>
<button class="btn btn-primary" type="submit">Suchen</button>
</form>

View File

@ -1,31 +1,8 @@
package mops.gruppen2;
import com.github.javafaker.Faker;
import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.event.AddUserEvent;
import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.DeleteGroupEvent;
import mops.gruppen2.domain.event.DeleteUserEvent;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.UpdateGroupDescriptionEvent;
import mops.gruppen2.domain.event.UpdateGroupTitleEvent;
import mops.gruppen2.domain.event.UpdateRoleEvent;
import mops.gruppen2.domain.event.UpdateUserLimitEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class TestBuilder {
private static final Faker faker = new Faker();
/*private static final Faker faker = new Faker();
public static Account account(String name) {
return new Account(name,
@ -48,13 +25,13 @@ public class TestBuilder {
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-"
@ -62,14 +39,14 @@ public class TestBuilder {
+ idString);
}
/**
*//**
* Generiert ein EventLog mit mehreren Gruppen und Usern.
*
* @param count Gruppenanzahl
* @param membercount Mitgliederanzahl pro Gruppe
*
* @return Eventliste
*/
*//*
public static List<Event> completePublicGroups(int count, int membercount) {
return IntStream.range(0, count)
.parallel()
@ -93,7 +70,7 @@ public class TestBuilder {
eventList.add(createPublicGroupEvent(groupId));
eventList.add(updateGroupTitleEvent(groupId));
eventList.add(updateGroupDescriptionEvent(groupId));
eventList.add(new UpdateUserLimitEvent(groupId, "fgsadggas", Long.MAX_VALUE));
eventList.add(new SetLimitEvent(groupId, "fgsadggas", new Limit(Long.MAX_VALUE)));
eventList.addAll(addUserEvents(membercount, groupId));
return eventList;
@ -106,7 +83,7 @@ public class TestBuilder {
eventList.add(createPrivateGroupEvent(groupId));
eventList.add(updateGroupTitleEvent(groupId));
eventList.add(updateGroupDescriptionEvent(groupId));
eventList.add(new UpdateUserLimitEvent(groupId, "fgsadggas", Long.MAX_VALUE));
eventList.add(new SetLimitEvent(groupId, "fgsadggas", new Limit(Long.MAX_VALUE)));
eventList.addAll(addUserEvents(membercount, groupId));
return eventList;
@ -120,13 +97,13 @@ public class TestBuilder {
return completePrivateGroup(100);
}
/**
*//**
* Generiert mehrere CreateGroupEvents, 1 <= groupId <= count.
*
* @param count Anzahl der verschiedenen Gruppen
*
* @return Eventliste
*/
*//*
public static List<Event> createPublicGroupEvents(int count) {
return IntStream.range(0, count)
.parallel()
@ -150,8 +127,9 @@ public class TestBuilder {
.collect(Collectors.toList());
}
public static Event createPrivateGroupEvent(UUID groupId) {
return createGroupEvent(groupId, GroupType.PRIVATE);
public static List<Event> createPrivateGroupEvents(UUID groupId) {
return (new ArrayList<>()).addAll(createGroupEvent(groupId)),
new SetTypeEvent(groupId);
}
public static Event createPrivateGroupEvent() {
@ -159,20 +137,17 @@ public class TestBuilder {
}
public static Event createPublicGroupEvent(UUID groupId) {
return createGroupEvent(groupId, GroupType.PUBLIC);
return createGroupEvent(groupId, Type.PUBLIC);
}
public static Event createPublicGroupEvent() {
return createPublicGroupEvent(UUID.randomUUID());
}
public static Event createGroupEvent(UUID groupId, GroupType type) {
return new CreateGroupEvent(
groupId,
faker.random().hex(),
null,
type
);
public static Event createGroupEvent(UUID groupId) {
return new CreateGroupEvent(groupId,
faker.random().hex(),
LocalDateTime.now());
}
public static Event createLectureEvent() {
@ -184,18 +159,18 @@ public class TestBuilder {
groupId,
faker.random().hex(),
null,
GroupType.LECTURE
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<Event> addUserEvents(int count, UUID groupId) {
return IntStream.range(0, count)
.parallel()
@ -207,7 +182,7 @@ public class TestBuilder {
String firstname = firstname();
String lastname = lastname();
return new AddUserEvent(
return new AddMemberEvent(
groupId,
userId,
firstname,
@ -224,13 +199,13 @@ public class TestBuilder {
public static List<Event> deleteUserEvents(int count, List<Event> eventList) {
List<Event> removeEvents = new ArrayList<>();
List<Event> shuffle = eventList.parallelStream()
.filter(event -> event instanceof AddUserEvent)
.filter(event -> event instanceof AddMemberEvent)
.collect(Collectors.toList());
Collections.shuffle(shuffle);
for (Event event : shuffle) {
removeEvents.add(new DeleteUserEvent(event.getGroupId(), event.getUserId()));
removeEvents.add(new KickMemberEvent(event.getGroupid(), event.getTarget()));
if (removeEvents.size() >= count) {
break;
@ -240,21 +215,21 @@ public class TestBuilder {
return removeEvents;
}
/**
*//**
* Erzeugt mehrere DeleteUserEvents, sodass eine Gruppe komplett geleert wird.
*
* @param group Gruppe welche geleert wird
*
* @return Eventliste
*/
*//*
public static List<Event> deleteUserEvents(Group group) {
return group.getMembers().parallelStream()
.map(user -> deleteUserEvent(group.getId(), user.getId()))
return group.getMemberships().parallelStream()
.map(user -> deleteUserEvent(group.getGroupid(), user.getUserId()))
.collect(Collectors.toList());
}
public static Event deleteUserEvent(UUID groupId, String userId) {
return new DeleteUserEvent(
return new KickMemberEvent(
groupId,
userId
);
@ -265,7 +240,7 @@ public class TestBuilder {
}
public static Event updateGroupDescriptionEvent(UUID groupId, String description) {
return new UpdateGroupDescriptionEvent(
return new SetDescriptionEvent(
groupId,
faker.random().hex(),
description
@ -277,7 +252,7 @@ public class TestBuilder {
}
public static Event updateGroupTitleEvent(UUID groupId, String title) {
return new UpdateGroupTitleEvent(
return new SetTitleEvent(
groupId,
faker.random().hex(),
title
@ -285,7 +260,7 @@ public class TestBuilder {
}
public static Event updateUserLimitMaxEvent(UUID groupId) {
return new UpdateUserLimitEvent(groupId, firstname(), Long.MAX_VALUE);
return new SetLimitEvent(groupId, firstname(), Long.MAX_VALUE);
}
public static Event updateRoleEvent(UUID groupId, String userId, Role role) {
@ -297,7 +272,7 @@ public class TestBuilder {
}
public static Event deleteGroupEvent(UUID groupId) {
return new DeleteGroupEvent(groupId, faker.random().hex());
return new DestroyGroupEvent(groupId, faker.random().hex());
}
private static String firstname() {
@ -318,5 +293,5 @@ public class TestBuilder {
private static String clean(String string) {
return string.replaceAll("['\";,]", "");
}
}*/
}

View File

@ -27,7 +27,7 @@ class DomainTest {
@ArchTest
public static final ArchRule classesInEventPackageShouldHaveEventInName = classes()
.that().resideInAPackage("..domain.event..")
.should().haveSimpleNameEndingWith("Event");
.should().haveSimpleNameNotContaining("..Event..");
@ArchTest
public static final ArchRule exceptionClassesShouldBeInExceptionPackage = classes()

View File

@ -1,60 +0,0 @@
package mops.gruppen2.domain.event;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.exception.GroupFullException;
import mops.gruppen2.domain.exception.UserAlreadyExistsException;
import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.addUserEvent;
import static mops.gruppen2.TestBuilder.apply;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
import static mops.gruppen2.TestBuilder.updateUserLimitMaxEvent;
import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
class AddUserEventTest {
@Test
void applyEvent() {
Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateLimitEvent = updateUserLimitMaxEvent(uuidMock(0));
Event addEvent = new AddUserEvent(uuidMock(0), "A", "Thomas", "Tom", "tho@mail.de");
Group group = apply(createEvent, updateLimitEvent, addEvent);
assertThat(group.getMembers()).hasSize(1);
assertThat(group.getMembers().get(0).getGivenname()).isEqualTo("Thomas");
assertThat(group.getMembers().get(0).getFamilyname()).isEqualTo("Tom");
assertThat(group.getMembers().get(0).getEmail()).isEqualTo("tho@mail.de");
}
@Test
void applyEvent_userAlreadyExists() {
Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateLimitEvent = updateUserLimitMaxEvent(uuidMock(0));
Event addEventA = addUserEvent(uuidMock(0), "A");
Event addEventB = addUserEvent(uuidMock(0), "B");
Event addEventC = addUserEvent(uuidMock(0), "A");
Group group = apply(createEvent, updateLimitEvent, addEventA, addEventB);
assertThrows(UserAlreadyExistsException.class, () -> addEventA.apply(group));
assertThrows(UserAlreadyExistsException.class, () -> addEventC.apply(group));
assertThat(group.getMembers()).hasSize(2);
}
@Test
void applyEvent_groupFull() {
Event createEvent = createPublicGroupEvent(uuidMock(0));
Event maxSizeEvent = new UpdateUserLimitEvent(uuidMock(0), "A", 2L);
Event addEventA = addUserEvent(uuidMock(0), "A");
Event addEventB = addUserEvent(uuidMock(0), "B");
Event addEventC = addUserEvent(uuidMock(0), "C");
Group group = apply(createEvent, maxSizeEvent, addEventA, addEventB);
assertThrows(GroupFullException.class, () -> addEventC.apply(group));
assertThat(group.getMembers()).hasSize(2);
}
}

View File

@ -1,27 +0,0 @@
package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
class CreateGroupEventTest {
@Test
void applyEvent() {
Event createEvent = new CreateGroupEvent(uuidMock(0),
"A",
uuidMock(1),
GroupType.PUBLIC);
Group group = TestBuilder.apply(createEvent);
assertThat(group.getMembers()).hasSize(0);
assertThat(group.getType()).isEqualTo(GroupType.PUBLIC);
assertThat(group.getId()).isEqualTo(uuidMock(0));
assertThat(group.getParent()).isEqualTo(uuidMock(1));
}
}

View File

@ -1,29 +0,0 @@
package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
class DeleteGroupEventTest {
@Test
void applyEvent() {
Event createEvent = new CreateGroupEvent(uuidMock(0),
"A",
uuidMock(1),
GroupType.PUBLIC);
Event deleteEvent = new DeleteGroupEvent(uuidMock(0), "A");
Group group = TestBuilder.apply(createEvent, deleteEvent);
assertThat(group.getMembers()).isEmpty();
assertThat(group.getType()).isEqualTo(null);
assertThat(group.getUserLimit()).isEqualTo(0);
assertThat(group.getId()).isEqualTo(uuidMock(0));
assertThat(group.getParent()).isEqualTo(null);
}
}

Some files were not shown because too many files have changed in this diff Show More