1

refactor, templates need fixing

This commit is contained in:
Christoph
2020-04-14 02:19:27 +02:00
parent f5d668fba2
commit dbb60f30a7
99 changed files with 1612 additions and 1114 deletions

View File

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

View File

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

View File

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

@ -0,0 +1,44 @@
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;
/**
* Fügt einen einzelnen Nutzer einer Gruppe hinzu.
*/
@Log4j2
@Value
@AllArgsConstructor
public class AddMemberEvent extends Event {
@JsonProperty("user")
User user;
public AddMemberEvent(Group group, String exec, String target, User user) throws IdMismatchException {
super(group.getId(), 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 getType() {
return EventType.ADDMEMBER.toString();
}
}

View File

@ -1,49 +0,0 @@
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.helper.ValidationHelper;
import mops.gruppen2.domain.model.Group;
import mops.gruppen2.domain.model.Role;
import mops.gruppen2.domain.model.User;
/**
* Fügt einen einzelnen Nutzer einer Gruppe hinzu.
*/
@Log4j2
@Value
@AllArgsConstructor
public class AddUserEvent extends Event {
@JsonProperty("givenname")
String givenname;
@JsonProperty("familyname")
String familyname;
@JsonProperty("email")
String email;
public AddUserEvent(Group group, User user) {
super(group.getGroupid(), user.getUserid());
givenname = user.getGivenname();
familyname = user.getFamilyname();
email = user.getEmail();
}
@Override
protected void applyEvent(Group group) throws EventException {
ValidationHelper.throwIfMember(group, new User(userid));
ValidationHelper.throwIfGroupFull(group);
group.getMembers().put(userid, new User(userid, givenname, familyname, email));
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

@ -4,10 +4,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Value; import lombok.Value;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.User;
import java.time.LocalDateTime;
import java.util.UUID; import java.util.UUID;
@Log4j2 @Log4j2
@ -15,24 +15,30 @@ import java.util.UUID;
@AllArgsConstructor// Value generiert den allArgsConstrucot nur, wenn keiner explizit angegeben ist @AllArgsConstructor// Value generiert den allArgsConstrucot nur, wenn keiner explizit angegeben ist
public class CreateGroupEvent extends Event { public class CreateGroupEvent extends Event {
@JsonProperty("parent") @JsonProperty("date")
UUID parent; LocalDateTime date;
@JsonProperty("type") public CreateGroupEvent(UUID groupId, String exec, LocalDateTime date) {
Type type; super(groupId, exec, null);
this.date = date;
public CreateGroupEvent(UUID groupId, User user, UUID parent, Type type) {
super(groupId, user.getUserid());
this.parent = parent;
this.type = type;
} }
@Override @Override
protected void applyEvent(Group group) { protected void applyEvent(Group group) throws BadArgumentException {
group.setGroupid(groupid); group.setId(groupid);
group.setParent(parent); group.setCreator(exec);
group.setType(type); group.setCreationDate(date);
log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString()); log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString());
} }
@Override
public String getType() {
return EventType.CREATEGROUP.toString();
}
@Override
public String toString() {
return "(" + version + "," + groupid + "," + date + ")";
}
} }

View File

@ -1,30 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.model.Group;
import mops.gruppen2.domain.model.User;
@Log4j2
@Value
@AllArgsConstructor
public class DeleteGroupEvent extends Event {
public DeleteGroupEvent(Group group, User user) {
super(group.getGroupid(), user.getUserid());
}
@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.setLimit(null);
log.trace("\t\t\t\t\tGelöschte Gruppe: {}", group);
}
}

View File

@ -1,33 +0,0 @@
package mops.gruppen2.domain.event;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.Group;
import mops.gruppen2.domain.model.User;
/**
* Entfernt ein einzelnes Mitglied einer Gruppe.
*/
@Log4j2
@Value
@AllArgsConstructor
public class DeleteUserEvent extends Event {
public DeleteUserEvent(Group group, User user) {
super(group.getGroupid(), user.getUserid());
}
@Override
protected void applyEvent(Group group) throws EventException {
ValidationHelper.throwIfNoMember(group, new User(userid));
group.getMembers().remove(userid);
group.getRoles().remove(userid);
log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers());
log.trace("\t\t\t\t\tNeue Rollen: {}", group.getRoles());
}
}

View File

@ -0,0 +1,29 @@
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;
@Log4j2
@Value
@AllArgsConstructor
public class DestroyGroupEvent extends Event {
public DestroyGroupEvent(Group group, String exec) {
super(group.getId(), 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 getType() {
return EventType.DESTROYGROUP.toString();
}
}

View File

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

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,33 @@
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;
/**
* Entfernt ein einzelnes Mitglied einer Gruppe.
*/
@Log4j2
@Value
@AllArgsConstructor
public class KickMemberEvent extends Event {
public KickMemberEvent(Group group, String exec, String target) {
super(group.getId(), 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 getType() {
return EventType.KICKMEMBER.toString();
}
}

View File

@ -0,0 +1,40 @@
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;
/**
* Ändert nur die Gruppenbeschreibung.
*/
@Log4j2
@Value
@AllArgsConstructor
public class SetDescriptionEvent extends Event {
@JsonProperty("desc")
Description description;
public SetDescriptionEvent(Group group, String exec, @Valid Description description) {
super(group.getId(), 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 getType() {
return EventType.SETDESCRIPTION.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.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.wrapper.Link;
import javax.validation.Valid;
@Log4j2
@Value
@AllArgsConstructor
public class SetInviteLinkEvent extends Event {
@JsonProperty("link")
Link link;
public SetInviteLinkEvent(Group group, String exec, @Valid Link link) {
super(group.getId(), 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 getType() {
return EventType.SETLINK.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.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;
@Log4j2
@Value
@AllArgsConstructor
public class SetLimitEvent extends Event {
@JsonProperty("limit")
Limit limit;
public SetLimitEvent(Group group, String exec, @Valid Limit limit) {
super(group.getId(), 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 getType() {
return EventType.SETLIMIT.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.NoAccessException;
import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.group.wrapper.Parent;
import javax.validation.Valid;
@Log4j2
@Value
@AllArgsConstructor
public class SetParentEvent extends Event {
@JsonProperty("parent")
Parent parent;
public SetParentEvent(Group group, String exec, @Valid Parent parent) {
super(group.getId(), 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 getType() {
return EventType.SETPARENT.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.Title;
import javax.validation.Valid;
/**
* Ändert nur den Gruppentitel.
*/
@Log4j2
@Value
@AllArgsConstructor
public class SetTitleEvent extends Event {
@JsonProperty("title")
Title title;
public SetTitleEvent(Group group, String exec, @Valid Title title) {
super(group.getId(), 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 getType() {
return EventType.SETTITLE.toString();
}
}

View File

@ -0,0 +1,36 @@
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;
@Log4j2
@Value
@AllArgsConstructor
public class SetTypeEvent extends Event {
@JsonProperty("type")
Type type;
public SetTypeEvent(Group group, String exec, @Valid Type type) {
super(group.getId(), exec, null);
this.type = type;
}
@Override
protected void applyEvent(Group group) throws EventException {
group.setType(exec, type);
}
@Override
public String getType() {
return EventType.SETTYPE.toString();
}
}

View File

@ -1,33 +0,0 @@
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.model.Description;
import mops.gruppen2.domain.model.Group;
import mops.gruppen2.domain.model.User;
/**
* Ändert nur die Gruppenbeschreibung.
*/
@Log4j2
@Value
@AllArgsConstructor
public class UpdateGroupDescriptionEvent extends Event {
@JsonProperty("desc")
Description description;
public UpdateGroupDescriptionEvent(Group group, User user, Description description) {
super(group.getGroupid(), user.getUserid());
this.description = description;
}
@Override
protected void applyEvent(Group group) {
group.setDescription(description);
log.trace("\t\t\t\t\tNeue Beschreibung: {}", group.getDescription());
}
}

View File

@ -1,34 +0,0 @@
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.model.Group;
import mops.gruppen2.domain.model.Title;
import mops.gruppen2.domain.model.User;
/**
* Ändert nur den Gruppentitel.
*/
@Log4j2
@Value
@AllArgsConstructor
public class UpdateGroupTitleEvent extends Event {
@JsonProperty("title")
Title title;
public UpdateGroupTitleEvent(Group group, User user, Title title) {
super(group.getGroupid(), user.getUserid());
this.title = title;
}
@Override
protected void applyEvent(Group group) {
group.setTitle(title);
log.trace("\t\t\t\t\tNeuer Titel: {}", group.getTitle());
}
}

View File

@ -4,11 +4,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Value; import lombok.Value;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.LastAdminException;
import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.helper.ValidationHelper; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Role;
import mops.gruppen2.domain.model.Role;
import mops.gruppen2.domain.model.User;
/** /**
* Aktualisiert die Gruppenrolle eines Teilnehmers. * Aktualisiert die Gruppenrolle eines Teilnehmers.
@ -21,18 +20,21 @@ public class UpdateRoleEvent extends Event {
@JsonProperty("role") @JsonProperty("role")
Role role; Role role;
public UpdateRoleEvent(Group group, User user, Role tole) { public UpdateRoleEvent(Group group, String exec, String target, Role role) {
super(group.getGroupid(), user.getUserid()); super(group.getId(), exec, target);
role = tole; this.role = role;
} }
@Override @Override
protected void applyEvent(Group group) throws UserNotFoundException { protected void applyEvent(Group group) throws UserNotFoundException, LastAdminException {
ValidationHelper.throwIfNoMember(group, new User(userid)); group.memberPutRole(target, role);
group.getRoles().put(userid, role); log.trace("\t\t\t\t\tNeue Admin: {}", group.getAdmins());
}
log.trace("\t\t\t\t\tNeue Rollen: {}", group.getRoles()); @Override
public String getType() {
return EventType.UPDATEROLE.toString();
} }
} }

View File

@ -1,36 +0,0 @@
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.BadParameterException;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.model.Group;
import mops.gruppen2.domain.model.Limit;
import mops.gruppen2.domain.model.User;
@Log4j2
@Value
@AllArgsConstructor
public class UpdateUserLimitEvent extends Event {
@JsonProperty("limit")
Limit limit;
public UpdateUserLimitEvent(Group group, User user, Limit limit) {
super(group.getGroupid(), user.getUserid());
this.limit = limit;
}
@Override
protected void applyEvent(Group group) throws EventException {
if (limit.getUserLimit() < group.getMembers().size()) {
throw new BadParameterException("Teilnehmerlimit zu klein.");
}
group.setLimit(limit);
log.trace("\t\t\t\t\tNeues UserLimit: {}", group.getLimit());
}
}

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,12 +0,0 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class BadParameterException extends EventException {
private static final long serialVersionUID = -6757742013238625595L;
public BadParameterException(String info) {
super(HttpStatus.BAD_REQUEST, "Fehlerhafter Parameter angegeben!", info);
}
}

View File

@ -7,7 +7,7 @@ public class BadPayloadException extends EventException {
private static final long serialVersionUID = -3978242017847155629L; private static final long serialVersionUID = -3978242017847155629L;
public BadPayloadException(String info) { 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

@ -7,7 +7,7 @@ public class GroupFullException extends EventException {
private static final long serialVersionUID = -4011141160467668713L; private static final long serialVersionUID = -4011141160467668713L;
public GroupFullException(String info) { public GroupFullException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Die Gruppe hat die maximale Mitgliederanzahl bereits erreicht!", info); super(HttpStatus.INTERNAL_SERVER_ERROR, "Gruppe hat maximale Teilnehmeranzahl bereits erreicht.", info);
} }
} }

View File

@ -1,13 +0,0 @@
package mops.gruppen2.domain.exception;
import org.springframework.http.HttpStatus;
public class GroupIdMismatchException extends EventException {
private static final long serialVersionUID = 7944077617758922089L;
public GroupIdMismatchException(String info) {
super(HttpStatus.INTERNAL_SERVER_ERROR, "Falsche Gruppe für Event.", info);
}
}

View File

@ -7,7 +7,7 @@ public class GroupNotFoundException extends EventException {
private static final long serialVersionUID = -4738218416842951106L; private static final long serialVersionUID = -4738218416842951106L;
public GroupNotFoundException(String info) { 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

@ -7,7 +7,7 @@ public class InvalidInviteException extends EventException {
private static final long serialVersionUID = 2643001101459427944L; private static final long serialVersionUID = 2643001101459427944L;
public InvalidInviteException(String info) { public InvalidInviteException(String info) {
super(HttpStatus.NOT_FOUND, "Der Einladungslink ist ungültig oder die Gruppe wurde gelöscht.", 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

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

View File

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

View File

@ -7,7 +7,7 @@ public class PageNotFoundException extends EventException {
private static final long serialVersionUID = 2374509005158710104L; private static final long serialVersionUID = 2374509005158710104L;
public PageNotFoundException(String info) { 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

@ -7,7 +7,7 @@ public class UserAlreadyExistsException extends EventException {
private static final long serialVersionUID = -8150634358760194625L; private static final long serialVersionUID = -8150634358760194625L;
public UserAlreadyExistsException(String info) { 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

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

View File

@ -7,7 +7,7 @@ public class WrongFileException extends EventException {
private static final long serialVersionUID = -166192514348555116L; private static final long serialVersionUID = -166192514348555116L;
public WrongFileException(String info) { public WrongFileException(String info) {
super(HttpStatus.BAD_REQUEST, "Die 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; package mops.gruppen2.domain.helper;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.web.api.GroupRequestWrapper; import mops.gruppen2.web.api.GroupRequestWrapper;
import java.util.List; import java.util.List;
//TODO: sinnvolles format
@Log4j2 @Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class APIHelper { public final class APIHelper {
private APIHelper() {}
public static GroupRequestWrapper wrap(long status, List<Group> groupList) { public static GroupRequestWrapper wrap(long status, List<Group> groupList) {
return new GroupRequestWrapper(status, groupList); return new GroupRequestWrapper(status, groupList);
} }

View File

@ -0,0 +1,35 @@
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());
}
}

View File

@ -3,10 +3,12 @@ package mops.gruppen2.domain.helper;
import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvMapper; import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema; import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.WrongFileException; import mops.gruppen2.domain.exception.WrongFileException;
import mops.gruppen2.domain.model.User; import mops.gruppen2.domain.model.group.User;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
@ -16,10 +18,9 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Log4j2 @Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CsvHelper { public final class CsvHelper {
private CsvHelper() {}
public static List<User> readCsvFile(MultipartFile file) throws EventException { public static List<User> readCsvFile(MultipartFile file) throws EventException {
if (file == null || file.isEmpty()) { if (file == null || file.isEmpty()) {
return Collections.emptyList(); 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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; 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 lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.Event; 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. * Übersetzt JSON-Event-Payloads zu Java-Event-Repräsentationen und zurück.
*/ */
@Log4j2 @Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class JsonHelper { public final class JsonHelper {
private JsonHelper() {}
/** /**
* Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload. * Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload.
* *
@ -24,7 +26,7 @@ public final class JsonHelper {
*/ */
public static String serializeEvent(Event event) throws JsonProcessingException { public static String serializeEvent(Event event) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
String payload = mapper.writeValueAsString(event); String payload = mapper.writeValueAsString(event);
log.trace(payload); log.trace(payload);
return payload; return payload;
@ -40,7 +42,9 @@ public final class JsonHelper {
* @throws JsonProcessingException Bei JSON Fehler * @throws JsonProcessingException Bei JSON Fehler
*/ */
public static Event deserializeEvent(String json) throws JsonProcessingException { public static Event deserializeEvent(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
return mapper.readValue(json, Event.class); Event event = mapper.readValue(json, Event.class);
log.trace(event);
return event;
} }
} }

View File

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

View File

@ -1,39 +0,0 @@
package mops.gruppen2.domain.model;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.HashMap;
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 groupid;
@ToString.Exclude
private UUID parent;
private Type type;
private Title title;
private Description description;
@ToString.Exclude
private Limit limit = new Limit(1); // Add initial user
@ToString.Exclude
private final Map<String, User> members = new HashMap<>();
@ToString.Exclude
private final Map<String, Role> roles = new HashMap<>();
}

View File

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

View File

@ -0,0 +1,293 @@
package mops.gruppen2.domain.model.group;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.exception.GroupFullException;
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.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)
@ToString(onlyExplicitlyIncluded = true)
public class Group {
// Metainformationen
@EqualsAndHashCode.Include
@ToString.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();
@ToString.Include
private GroupMeta meta = GroupMeta.EMPTY();
private GroupOptions options = GroupOptions.DEFAULT();
//@ToString.Exclude
//private LocalDateTime age;
// Inhalt
private Title title;
private Description description;
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 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.getGroupid();
}
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 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 {
ValidationHelper.throwIfNoAdmin(this, userid);
groupid = null;
parent = null;
type = null;
title = null;
description = null;
limit = null;
memberships = null;
link = null;
meta = null;
options = null;
body = null;
}
public String format() {
return title + " " + description;
}
}

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

@ -1,4 +1,4 @@
package mops.gruppen2.domain.model; package mops.gruppen2.domain.model.group;
public enum Type { public enum Type {
PUBLIC, PUBLIC,

View File

@ -1,26 +1,32 @@
package mops.gruppen2.domain.model; package mops.gruppen2.domain.model.group;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.Getter;
import lombok.Value; import lombok.Value;
import lombok.extern.log4j.Log4j2;
import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@Log4j2
@Value @Value
@AllArgsConstructor @AllArgsConstructor
@ToString
public class User { public class User {
@EqualsAndHashCode.Include @EqualsAndHashCode.Include
@Getter(AccessLevel.NONE)
@JsonProperty("id")
String userid; String userid;
@JsonProperty("givenname")
String givenname; String givenname;
@ToString.Exclude @JsonProperty("familyname")
String familyname; String familyname;
@ToString.Exclude @JsonProperty("mail")
String email; String email;
public User(KeycloakAuthenticationToken token) { public User(KeycloakAuthenticationToken token) {
@ -42,4 +48,12 @@ public class User {
familyname = ""; familyname = "";
email = ""; 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

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

View File

@ -1,21 +1,27 @@
package mops.gruppen2.domain.model; package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value; import lombok.Value;
import javax.validation.constraints.Max; import javax.validation.constraints.Max;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Value @Value
public class Limit { public class Limit {
@NotNull
@Min(1) @Min(1)
@Max(999_999) @Max(999_999)
@JsonProperty("limit") @JsonProperty("value")
long userLimit; long value;
public static Limit DEFAULT() {
return new Limit(1);
}
@Override @Override
public String toString() { public String toString() {
return String.valueOf(userLimit); 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,34 @@
package mops.gruppen2.domain.model.group.wrapper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
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
public class Parent {
@NotNull
@JsonProperty("id")
UUID groupid;
@ConstructorProperties("id")
public Parent(@NotBlank @Size(min = 36, max = 36) String parentid) {
groupid = UUID.fromString(parentid);
}
public static Parent EMPTY() {
return new Parent("00000000-0000-0000-0000-000000000000");
}
@JsonIgnore
public boolean isEmpty() {
return CommonHelper.uuidIsEmpty(groupid);
}
}

View File

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

View File

@ -1,35 +1,40 @@
package mops.gruppen2.domain.service; package mops.gruppen2.domain.service;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
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.CreateGroupEvent;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.EventType;
import mops.gruppen2.domain.exception.BadPayloadException; import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.domain.helper.IdHelper; import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.helper.JsonHelper; import mops.gruppen2.domain.helper.JsonHelper;
import mops.gruppen2.domain.model.User;
import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO; import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; 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.helper.CommonHelper.eventTypesToString;
import static mops.gruppen2.domain.helper.CommonHelper.uuidsToString;
@Log4j2 @Log4j2
@RequiredArgsConstructor
@Service
@TraceMethodCalls
public class EventStoreService { public class EventStoreService {
private final EventRepository eventStore; private final EventRepository eventStore;
public EventStoreService(EventRepository eventStore) {
this.eventStore = eventStore;
}
//########################################### SAVE ########################################### //########################################### SAVE ###########################################
@ -67,7 +72,7 @@ public class EventStoreService {
//########################################### DTOs ########################################### //########################################### DTOs ###########################################
static List<EventDTO> getDTOsFromEvents(List<Event> events) { private static List<EventDTO> getDTOsFromEvents(List<Event> events) {
return events.stream() return events.stream()
.map(EventStoreService::getDTOFromEvent) .map(EventStoreService::getDTOFromEvent)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -80,13 +85,15 @@ public class EventStoreService {
* *
* @return EventDTO (Neues DTO) * @return EventDTO (Neues DTO)
*/ */
static EventDTO getDTOFromEvent(Event event) { private static EventDTO getDTOFromEvent(Event event) {
try { try {
String payload = JsonHelper.serializeEvent(event); String payload = JsonHelper.serializeEvent(event);
return new EventDTO(null, return new EventDTO(null,
event.getGroupid().toString(), event.getGroupid().toString(),
event.getUserid(), event.getVersion(),
getEventType(event), event.getExec(),
event.getTarget(),
event.getType(),
payload); payload);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
log.error("Event ({}) konnte nicht serialisiert werden!", event, e); log.error("Event ({}) konnte nicht serialisiert werden!", event, e);
@ -116,19 +123,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 ########################################## // ######################################## QUERIES ##########################################
@ -176,8 +170,8 @@ public class EventStoreService {
* @return GruppenIds (UUID) als Liste * @return GruppenIds (UUID) als Liste
*/ */
List<UUID> findExistingGroupIds() { List<UUID> findExistingGroupIds() {
List<Event> createEvents = findLatestEventsFromGroupsByType("CreateGroupEvent", List<Event> createEvents = findLatestEventsFromGroupsByType(CREATEGROUP,
"DeleteGroupEvent"); DESTROYGROUP);
return createEvents.stream() return createEvents.stream()
.filter(event -> event instanceof CreateGroupEvent) .filter(event -> event instanceof CreateGroupEvent)
@ -188,11 +182,18 @@ public class EventStoreService {
/** /**
* Liefert Gruppen-Ids von existierenden (ungelöschten) Gruppen, in welchen der User teilnimmt. * Liefert Gruppen-Ids von existierenden (ungelöschten) Gruppen, in welchen der User teilnimmt.
* *
* <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 * @return GruppenIds (UUID) als Liste
*/ */
public List<UUID> findExistingUserGroups(User user) { public List<UUID> findExistingUserGroups(String userid) {
List<Event> userEvents = findLatestEventsFromGroupsByUser(user); List<Event> userEvents = findLatestEventsFromGroupsByUser(userid);
List<UUID> deletedIds = findLatestEventsFromGroupsByType("DeleteGroupEvent") List<UUID> deletedIds = findLatestEventsFromGroupsByType(DESTROYGROUP)
.stream() .stream()
.map(Event::getGroupid) .map(Event::getGroupid)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -200,11 +201,25 @@ public class EventStoreService {
userEvents.removeIf(event -> deletedIds.contains(event.getGroupid())); userEvents.removeIf(event -> deletedIds.contains(event.getGroupid()));
return userEvents.stream() return userEvents.stream()
.filter(event -> event instanceof AddUserEvent) .filter(event -> event instanceof AddMemberEvent)
.map(Event::getGroupid) .map(Event::getGroupid)
.collect(Collectors.toList()); .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 ####################################### // #################################### SIMPLE QUERIES #######################################
@ -219,32 +234,32 @@ public class EventStoreService {
return eventStore.findMaxEventId(); return eventStore.findMaxEventId();
} catch (NullPointerException e) { } catch (NullPointerException e) {
log.debug("Keine Events vorhanden!"); log.debug("Keine Events vorhanden!");
return 0; return 1;
} }
} }
List<Event> findEventsByType(String... types) { List<Event> findEventsByType(String... types) {
return getEventsFromDTOs(eventStore.findEventDTOsByType(Arrays.asList(types))); return getEventsFromDTOs(eventStore.findEventDTOsByType(types));
} }
List<Event> findEventsByType(String type) { List<Event> findEventsByType(String type) {
return getEventsFromDTOs(eventStore.findEventDTOsByType(Collections.singletonList(type))); return getEventsFromDTOs(eventStore.findEventDTOsByType(type));
} }
List<Event> findEventsByGroupAndType(List<UUID> groupIds, String... types) { List<Event> findEventsByGroupAndType(List<UUID> groupIds, String... types) {
return getEventsFromDTOs(eventStore.findEventDTOsByGroupAndType(Arrays.asList(types), return getEventsFromDTOs(eventStore.findEventDTOsByGroupAndType(uuidsToString(groupIds),
IdHelper.uuidsToString(groupIds))); types));
} }
/** /**
* Sucht zu jeder Gruppe das letzte Add- oder DeleteUserEvent heraus, welches den übergebenen User betrifft. * 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 * @return Eine Liste von einem Add- oder DeleteUserEvent pro Gruppe
*/ */
private List<Event> findLatestEventsFromGroupsByUser(User user) { private List<Event> findLatestEventsFromGroupsByUser(String userid) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByUser(user.getUserid())); return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByUser(userid));
} }
@ -255,7 +270,7 @@ public class EventStoreService {
* *
* @return Eine Liste von einem Event pro Gruppe * @return Eine Liste von einem Event pro Gruppe
*/ */
private List<Event> findLatestEventsFromGroupsByType(String... types) { private List<Event> findLatestEventsFromGroupsByType(EventType... types) {
return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(Arrays.asList(types))); return getEventsFromDTOs(eventStore.findLatestEventDTOsPartitionedByGroupByType(eventTypesToString(types)));
} }
} }

View File

@ -1,26 +1,33 @@
package mops.gruppen2.domain.service; package mops.gruppen2.domain.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.AddUserEvent; import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.CreateGroupEvent;
import mops.gruppen2.domain.event.DeleteGroupEvent; import mops.gruppen2.domain.event.DestroyGroupEvent;
import mops.gruppen2.domain.event.DeleteUserEvent;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.UpdateGroupDescriptionEvent; import mops.gruppen2.domain.event.KickMemberEvent;
import mops.gruppen2.domain.event.UpdateGroupTitleEvent; 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.UpdateRoleEvent;
import mops.gruppen2.domain.event.UpdateUserLimitEvent;
import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.helper.ValidationHelper; import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.Description; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Role;
import mops.gruppen2.domain.model.Limit; import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.Role; import mops.gruppen2.domain.model.group.User;
import mops.gruppen2.domain.model.Title; import mops.gruppen2.domain.model.group.wrapper.Description;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.wrapper.Limit;
import mops.gruppen2.domain.model.User; 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 org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -28,51 +35,47 @@ import java.util.UUID;
* Behandelt Aufgaben, welche sich auf eine Gruppe beziehen. * Behandelt Aufgaben, welche sich auf eine Gruppe beziehen.
* Es werden übergebene Gruppen bearbeitet und dementsprechend Events erzeugt und gespeichert. * Es werden übergebene Gruppen bearbeitet und dementsprechend Events erzeugt und gespeichert.
*/ */
@Service
@Log4j2 @Log4j2
@RequiredArgsConstructor
@Service
public class GroupService { public class GroupService {
private final EventStoreService eventStoreService; private final EventStoreService eventStoreService;
private final InviteService inviteService;
public GroupService(EventStoreService eventStoreService, InviteService inviteService) {
this.eventStoreService = eventStoreService;
this.inviteService = inviteService;
}
// ################################# GRUPPE ERSTELLEN ######################################## // ################################# GRUPPE ERSTELLEN ########################################
/** public Group createGroup(String exec) {
* Erzeugt eine neue Gruppe und erzeugt nötige Events für die Initiale Setzung der Attribute. return createGroup(UUID.randomUUID(), exec, LocalDateTime.now());
* }
* @param user Keycloak-Account
* @param title Gruppentitel
* @param description Gruppenbeschreibung
*/
public Group createGroup(User user,
Title title,
Description description,
Type type,
Limit userLimit,
UUID parent) {
// Regeln: public void initGroupMembers(Group group,
// isPrivate -> !isLecture String exec,
// isLecture -> !isPrivate String target,
Group group = createGroup(user, parent, type); User user,
Limit limit) {
// Die Reihenfolge ist wichtig, da der ausführende User Admin sein muss addMember(group, exec, target, user);
addUser(user, group); updateRole(group, exec, target, Role.ADMIN);
updateRole(user, group, Role.ADMIN); setLimit(group, exec, limit);
updateTitle(user, group, title); }
updateDescription(user, group, description);
updateUserLimit(user, group, userLimit);
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);
} }
@ -87,12 +90,12 @@ public class GroupService {
* *
* @param newUsers Userliste * @param newUsers Userliste
* @param group Gruppe * @param group Gruppe
* @param user Ausführender User * @param exec Ausführender User
*/ */
public void addUsersToGroup(List<User> newUsers, Group group, User user) { public void addUsersToGroup(Group group, String exec, List<User> newUsers) {
updateUserLimit(user, group, getAdjustedUserLimit(newUsers, group)); setLimit(group, exec, getAdjustedUserLimit(newUsers, group));
newUsers.forEach(newUser -> addUserSilent(newUser, group)); newUsers.forEach(newUser -> addUserSilent(group, exec, newUser.getId(), newUser));
} }
/** /**
@ -106,24 +109,20 @@ public class GroupService {
* @return Das neue Teilnehmermaximum * @return Das neue Teilnehmermaximum
*/ */
private static Limit getAdjustedUserLimit(List<User> newUsers, Group group) { private static Limit getAdjustedUserLimit(List<User> newUsers, Group group) {
return new Limit(Math.max((long) group.getMembers().size() + newUsers.size(), group.getLimit().getUserLimit())); return new Limit(Math.max((long) group.size() + newUsers.size(), group.getLimit()));
} }
/** /**
* Wechselt die Rolle eines Teilnehmers von Admin zu Member oder andersherum. * 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. * Überprüft, ob der User Mitglied ist und ob er der letzte Admin ist.
* *
* @param user Teilnehmer, welcher geändert wird * @param target Teilnehmer, welcher geändert wird
* @param group Gruppe, in welcher sih der Teilnehmer befindet * @param group Gruppe, in welcher sih der Teilnehmer befindet
* *
* @throws EventException Falls der User nicht gefunden wird * @throws EventException Falls der User nicht gefunden wird
*/ */
public void toggleMemberRole(User user, Group group) throws EventException { public void toggleMemberRole(Group group, String exec, String target) {
ValidationHelper.throwIfNoMember(group, user); updateRole(group, exec, target, group.getRole(target).toggle());
ValidationHelper.throwIfLastAdmin(user, group);
Role role = group.getRoles().get(user.getUserid());
updateRole(user, group, role.toggle());
} }
@ -134,59 +133,44 @@ public class GroupService {
/** /**
* Erzeugt eine Gruppe, speichert diese und gibt diese zurück. * Erzeugt eine Gruppe, speichert diese und gibt diese zurück.
*/ */
private Group createGroup(User user, UUID parent, Type type) { private Group createGroup(UUID groupid, String exec, LocalDateTime date) {
Event event = new CreateGroupEvent(UUID.randomUUID(), Event event = new CreateGroupEvent(groupid,
user, exec,
parent, date);
type);
Group group = new Group(); Group group = new Group();
event.apply(group); applyAndSave(group, event);
eventStoreService.saveEvent(event);
return group; 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. * 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 { try {
addUser(user, group); addMember(group, exec, target, user);
} catch (Exception e) { } catch (Exception e) {
log.debug("Doppelter User {} wurde nicht zu Gruppe {} hinzugefügt!", user, group); 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, exec, target, user));
}
/** /**
* Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an. * Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist. * Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist.
*/ */
public void deleteUser(User user, Group group) throws EventException { public void deleteUser(Group group, String exec, String target) {
ValidationHelper.throwIfNoMember(group, user); applyAndSave(group, new KickMemberEvent(group, exec, target));
ValidationHelper.throwIfLastAdmin(user, group);
if (ValidationHelper.checkIfGroupEmpty(group)) { if (ValidationHelper.checkIfGroupEmpty(group)) {
deleteGroup(user, group); deleteGroup(group, target);
} else {
Event event = new DeleteUserEvent(group, user);
event.apply(group);
eventStoreService.saveEvent(event);
} }
} }
@ -194,14 +178,8 @@ public class GroupService {
* Erzeugt, speichert ein DeleteGroupEvent und wendet es auf eine Gruppe an. * Erzeugt, speichert ein DeleteGroupEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Admin ist. * Prüft, ob der Nutzer Admin ist.
*/ */
public void deleteGroup(User user, Group group) { public void deleteGroup(Group group, String exec) {
ValidationHelper.throwIfNoAdmin(group, user); applyAndSave(group, new DestroyGroupEvent(group, exec));
Event event = new DeleteGroupEvent(group, user);
event.apply(group);
inviteService.destroyLink(group);
eventStoreService.saveEvent(event);
} }
/** /**
@ -209,17 +187,8 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist und ob der Titel valide ist. * Prüft, ob der Nutzer Admin ist und ob der Titel valide ist.
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
public void updateTitle(User user, Group group, Title title) { public void setTitle(Group group, String exec, Title title) {
ValidationHelper.throwIfNoAdmin(group, user); applyAndSave(group, new SetTitleEvent(group, exec, title));
if (title.equals(group.getTitle())) {
return;
}
Event event = new UpdateGroupTitleEvent(group, user, title);
event.apply(group);
eventStoreService.saveEvent(event);
} }
/** /**
@ -227,17 +196,8 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist und ob die Beschreibung valide ist. * Prüft, ob der Nutzer Admin ist und ob die Beschreibung valide ist.
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
public void updateDescription(User user, Group group, Description description) { public void setDescription(Group group, String exec, Description description) {
ValidationHelper.throwIfNoAdmin(group, user); applyAndSave(group, new SetDescriptionEvent(group, exec, description));
if (description.equals(group.getDescription())) {
return;
}
Event event = new UpdateGroupDescriptionEvent(group, user, description);
event.apply(group);
eventStoreService.saveEvent(event);
} }
/** /**
@ -245,17 +205,8 @@ public class GroupService {
* Prüft, ob der Nutzer Mitglied ist. * Prüft, ob der Nutzer Mitglied ist.
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
private void updateRole(User user, Group group, Role role) { private void updateRole(Group group, String exec, String target, Role role) {
ValidationHelper.throwIfNoMember(group, user); applyAndSave(group, new UpdateRoleEvent(group, exec, target, role));
if (role == group.getRoles().get(user.getUserid())) {
return;
}
Event event = new UpdateRoleEvent(group, user, role);
event.apply(group);
eventStoreService.saveEvent(event);
} }
/** /**
@ -263,20 +214,24 @@ public class GroupService {
* Prüft, ob der Nutzer Admin ist und ob das Limit valide ist. * Prüft, ob der Nutzer Admin ist und ob das Limit valide ist.
* Bei keiner Änderung wird nichts erzeugt. * Bei keiner Änderung wird nichts erzeugt.
*/ */
public void updateUserLimit(User user, Group group, Limit userLimit) { public void setLimit(Group group, String exec, Limit userLimit) {
ValidationHelper.throwIfNoAdmin(group, user); applyAndSave(group, new SetLimitEvent(group, exec, userLimit));
}
if (userLimit == group.getLimit()) { public void setParent(Group group, String exec, Parent parent) {
return; applyAndSave(group, new SetParentEvent(group, exec, parent));
} }
Event event; public void setLink(Group group, String exec, Link link) {
if (userLimit.getUserLimit() < group.getMembers().size()) { applyAndSave(group, new SetInviteLinkEvent(group, exec, link));
event = new UpdateUserLimitEvent(group, user, new Limit(group.getMembers().size())); }
} else {
event = new UpdateUserLimitEvent(group, user, userLimit);
}
private void setType(Group group, String exec, Type type) {
applyAndSave(group, new SetTypeEvent(group, exec, type));
}
private void applyAndSave(Group group, Event event) throws EventException {
event.init(group.version() + 1);
event.apply(group); event.apply(group);
eventStoreService.saveEvent(event); eventStoreService.saveEvent(event);

View File

@ -1,54 +0,0 @@
package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.InvalidInviteException;
import mops.gruppen2.domain.exception.NoInviteExistException;
import mops.gruppen2.domain.model.Group;
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.getGroupid().toString(),
UUID.randomUUID().toString()));
log.debug("Link wurde erzeugt! (Gruppe: {})", group.getGroupid());
}
void destroyLink(Group group) {
inviteRepository.deleteLinkOfGroup(group.getGroupid().toString());
log.debug("Link wurde zerstört! (Gruppe: {})", group.getGroupid());
}
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.getGroupid().toString());
} catch (Exception e) {
log.error("Link zu Gruppe ({}) konnte nicht gefunden werden!", group.getGroupid(), e);
throw new NoInviteExistException(group.getGroupid().toString());
}
}
}

View File

@ -1,37 +1,41 @@
package mops.gruppen2.domain.service; package mops.gruppen2.domain.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupNotFoundException; import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.domain.helper.IdHelper; import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.User;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static mops.gruppen2.domain.event.EventType.CREATEGROUP;
import static mops.gruppen2.domain.event.EventType.SETDESCRIPTION;
import static mops.gruppen2.domain.event.EventType.SETLIMIT;
import static mops.gruppen2.domain.event.EventType.SETTITLE;
import static mops.gruppen2.domain.helper.CommonHelper.eventTypesToString;
/** /**
* Liefert verschiedene Projektionen auf Gruppen. * Liefert verschiedene Projektionen auf Gruppen.
* Benötigt ausschließlich den EventStoreService. * Benötigt ausschließlich den EventStoreService.
*/ */
@Service
@Log4j2 @Log4j2
@RequiredArgsConstructor
@Service
public class ProjectionService { public class ProjectionService {
private final EventStoreService eventStoreService; private final EventStoreService eventStoreService;
public ProjectionService(EventStoreService eventStoreService) {
this.eventStoreService = eventStoreService;
}
// ################################## STATISCHE PROJEKTIONEN ################################# // ################################## STATISCHE PROJEKTIONEN #################################
@ -121,11 +125,16 @@ public class ProjectionService {
@Cacheable("groups") @Cacheable("groups")
public List<Group> projectPublicGroups() throws EventException { public List<Group> projectPublicGroups() throws EventException {
List<UUID> groupIds = eventStoreService.findExistingGroupIds(); List<UUID> groupIds = eventStoreService.findExistingGroupIds();
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds, List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds,
"CreateGroupEvent", eventTypesToString(CREATEGROUP,
"UpdateGroupDescriptionEvent", SETDESCRIPTION,
"UpdateGroupTitleEvent", SETTITLE,
"UpdateUserMaxEvent"); SETLIMIT));
List<Group> groups = projectGroups(events); List<Group> groups = projectGroups(events);
@ -143,9 +152,14 @@ public class ProjectionService {
@Cacheable("groups") @Cacheable("groups")
public List<Group> projectLectures() { public List<Group> projectLectures() {
List<UUID> groupIds = eventStoreService.findExistingGroupIds(); List<UUID> groupIds = eventStoreService.findExistingGroupIds();
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds, List<Event> events = eventStoreService.findEventsByGroupAndType(groupIds,
"CreateGroupEvent", eventTypesToString(CREATEGROUP,
"UpdateGroupTitleEvent"); SETTITLE));
List<Group> lectures = projectGroups(events); List<Group> lectures = projectGroups(events);
@ -158,17 +172,22 @@ public class ProjectionService {
* Projiziert Gruppen, in welchen der User aktuell teilnimmt. * Projiziert Gruppen, in welchen der User aktuell teilnimmt.
* Die Gruppen enthalten nur Metainformationen: Titel und Beschreibung. * Die Gruppen enthalten nur Metainformationen: Titel und Beschreibung.
* *
* @param user Die Id * @param userid Die Id
* *
* @return Liste aus Gruppen * @return Liste aus Gruppen
*/ */
@Cacheable("groups") @Cacheable("groups")
public List<Group> projectUserGroups(User user) { public List<Group> projectUserGroups(String userid) {
List<UUID> groupIds = eventStoreService.findExistingUserGroups(user); List<UUID> groupIds = eventStoreService.findExistingUserGroups(userid);
if (groupIds.isEmpty()) {
return Collections.emptyList();
}
List<Event> groupEvents = eventStoreService.findEventsByGroupAndType(groupIds, List<Event> groupEvents = eventStoreService.findEventsByGroupAndType(groupIds,
"CreateGroupEvent", eventTypesToString(CREATEGROUP,
"UpdateGroupTitleEvent", SETTITLE,
"UpdateGroupDescriptionEvent"); SETDESCRIPTION));
return projectGroups(groupEvents); return projectGroups(groupEvents);
} }
@ -185,10 +204,6 @@ public class ProjectionService {
* @throws GroupNotFoundException Wenn die Gruppe nicht gefunden wird * @throws GroupNotFoundException Wenn die Gruppe nicht gefunden wird
*/ */
public Group projectSingleGroup(UUID groupId) throws GroupNotFoundException { public Group projectSingleGroup(UUID groupId) throws GroupNotFoundException {
if (IdHelper.isEmpty(groupId)) {
throw new GroupNotFoundException(groupId + ": " + ProjectionService.class);
}
try { try {
List<Event> events = eventStoreService.findGroupEvents(groupId); List<Event> events = eventStoreService.findGroupEvents(groupId);
return projectSingleGroup(events); return projectSingleGroup(events);
@ -198,27 +213,28 @@ public class ProjectionService {
} }
} }
/** public Group projectParent(UUID parent) {
* Projiziert eine einzelne Gruppe, welche leer sein darf. if (CommonHelper.uuidIsEmpty(parent)) {
*/
public Group projectParent(UUID parentId) {
if (IdHelper.isEmpty(parentId)) {
return new Group(); return new Group();
} }
return projectSingleGroup(parentId); return projectSingleGroup(parent);
} }
/** /**
* Entfernt alle Gruppen, in welchen ein User teilnimmt, aus einer Gruppenliste. * Entfernt alle Gruppen, in welchen ein User teilnimmt, aus einer Gruppenliste.
* *
* @param groups Gruppenliste, aus der entfernt wird * @param groups Gruppenliste, aus der entfernt wird
* @param user User, welcher teilnimmt * @param userid User, welcher teilnimmt
*/ */
void removeUserGroups(List<Group> groups, User user) { void removeUserGroups(List<Group> groups, String userid) {
List<UUID> userGroups = eventStoreService.findExistingUserGroups(user); List<UUID> userGroups = eventStoreService.findExistingUserGroups(userid);
groups.removeIf(group -> userGroups.contains(group.getGroupid())); groups.removeIf(group -> userGroups.contains(group.getId()));
}
public Group projectGroupByLink(String link) {
return projectSingleGroup(eventStoreService.findGroupByLink(link));
} }
} }

View File

@ -2,9 +2,8 @@ package mops.gruppen2.domain.service;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.EventException; import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.SortHelper;
import mops.gruppen2.domain.model.User;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -33,10 +32,10 @@ public class SearchService {
* @throws EventException Projektionsfehler * @throws EventException Projektionsfehler
*/ */
@Cacheable("groups") @Cacheable("groups")
public List<Group> searchPublicGroups(String search, User user) throws EventException { public List<Group> searchPublicGroups(String search, String userid) {
List<Group> groups = projectionService.projectPublicGroups(); List<Group> groups = projectionService.projectPublicGroups();
projectionService.removeUserGroups(groups, user); projectionService.removeUserGroups(groups, userid);
sortByGroupType(groups); SortHelper.sortByGroupType(groups);
if (search.isEmpty()) { if (search.isEmpty()) {
return groups; return groups;
@ -45,25 +44,8 @@ public class SearchService {
log.debug("Es wurde gesucht nach: {}", search); log.debug("Es wurde gesucht nach: {}", search);
return groups.stream() return groups.stream()
.filter(group -> group.toString().toLowerCase().contains(search.toLowerCase())) .filter(group -> group.format().toLowerCase().contains(search.toLowerCase()))
.collect(Collectors.toList()); .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() == Type.LECTURE) {
return -1;
}
if (g2.getType() == Type.LECTURE) {
return 0;
}
return 1;
});
}
} }

View File

@ -30,28 +30,28 @@ public interface EventRepository extends CrudRepository<EventDTO, Long> {
@Query("SELECT * FROM event" @Query("SELECT * FROM event"
+ " WHERE group_id IN (:userIds) ") + " WHERE group_id IN (:userIds) ")
List<EventDTO> findEventDTOsByUser(@Param("groupIds") List<String> userIds); List<EventDTO> findEventDTOsByUser(@Param("groupIds") String... userIds);
@Query("SELECT * FROM event" @Query("SELECT * FROM event"
+ " WHERE event_type IN (:types)") + " WHERE event_type IN (:types)")
List<EventDTO> findEventDTOsByType(@Param("types") List<String> types); List<EventDTO> findEventDTOsByType(@Param("types") String... types);
@Query("SELECT * FROM event" @Query("SELECT * FROM event"
+ " WHERE event_type IN (:types) AND group_id IN (:groupIds)") + " WHERE event_type IN (:types) AND group_id IN (:groupIds)")
List<EventDTO> findEventDTOsByGroupAndType(@Param("types") List<String> types, List<EventDTO> findEventDTOsByGroupAndType(@Param("groupIds") List<String> groupIds,
@Param("groupIds") List<String> groupIds); @Param("types") String... types);
@Query("SELECT * FROM event" @Query("SELECT * FROM event"
+ " WHERE event_type IN (:types) AND user_id = :userId") + " WHERE event_type IN (:types) AND user_id = :userId")
List<EventDTO> findEventDTOsByUserAndType(@Param("types") List<String> types, List<EventDTO> findEventDTOsByUserAndType(@Param("userId") String userId,
@Param("userId") String userId); @Param("types") String... types);
// ################################ LATEST EVENT DTOs ######################################## // ################################ LATEST EVENT DTOs ########################################
@Query("WITH ranked_events AS (" @Query("WITH ranked_events AS ("
+ "SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY event_id DESC) AS rn" + "SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY event_id DESC) AS rn"
+ " FROM event" + " FROM event"
+ " WHERE user_id = :userId AND event_type IN ('AddUserEvent', 'DeleteUserEvent')" + " WHERE user_id = :userId AND event_type IN ('ADDMEMBER', 'KICKMEMBER')"
+ ")" + ")"
+ "SELECT * FROM ranked_events WHERE rn = 1;") + "SELECT * FROM ranked_events WHERE rn = 1;")
List<EventDTO> findLatestEventDTOsPartitionedByGroupByUser(@Param("userId") String userId); List<EventDTO> findLatestEventDTOsPartitionedByGroupByUser(@Param("userId") String userId);
@ -62,7 +62,7 @@ public interface EventRepository extends CrudRepository<EventDTO, Long> {
+ " WHERE event_type IN (:types)" + " WHERE event_type IN (:types)"
+ ")" + ")"
+ "SELECT * FROM ranked_events WHERE rn = 1;") + "SELECT * FROM ranked_events WHERE rn = 1;")
List<EventDTO> findLatestEventDTOsPartitionedByGroupByType(@Param("types") List<String> types); List<EventDTO> findLatestEventDTOsPartitionedByGroupByType(@Param("types") String... types);
// ######################################### COUNT ########################################### // ######################################### COUNT ###########################################

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 @Id
Long event_id; Long event_id;
String group_id; String group_id;
String user_id; long group_version;
String exec_user_id;
String target_user_id;
String event_type; String event_type;
String event_payload; 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

@ -7,9 +7,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.helper.APIHelper; import mops.gruppen2.domain.helper.APIHelper;
import mops.gruppen2.domain.helper.IdHelper; import mops.gruppen2.domain.helper.CommonHelper;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.User;
import mops.gruppen2.domain.service.EventStoreService; import mops.gruppen2.domain.service.EventStoreService;
import mops.gruppen2.domain.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.web.api.GroupRequestWrapper; import mops.gruppen2.web.api.GroupRequestWrapper;
@ -60,7 +59,7 @@ public class APIController {
public List<String> getApiUserGroups(@ApiParam("Nutzer-Id") public List<String> getApiUserGroups(@ApiParam("Nutzer-Id")
@PathVariable("id") String userId) { @PathVariable("id") String userId) {
return IdHelper.uuidsToString(eventStoreService.findExistingUserGroups(new User(userId))); return CommonHelper.uuidsToString(eventStoreService.findExistingUserGroups(userId));
} }
/** /**

View File

@ -4,16 +4,16 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.helper.CsvHelper; import mops.gruppen2.domain.helper.CsvHelper;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.helper.ValidationHelper; import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.Description; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.Limit; import mops.gruppen2.domain.model.group.User;
import mops.gruppen2.domain.model.Title; import mops.gruppen2.domain.model.group.wrapper.Description;
import mops.gruppen2.domain.model.User; 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.GroupService;
import mops.gruppen2.domain.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.web.form.CreateForm;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -21,6 +21,8 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; 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.annotation.security.RolesAllowed;
import javax.validation.Valid; import javax.validation.Valid;
@ -49,27 +51,27 @@ public class GroupCreationController {
@PostMapping("/create") @PostMapping("/create")
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postCreateOrga(KeycloakAuthenticationToken token, public String postCreateOrga(KeycloakAuthenticationToken token,
@Valid CreateForm create, @RequestParam("type") Type type,
@Valid Title title, @RequestParam("parent") @Valid Parent parent,
@Valid Description description, @RequestParam("title") @Valid Title title,
@Valid Limit limit) { @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 // Zusätzlicher check: studentin kann keine lecture erstellen
ValidationHelper.validateCreateForm(token, create); ValidationHelper.validateCreateForm(token, type);
User user = new User(token); String principal = token.getName();
Group group = groupService.createGroup(user, Group group = groupService.createGroup(principal);
title, groupService.initGroupMembers(group, principal, principal, new User(token), limit);
description, groupService.initGroupMeta(group, principal, type, parent);
create.getType(), groupService.initGroupText(group, principal, title, description);
limit,
create.getParent());
// ROLE_studentin kann kein CSV importieren // ROLE_studentin kann kein CSV importieren
if (token.getAccount().getRoles().contains("orga")) { 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.getGroupid()); return "redirect:/gruppen2/details/" + group.getId();
} }
} }

View File

@ -4,15 +4,13 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.helper.CsvHelper; import mops.gruppen2.domain.helper.CsvHelper;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.helper.ValidationHelper; import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.Description; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.User;
import mops.gruppen2.domain.model.Limit; import mops.gruppen2.domain.model.group.wrapper.Description;
import mops.gruppen2.domain.model.Title; import mops.gruppen2.domain.model.group.wrapper.Limit;
import mops.gruppen2.domain.model.User; import mops.gruppen2.domain.model.group.wrapper.Title;
import mops.gruppen2.domain.service.GroupService; import mops.gruppen2.domain.service.GroupService;
import mops.gruppen2.domain.service.InviteService;
import mops.gruppen2.domain.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
@ -38,7 +36,6 @@ import java.util.UUID;
@RequestMapping("/gruppen2") @RequestMapping("/gruppen2")
public class GroupDetailsController { public class GroupDetailsController {
private final InviteService inviteService;
private final GroupService groupService; private final GroupService groupService;
private final ProjectionService projectionService; private final ProjectionService projectionService;
@ -48,18 +45,17 @@ public class GroupDetailsController {
Model model, Model model,
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
// Parent Badge // Parent Badge
UUID parentId = group.getParent(); Group parent = projectionService.projectParent(group.getParent());
Group parent = projectionService.projectParent(parentId);
model.addAttribute("group", group); model.addAttribute("group", group);
model.addAttribute("parent", parent); model.addAttribute("parent", parent);
// Detailseite für nicht-Mitglieder // Detailseite für nicht-Mitglieder
if (!ValidationHelper.checkIfMember(group, user)) { if (!ValidationHelper.checkIfMember(group, principal)) {
return "preview"; return "preview";
} }
@ -72,14 +68,14 @@ public class GroupDetailsController {
public String postDetailsJoin(KeycloakAuthenticationToken token, public String postDetailsJoin(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
if (ValidationHelper.checkIfMember(group, user)) { if (ValidationHelper.checkIfMember(group, principal)) {
return "redirect:/gruppen2/details/" + groupId; return "redirect:/gruppen2/details/" + groupId;
} }
groupService.addUser(user, group); groupService.addMember(group, principal, principal, new User(token));
return "redirect:/gruppen2/details/" + groupId; return "redirect:/gruppen2/details/" + groupId;
} }
@ -90,12 +86,10 @@ public class GroupDetailsController {
public String postDetailsLeave(KeycloakAuthenticationToken token, public String postDetailsLeave(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationHelper.throwIfNoMember(group, user); groupService.deleteUser(group, principal, principal);
groupService.deleteUser(user, group);
return "redirect:/gruppen2"; return "redirect:/gruppen2";
} }
@ -107,15 +101,15 @@ public class GroupDetailsController {
HttpServletRequest request, HttpServletRequest request,
@PathVariable("id") String groupId) { @PathVariable("id") String groupId) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
// Invite Link // Invite Link
String actualURL = request.getRequestURL().toString(); String actualURL = request.getRequestURL().toString();
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/")); 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("group", group);
model.addAttribute("link", link); model.addAttribute("link", link);
@ -131,11 +125,11 @@ public class GroupDetailsController {
@Valid Title title, @Valid Title title,
@Valid Description description) { @Valid Description description) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateTitle(user, group, title); groupService.setTitle(group, principal, title);
groupService.updateDescription(user, group, description); groupService.setDescription(group, principal, description);
return "redirect:/gruppen2/details/" + groupId + "/edit"; return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
@ -146,10 +140,10 @@ public class GroupDetailsController {
public String postDetailsEditUserLimit(KeycloakAuthenticationToken token, public String postDetailsEditUserLimit(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@Valid Limit limit) { @Valid Limit limit) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateUserLimit(user, group, limit); groupService.setLimit(group, principal, limit);
return "redirect:/gruppen2/details/" + groupId + "/edit"; return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
@ -161,10 +155,10 @@ public class GroupDetailsController {
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@RequestParam(value = "file", required = false) MultipartFile file) { @RequestParam(value = "file", required = false) MultipartFile file) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(IdHelper.stringToUUID(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.addUsersToGroup(CsvHelper.readCsvFile(file), group, user); groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file));
return "redirect:/gruppen2/details/" + groupId + "/edit"; return "redirect:/gruppen2/details/" + groupId + "/edit";
} }
@ -174,17 +168,17 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditRole(KeycloakAuthenticationToken token, public String postDetailsEditRole(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@PathVariable("userid") String userId) { @PathVariable("userid") String target) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(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 // Falls sich der User selbst die Rechte genommen hat
if (!ValidationHelper.checkIfAdmin(group, user)) { if (!ValidationHelper.checkIfAdmin(group, principal)) {
return "redirect:/gruppen2/details/" + groupId; return "redirect:/gruppen2/details/" + groupId;
} }
@ -196,16 +190,16 @@ public class GroupDetailsController {
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditDelete(KeycloakAuthenticationToken token, public String postDetailsEditDelete(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId, @PathVariable("id") String groupId,
@PathVariable("userid") String userId) { @PathVariable("userid") String target) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationHelper.throwIfNoAdmin(group, user); ValidationHelper.throwIfNoAdmin(group, principal);
// Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen) // Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen)
if (!userId.equals(user.getUserid())) { if (!principal.equals(target)) {
groupService.deleteUser(new User(userId), group); groupService.deleteUser(group, principal, target);
} }
return "redirect:/gruppen2/details/" + groupId + "/edit"; return "redirect:/gruppen2/details/" + groupId + "/edit";
@ -215,12 +209,12 @@ public class GroupDetailsController {
@PostMapping("/details/{id}/edit/destroy") @PostMapping("/details/{id}/edit/destroy")
@CacheEvict(value = "groups", allEntries = true) @CacheEvict(value = "groups", allEntries = true)
public String postDetailsEditDestroy(KeycloakAuthenticationToken token, public String postDetailsEditDestroy(KeycloakAuthenticationToken token,
@PathVariable("id") String groupId) { @PathVariable("id") String groupid) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId)); Group group = projectionService.projectSingleGroup(UUID.fromString(groupid));
groupService.deleteGroup(user, group); groupService.deleteGroup(group, principal);
return "redirect:/gruppen2"; return "redirect:/gruppen2";
} }

View File

@ -4,7 +4,6 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCall; import mops.gruppen2.aspect.annotation.TraceMethodCall;
import mops.gruppen2.domain.exception.PageNotFoundException; import mops.gruppen2.domain.exception.PageNotFoundException;
import mops.gruppen2.domain.model.User;
import mops.gruppen2.domain.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -35,9 +34,8 @@ public class GruppenfindungController {
public String getIndexPage(KeycloakAuthenticationToken token, public String getIndexPage(KeycloakAuthenticationToken token,
Model model) { Model model) {
User user = new User(token); String principal = token.getName();
model.addAttribute("groups", projectionService.projectUserGroups(principal));
model.addAttribute("groups", projectionService.projectUserGroups(user));
return "index"; return "index";
} }

View File

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

View File

@ -4,10 +4,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls; import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.helper.ValidationHelper; import mops.gruppen2.domain.helper.ValidationHelper;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.User;
import mops.gruppen2.domain.service.InviteService;
import mops.gruppen2.domain.service.ProjectionService; import mops.gruppen2.domain.service.ProjectionService;
import mops.gruppen2.domain.service.SearchService; import mops.gruppen2.domain.service.SearchService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@ -31,7 +29,6 @@ import java.util.List;
@RequestMapping("/gruppen2") @RequestMapping("/gruppen2")
public class SearchAndInviteController { public class SearchAndInviteController {
private final InviteService inviteService;
private final ProjectionService projectionService; private final ProjectionService projectionService;
private final SearchService searchService; private final SearchService searchService;
@ -50,8 +47,8 @@ public class SearchAndInviteController {
Model model, Model model,
@RequestParam("string") String search) { @RequestParam("string") String search) {
User user = new User(token); String principal = token.getName();
List<Group> groups = searchService.searchPublicGroups(search, user); List<Group> groups = searchService.searchPublicGroups(search, principal);
model.addAttribute("groups", groups); model.addAttribute("groups", groups);
@ -64,19 +61,19 @@ public class SearchAndInviteController {
Model model, Model model,
@PathVariable("link") String link) { @PathVariable("link") String link) {
User user = new User(token); String principal = token.getName();
Group group = projectionService.projectSingleGroup(inviteService.getGroupIdFromLink(link)); Group group = projectionService.projectGroupByLink(link);
model.addAttribute("group", group); model.addAttribute("group", group);
// Gruppe öffentlich // Gruppe öffentlich
if (group.getType() == Type.PUBLIC) { if (group.getType() == Type.PUBLIC) {
return "redirect:/gruppen2/details/" + group.getGroupid(); return "redirect:/gruppen2/details/" + group.getId();
} }
// Bereits Mitglied // Bereits Mitglied
if (ValidationHelper.checkIfMember(group, user)) { if (ValidationHelper.checkIfMember(group, principal)) {
return "redirect:/gruppen2/details/" + group.getGroupid(); return "redirect:/gruppen2/details/" + group.getId();
} }
return "link"; return "link";

View File

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

View File

@ -1,29 +0,0 @@
package mops.gruppen2.web.form;
import lombok.Data;
import mops.gruppen2.domain.helper.IdHelper;
import mops.gruppen2.domain.model.Type;
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 Type getType() {
return Type.valueOf(type);
}
public UUID getParent() {
return getType() == Type.LECTURE ? IdHelper.emptyUUID() : IdHelper.stringToUUID(parent);
}
}

View File

@ -2,7 +2,7 @@
logging.application.name = gruppen2 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 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 spring.output.ansi.enabled = always
logging.level.mops.gruppen2 = info logging.level.mops.gruppen2 = trace
logging.level.org.springframework.jdbc.core = info logging.level.org.springframework.jdbc.core = info
# Database # Database

View File

@ -2,18 +2,11 @@ DROP TABLE IF EXISTS event;
CREATE TABLE event CREATE TABLE event
( (
event_id INT PRIMARY KEY AUTO_INCREMENT, event_id INT PRIMARY KEY AUTO_INCREMENT,
group_id VARCHAR(36) NOT NULL, group_id VARCHAR(36) NOT NULL,
user_id VARCHAR(50), group_version INT NOT NULL,
event_type VARCHAR(32), exec_user_id VARCHAR(50) NOT NULL,
event_payload VARCHAR(2500) target_user_id VARCHAR(50),
); event_type VARCHAR(32) NOT NULL,
event_payload VARCHAR(2500) NOT NULL
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
); );

View File

@ -27,7 +27,7 @@
<!--Spacer--> <!--Spacer-->
<span class="col"></span> <span class="col"></span>
<form method="post" th:action="@{/gruppen2/details/{id}/leave(id=${group.getGroupid()})}"> <form method="post" th:action="@{/gruppen2/details/{id}/leave(id=${group.getId()})}">
<button class="btn btn-danger btn-bar" type="submit">Gruppe verlassen <button class="btn btn-danger btn-bar" type="submit">Gruppe verlassen
</button> </button>
</form> </form>
@ -40,13 +40,13 @@
<!--Anzahl Text--> <!--Anzahl Text-->
<div class="mb-2"> <div class="mb-2">
<span>Teilnehmer: </span> <span>Teilnehmer: </span>
<span th:text="${group.getMembers().size() + ' von ' + group.getLimit().getUserLimit()}"></span> <span th:text="${group.size() + ' von ' + group.getLimit()}"></span>
</div> </div>
<!--Bearbeiten-Button--> <!--Bearbeiten-Button-->
<div class="mb-2" th:if="${group.getRoles().get(user.getUserid()) == admin}"> <div class="mb-2" th:if="${group.isAdmin(user.getId())}">
<form method="get" <form method="get"
th:action="@{/gruppen2/details/{id}/edit(id=${group.getGroupid()})}"> th:action="@{/gruppen2/details/{id}/edit(id=${group.getId()})}">
<button class="btn btn-secondary btn-block">Gruppe verwalten</button> <button class="btn btn-secondary btn-block">Gruppe verwalten</button>
</form> </form>
</div> </div>
@ -55,8 +55,8 @@
<div class="members"> <div class="members">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item d-flex justify-content-between" <li class="list-group-item d-flex justify-content-between"
th:each="member : ${group.getMembers().values()}"> th:each="member : ${group.getMembers()}">
<span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span> <span th:text="${member}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span> <span th:replace="~{fragments/groups :: userbadges}"></span>
</li> </li>
</ul> </ul>

View File

@ -15,14 +15,14 @@
<!--Fertig oder löschen--> <!--Fertig oder löschen-->
<div class="content"> <div class="content">
<div class="row"> <div class="row">
<form method="get" th:action="@{/gruppen2/details/{id}(id=${group.getGroupid()})}"> <form method="get" th:action="@{/gruppen2/details/{id}(id=${group.getId()})}">
<button class="btn btn-primary">Fertig</button> <button class="btn btn-primary">Fertig</button>
</form> </form>
<!--Spacer--> <!--Spacer-->
<span class="col"></span> <span class="col"></span>
<form method="post" th:action="@{/gruppen2/details/{id}/edit/destroy(id=${group.getGroupid()})}"> <form method="post" th:action="@{/gruppen2/details/{id}/edit/destroy(id=${group.getId()})}">
<button class="btn btn-danger btn-bar" type="submit">Gruppe löschen <button class="btn btn-danger btn-bar" type="submit">Gruppe löschen
</button> </button>
</form> </form>
@ -50,7 +50,7 @@
<!--Beschreibung + Titel--> <!--Beschreibung + Titel-->
<div class="content-text"> <div class="content-text">
<form th:action="@{/gruppen2/details/{id}/edit/meta(id=${group.getGroupid()})}" <form th:action="@{/gruppen2/details/{id}/edit/meta(id=${group.getId()})}"
method="post"> method="post">
<div th:replace="~{fragments/forms :: meta}"></div> <div th:replace="~{fragments/forms :: meta}"></div>
@ -63,7 +63,7 @@
<!--Userlimit--> <!--Userlimit-->
<div class="content-text-in"> <div class="content-text-in">
<form th:action="@{/gruppen2/details/{id}/edit/userlimit(id=${group.getGroupid()})}" <form th:action="@{/gruppen2/details/{id}/edit/userlimit(id=${group.getId()})}"
method="post"> method="post">
<div th:replace="~{fragments/forms :: userlimit}"></div> <div th:replace="~{fragments/forms :: userlimit}"></div>
@ -76,7 +76,7 @@
<!--CSV Import--> <!--CSV Import-->
<div class="content-text mb-0"> <div class="content-text mb-0">
<form th:action="@{/gruppen2/details/{id}/edit/csv(id=${group.getGroupid()})}" <form th:action="@{/gruppen2/details/{id}/edit/csv(id=${group.getId()})}"
th:if="${account.getRoles().contains('orga')}" th:if="${account.getRoles().contains('orga')}"
enctype="multipart/form-data" method="post"> enctype="multipart/form-data" method="post">
@ -96,20 +96,20 @@
</div> </div>
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item d-flex justify-content-between" th:each="member : ${group.getMembers().values()}"> <li class="list-group-item d-flex justify-content-between" th:each="member : ${group.getMembers()}">
<div> <div>
<span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span> <span th:text="${member.getGivenname() + ' ' + member.getFamilyname().charAt(0) + '.'}"></span>
<span th:replace="~{fragments/groups :: userbadges}"></span> <span th:replace="~{fragments/groups :: userbadges}"></span>
</div> </div>
<div class="d-flex"> <div class="d-flex">
<form th:action="@{/gruppen2/details/{id}/edit/delete/{userid}(id=${group.getGroupid()}, userid=${member.getUserid()})}" <form th:action="@{/gruppen2/details/{id}/edit/delete/{userid}(id=${group.getId()}, userid=${member.getId()})}"
th:unless="${member.getUserid() == account.getName()}" th:unless="${member.getId() == account.getName()}"
method="post"> method="post">
<button type="submit" class="btn btn-danger mr-2">Entfernen</button> <button type="submit" class="btn btn-danger mr-2">Entfernen</button>
</form> </form>
<form th:action="@{/gruppen2/details/{id}/edit/role/{userid}(id=${group.getGroupid()}, userid=${member.getUserid()})}" <form th:action="@{/gruppen2/details/{id}/edit/role/{userid}(id=${group.getId()}, userid=${member.getId()})}"
method="post"> method="post">
<button type="submit" class="btn btn-warning">Rolle ändern</button> <button type="submit" class="btn btn-warning">Rolle ändern</button>
</form> </form>

View File

@ -10,7 +10,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text text-monospace">Gruppentitel:</span> <span class="input-group-text text-monospace">Gruppentitel:</span>
</div> </div>
<input type="text" class="form-control" name="groupTitle" minlength="4" maxlength="128" <input type="text" class="form-control" name="title" minlength="4" maxlength="128"
th:value="${group?.getTitle()}" required> th:value="${group?.getTitle()}" required>
</div> </div>
@ -20,8 +20,8 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text text-monospace">Beschreibung:</span> <span class="input-group-text text-monospace">Beschreibung:</span>
</div> </div>
<textarea class="form-control" name="groupDescription" minlength="4" maxlength="512" <textarea class="form-control" name="description" minlength="4" maxlength="512"
th:text="${group?.getDescription()?.getGroupDescription()}" required></textarea> th:text="${group?.getDescription()}" required></textarea>
</div> </div>
</th:block> </th:block>
@ -50,7 +50,7 @@
<input type="hidden" id="parentdummy" name="parent" 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"> <select class="custom-select" id="parentselect" name="parent">
<option value="00000000-0000-0000-0000-000000000000" selected>Keiner</option> <option value="00000000-0000-0000-0000-000000000000" selected>Keiner</option>
<option th:each="lecture : ${lectures}" th:value="${lecture.getGroupid()}" <option th:each="lecture : ${lectures}" th:value="${lecture.getId()}"
th:text="${lecture.getTitle()}"></option> th:text="${lecture.getTitle()}"></option>
</select> </select>
</div> </div>
@ -61,17 +61,17 @@
<th:block th:fragment="userlimit"> <th:block th:fragment="userlimit">
<label for="userlimit">Teilnehmeranzahl:</label> <label for="userlimit">Teilnehmeranzahl:</label>
<div class="btn-toolbar row mx-0" id="userlimit"> <div class="btn-toolbar row mx-0" id="userlimit">
<input type="hidden" name="userLimit" id="limit" value="999999" <input type="hidden" name="limit" id="limit" value="999999"
th:disabled="${group != null && group.getLimit().getUserLimit() < 999999} ? 'disabled' : 'false'"> th:disabled="${group != null && group.getLimit() < 999999} ? 'disabled' : 'false'">
<div class="btn-group btn-group-toggle col-sm-4 px-0" data-toggle="buttons"> <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')"> <label class="btn btn-secondary active" onclick="disable('#limitselect'); enable('#limit')">
<input type="radio" <input type="radio"
th:checked="${group != null && group.getLimit().getUserLimit() < 999999} ? 'false' : 'checked'"> th:checked="${group != null && group.getLimit() < 999999} ? 'false' : 'checked'">
Unbegrenzt Unbegrenzt
</label> </label>
<label class="btn btn-secondary" onclick="enable('#limitselect'); disable('#limit')"> <label class="btn btn-secondary" onclick="enable('#limitselect'); disable('#limit')">
<input type="radio" <input type="radio"
th:checked="${group != null && group.getLimit().getUserLimit() < 999999} ? 'checked' : 'false'"> th:checked="${group != null && group.getLimit() < 999999} ? 'checked' : 'false'">
Begrenzt Begrenzt
</label> </label>
</div> </div>
@ -81,10 +81,10 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text text-monospace">Limit:</span> <span class="input-group-text text-monospace">Limit:</span>
</div> </div>
<input type="number" class="form-control" name="userLimit" <input type="number" class="form-control" name="limit"
th:value="${group != null} ? ${group.getLimit().getUserLimit()} : '999999'" th:value="${group != null} ? ${group.getLimit()} : '999999'"
min="1" max="999999" id="limitselect" required min="1" max="999999" id="limitselect" required
th:disabled="${group != null && group.getLimit().getUserLimit() < 999999} ? 'false' : 'disabled'"> th:disabled="${group != null && group.getLimit() < 999999} ? 'false' : 'disabled'">
<div class="input-group-append"> <div class="input-group-append">
<span class="input-group-text text-monospace">Teilnehmer</span> <span class="input-group-text text-monospace">Teilnehmer</span>
</div> </div>
@ -99,7 +99,7 @@
<span class="input-group-text text-monospace">CSV:</span> <span class="input-group-text text-monospace">CSV:</span>
</div> </div>
<div class="custom-file"> <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> <label class="custom-file-label" for="file">Datei auswählen</label>
</div> </div>
<script> <script>

View File

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

View File

@ -16,13 +16,13 @@
<!--Gruppenliste belegte Gruppen--> <!--Gruppenliste belegte Gruppen-->
<div class="content" th:each="group: ${groups}"> <div class="content" th:each="group: ${groups}">
<div class="content-heading row"> <div class="content-heading row">
<a class="link col" th:href="@{/gruppen2/details/{id}(id=${group.getGroupid()})}" <a class="link col" th:href="@{/gruppen2/details/{id}(id=${group.getId()})}"
th:text="${group.getTitle()}"></a> th:text="${group.getTitle()}"></a>
<span th:replace="~{fragments/groups :: badges}"></span> <span th:replace="~{fragments/groups :: badges}"></span>
</div> </div>
<div class="content-text-in"> <div class="content-text-in">
<span th:text="${group.getDescription().getGroupDescription()}"></span> <span th:text="${group.getDescription()}"></span>
</div> </div>
</div> </div>

View File

@ -14,10 +14,10 @@
<div class="content" th:insert="~{fragments/groups :: groupcontent}"></div> <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> 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> <span class="col"></span>
<a class="btn btn-primary" href="/gruppen2">Startseite.</a> <a class="btn btn-primary" href="/gruppen2">Startseite.</a>

View File

@ -31,11 +31,11 @@
<div class="content-heading row"> <div class="content-heading row">
<span th:replace="~{fragments/groups :: badges}"></span> <span th:replace="~{fragments/groups :: badges}"></span>
<a class="link col" th:href="@{/gruppen2/details/{id}(id=${group.getGroupid()})}" <a class="link col" th:href="@{/gruppen2/details/{id}(id=${group.getId()})}"
th:text="${group.getTitle()}"></a> th:text="${group.getTitle()}"></a>
</div> </div>
<div class="content-text-in"> <div class="content-text-in">
<span th:text="${group.getDescription().getGroupDescription()}"></span> <span th:text="${group.getDescription()}"></span>
</div> </div>
</div> </div>

View File

@ -2,18 +2,17 @@ package mops.gruppen2;
import com.github.javafaker.Faker; import com.github.javafaker.Faker;
import mops.gruppen2.domain.Account; import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.event.AddUserEvent; import mops.gruppen2.domain.event.AddMemberEvent;
import mops.gruppen2.domain.event.CreateGroupEvent; import mops.gruppen2.domain.event.DestroyGroupEvent;
import mops.gruppen2.domain.event.DeleteGroupEvent;
import mops.gruppen2.domain.event.DeleteUserEvent;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.event.UpdateGroupDescriptionEvent; import mops.gruppen2.domain.event.KickMemberEvent;
import mops.gruppen2.domain.event.UpdateGroupTitleEvent; import mops.gruppen2.domain.event.SetDescriptionEvent;
import mops.gruppen2.domain.event.SetLimitEvent;
import mops.gruppen2.domain.event.SetTitleEvent;
import mops.gruppen2.domain.event.UpdateRoleEvent; import mops.gruppen2.domain.event.UpdateRoleEvent;
import mops.gruppen2.domain.event.UpdateUserLimitEvent; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Role;
import mops.gruppen2.domain.model.Role; import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -93,7 +92,7 @@ public class TestBuilder {
eventList.add(createPublicGroupEvent(groupId)); eventList.add(createPublicGroupEvent(groupId));
eventList.add(updateGroupTitleEvent(groupId)); eventList.add(updateGroupTitleEvent(groupId));
eventList.add(updateGroupDescriptionEvent(groupId)); eventList.add(updateGroupDescriptionEvent(groupId));
eventList.add(new UpdateUserLimitEvent(groupId, "fgsadggas", Long.MAX_VALUE)); eventList.add(new SetLimitEvent(groupId, "fgsadggas", Long.MAX_VALUE));
eventList.addAll(addUserEvents(membercount, groupId)); eventList.addAll(addUserEvents(membercount, groupId));
return eventList; return eventList;
@ -106,7 +105,7 @@ public class TestBuilder {
eventList.add(createPrivateGroupEvent(groupId)); eventList.add(createPrivateGroupEvent(groupId));
eventList.add(updateGroupTitleEvent(groupId)); eventList.add(updateGroupTitleEvent(groupId));
eventList.add(updateGroupDescriptionEvent(groupId)); eventList.add(updateGroupDescriptionEvent(groupId));
eventList.add(new UpdateUserLimitEvent(groupId, "fgsadggas", Long.MAX_VALUE)); eventList.add(new SetLimitEvent(groupId, "fgsadggas", Long.MAX_VALUE));
eventList.addAll(addUserEvents(membercount, groupId)); eventList.addAll(addUserEvents(membercount, groupId));
return eventList; return eventList;
@ -207,7 +206,7 @@ public class TestBuilder {
String firstname = firstname(); String firstname = firstname();
String lastname = lastname(); String lastname = lastname();
return new AddUserEvent( return new AddMemberEvent(
groupId, groupId,
userId, userId,
firstname, firstname,
@ -224,13 +223,13 @@ public class TestBuilder {
public static List<Event> deleteUserEvents(int count, List<Event> eventList) { public static List<Event> deleteUserEvents(int count, List<Event> eventList) {
List<Event> removeEvents = new ArrayList<>(); List<Event> removeEvents = new ArrayList<>();
List<Event> shuffle = eventList.parallelStream() List<Event> shuffle = eventList.parallelStream()
.filter(event -> event instanceof AddUserEvent) .filter(event -> event instanceof AddMemberEvent)
.collect(Collectors.toList()); .collect(Collectors.toList());
Collections.shuffle(shuffle); Collections.shuffle(shuffle);
for (Event event : 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) { if (removeEvents.size() >= count) {
break; break;
@ -248,13 +247,13 @@ public class TestBuilder {
* @return Eventliste * @return Eventliste
*/ */
public static List<Event> deleteUserEvents(Group group) { public static List<Event> deleteUserEvents(Group group) {
return group.getMembers().parallelStream() return group.getMemberships().parallelStream()
.map(user -> deleteUserEvent(group.getGroupid(), user.getUserId())) .map(user -> deleteUserEvent(group.getGroupid(), user.getUserId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public static Event deleteUserEvent(UUID groupId, String userId) { public static Event deleteUserEvent(UUID groupId, String userId) {
return new DeleteUserEvent( return new KickMemberEvent(
groupId, groupId,
userId userId
); );
@ -265,7 +264,7 @@ public class TestBuilder {
} }
public static Event updateGroupDescriptionEvent(UUID groupId, String description) { public static Event updateGroupDescriptionEvent(UUID groupId, String description) {
return new UpdateGroupDescriptionEvent( return new SetDescriptionEvent(
groupId, groupId,
faker.random().hex(), faker.random().hex(),
description description
@ -277,7 +276,7 @@ public class TestBuilder {
} }
public static Event updateGroupTitleEvent(UUID groupId, String title) { public static Event updateGroupTitleEvent(UUID groupId, String title) {
return new UpdateGroupTitleEvent( return new SetTitleEvent(
groupId, groupId,
faker.random().hex(), faker.random().hex(),
title title
@ -285,7 +284,7 @@ public class TestBuilder {
} }
public static Event updateUserLimitMaxEvent(UUID groupId) { 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) { public static Event updateRoleEvent(UUID groupId, String userId, Role role) {
@ -297,7 +296,7 @@ public class TestBuilder {
} }
public static Event deleteGroupEvent(UUID groupId) { public static Event deleteGroupEvent(UUID groupId) {
return new DeleteGroupEvent(groupId, faker.random().hex()); return new DestroyGroupEvent(groupId, faker.random().hex());
} }
private static String firstname() { private static String firstname() {

View File

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

View File

@ -1,8 +1,8 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.Type;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
@ -19,7 +19,7 @@ class CreateGroupEventTest {
Group group = TestBuilder.apply(createEvent); Group group = TestBuilder.apply(createEvent);
assertThat(group.getMembers()).hasSize(0); assertThat(group.getMemberships()).hasSize(0);
assertThat(group.getType()).isEqualTo(Type.PUBLIC); assertThat(group.getType()).isEqualTo(Type.PUBLIC);
assertThat(group.getGroupid()).isEqualTo(uuidMock(0)); assertThat(group.getGroupid()).isEqualTo(uuidMock(0));
assertThat(group.getParent()).isEqualTo(uuidMock(1)); assertThat(group.getParent()).isEqualTo(uuidMock(1));

View File

@ -1,14 +1,14 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.Type;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
class DeleteGroupEventTest { class DestroyGroupEventTest {
@Test @Test
void applyEvent() { void applyEvent() {
@ -16,11 +16,11 @@ class DeleteGroupEventTest {
"A", "A",
uuidMock(1), uuidMock(1),
Type.PUBLIC); Type.PUBLIC);
Event deleteEvent = new DeleteGroupEvent(uuidMock(0), "A"); Event deleteEvent = new DestroyGroupEvent(uuidMock(0), "A");
Group group = TestBuilder.apply(createEvent, deleteEvent); Group group = TestBuilder.apply(createEvent, deleteEvent);
assertThat(group.getMembers()).isEmpty(); assertThat(group.getMemberships()).isEmpty();
assertThat(group.getType()).isEqualTo(null); assertThat(group.getType()).isEqualTo(null);
assertThat(group.getLimit()).isEqualTo(0); assertThat(group.getLimit()).isEqualTo(0);
assertThat(group.getGroupid()).isEqualTo(uuidMock(0)); assertThat(group.getGroupid()).isEqualTo(uuidMock(0));

View File

@ -1,8 +1,8 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.exception.GroupIdMismatchException; import mops.gruppen2.domain.exception.IdMismatchException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent; import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
@ -18,7 +18,7 @@ class EventTest {
Group group = TestBuilder.apply(createEvent); Group group = TestBuilder.apply(createEvent);
assertThrows(GroupIdMismatchException.class, () -> addEvent.apply(group)); assertThrows(IdMismatchException.class, () -> addEvent.apply(group));
} }
} }

View File

@ -2,28 +2,27 @@ package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.addUserEvent; import static mops.gruppen2.TestBuilder.addUserEvent;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent; import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
import static mops.gruppen2.TestBuilder.updateUserLimitMaxEvent; import static mops.gruppen2.TestBuilder.updateUserLimitMaxEvent;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class DeleteUserEventTest { class KickMemberEventTest {
@Test @Test
void applyEvent() { void applyEvent() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateLimitEvent = updateUserLimitMaxEvent(uuidMock(0)); Event updateLimitEvent = updateUserLimitMaxEvent(uuidMock(0));
Event addEvent = addUserEvent(uuidMock(0), "A"); Event addEvent = addUserEvent(uuidMock(0), "A");
Event deleteEvent = new DeleteUserEvent(uuidMock(0), "A"); Event deleteEvent = new KickMemberEvent(uuidMock(0), "A");
Group group = TestBuilder.apply(createEvent, updateLimitEvent, addEvent, deleteEvent); Group group = TestBuilder.apply(createEvent, updateLimitEvent, addEvent, deleteEvent);
assertThat(group.getMembers()).hasSize(0); assertThat(group.getMemberships()).hasSize(0);
} }
@Test @Test
@ -31,11 +30,11 @@ class DeleteUserEventTest {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateLimitEvent = updateUserLimitMaxEvent(uuidMock(0)); Event updateLimitEvent = updateUserLimitMaxEvent(uuidMock(0));
Event addEvent = addUserEvent(uuidMock(0), "A"); Event addEvent = addUserEvent(uuidMock(0), "A");
Event deleteEvent = new DeleteUserEvent(uuidMock(0), "B"); Event deleteEvent = new KickMemberEvent(uuidMock(0), "B");
Group group = TestBuilder.apply(createEvent, updateLimitEvent, addEvent); Group group = TestBuilder.apply(createEvent, updateLimitEvent, addEvent);
assertThrows(UserNotFoundException.class, () -> deleteEvent.apply(group)); assertThrows(UserNotFoundException.class, () -> deleteEvent.apply(group));
assertThat(group.getMembers()).hasSize(1); assertThat(group.getMemberships()).hasSize(1);
} }
} }

View File

@ -1,21 +1,20 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.exception.BadParameterException; import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent; import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class UpdateGroupDescriptionEventTest { class SetDescriptionEventTest {
@Test @Test
void applyEvent() { void applyEvent() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEvent = new UpdateGroupDescriptionEvent(uuidMock(0), "A", "desc."); Event updateEvent = new SetDescriptionEvent(uuidMock(0), "A", "desc.");
Group group = TestBuilder.apply(createEvent, updateEvent); Group group = TestBuilder.apply(createEvent, updateEvent);
@ -25,10 +24,10 @@ class UpdateGroupDescriptionEventTest {
@Test @Test
void applyEvent_badDescription() { void applyEvent_badDescription() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEventA = new UpdateGroupDescriptionEvent(uuidMock(0), "A", ""); Event updateEventA = new SetDescriptionEvent(uuidMock(0), "A", "");
Group group = TestBuilder.apply(createEvent); Group group = TestBuilder.apply(createEvent);
assertThrows(BadParameterException.class, () -> updateEventA.apply(group)); assertThrows(BadArgumentException.class, () -> updateEventA.apply(group));
} }
} }

View File

@ -1,22 +1,21 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.domain.exception.BadParameterException; import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.addUserEvent; import static mops.gruppen2.TestBuilder.addUserEvent;
import static mops.gruppen2.TestBuilder.apply; import static mops.gruppen2.TestBuilder.apply;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent; import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class UpdateUserLimitEventTest { class SetLimitEventTest {
@Test @Test
void applyEvent() { void applyEvent() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEvent = new UpdateUserLimitEvent(uuidMock(0), "A", 5L); Event updateEvent = new SetLimitEvent(uuidMock(0), "A", 5L);
Group group = apply(createEvent, updateEvent); Group group = apply(createEvent, updateEvent);
@ -26,24 +25,24 @@ class UpdateUserLimitEventTest {
@Test @Test
void applyEvent_badParameter_negative() { void applyEvent_badParameter_negative() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEvent = new UpdateUserLimitEvent(uuidMock(0), "A", -5L); Event updateEvent = new SetLimitEvent(uuidMock(0), "A", -5L);
Group group = apply(createEvent); Group group = apply(createEvent);
assertThrows(BadParameterException.class, () -> updateEvent.apply(group)); assertThrows(BadArgumentException.class, () -> updateEvent.apply(group));
} }
@Test @Test
void applyEvent_badParameter_tooSmall() { void applyEvent_badParameter_tooSmall() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEventA = new UpdateUserLimitEvent(uuidMock(0), "A", 5L); Event updateEventA = new SetLimitEvent(uuidMock(0), "A", 5L);
Event addEventA = addUserEvent(uuidMock(0)); Event addEventA = addUserEvent(uuidMock(0));
Event addEventB = addUserEvent(uuidMock(0)); Event addEventB = addUserEvent(uuidMock(0));
Event addEventC = addUserEvent(uuidMock(0)); Event addEventC = addUserEvent(uuidMock(0));
Event updateEventB = new UpdateUserLimitEvent(uuidMock(0), "A", 2L); Event updateEventB = new SetLimitEvent(uuidMock(0), "A", 2L);
Group group = apply(createEvent, updateEventA, addEventA, addEventB, addEventC); Group group = apply(createEvent, updateEventA, addEventA, addEventB, addEventC);
assertThrows(BadParameterException.class, () -> updateEventB.apply(group)); assertThrows(BadArgumentException.class, () -> updateEventB.apply(group));
} }
} }

View File

@ -1,21 +1,20 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.exception.BadParameterException; import mops.gruppen2.domain.exception.BadArgumentException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent; import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class UpdateGroupTitleEventTest { class SetTitleEventTest {
@Test @Test
void applyEvent() { void applyEvent() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEvent = new UpdateGroupTitleEvent(uuidMock(0), "A", "title."); Event updateEvent = new SetTitleEvent(uuidMock(0), "A", "title.");
Group group = TestBuilder.apply(createEvent, updateEvent); Group group = TestBuilder.apply(createEvent, updateEvent);
@ -25,10 +24,10 @@ class UpdateGroupTitleEventTest {
@Test @Test
void applyEvent_badDescription() { void applyEvent_badDescription() {
Event createEvent = createPublicGroupEvent(uuidMock(0)); Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEventA = new UpdateGroupTitleEvent(uuidMock(0), "A", ""); Event updateEventA = new SetTitleEvent(uuidMock(0), "A", "");
Group group = TestBuilder.apply(createEvent); Group group = TestBuilder.apply(createEvent);
assertThrows(BadParameterException.class, () -> updateEventA.apply(group)); assertThrows(BadArgumentException.class, () -> updateEventA.apply(group));
} }
} }

View File

@ -1,8 +1,8 @@
package mops.gruppen2.domain.event; package mops.gruppen2.domain.event;
import mops.gruppen2.domain.exception.UserNotFoundException; import mops.gruppen2.domain.exception.UserNotFoundException;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Role; import mops.gruppen2.domain.model.group.Role;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static mops.gruppen2.TestBuilder.addUserEvent; import static mops.gruppen2.TestBuilder.addUserEvent;
@ -10,7 +10,6 @@ import static mops.gruppen2.TestBuilder.apply;
import static mops.gruppen2.TestBuilder.createPublicGroupEvent; import static mops.gruppen2.TestBuilder.createPublicGroupEvent;
import static mops.gruppen2.TestBuilder.updateUserLimitMaxEvent; import static mops.gruppen2.TestBuilder.updateUserLimitMaxEvent;
import static mops.gruppen2.TestBuilder.uuidMock; import static mops.gruppen2.TestBuilder.uuidMock;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
class UpdateRoleEventTest { class UpdateRoleEventTest {

View File

@ -2,7 +2,7 @@ package mops.gruppen2.domain.service;
import mops.gruppen2.Gruppen2Application; import mops.gruppen2.Gruppen2Application;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.model.User; import mops.gruppen2.domain.model.group.User;
import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO; import mops.gruppen2.persistance.dto.EventDTO;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -73,7 +73,7 @@ class EventStoreServiceTest {
EventDTO dto = EventStoreService.getDTOFromEvent(event); EventDTO dto = EventStoreService.getDTOFromEvent(event);
assertThat(dto.getGroup_id()).isEqualTo(event.getGroupid().toString()); assertThat(dto.getGroup_id()).isEqualTo(event.getGroupid().toString());
assertThat(dto.getUser_id()).isEqualTo(event.getUserid()); assertThat(dto.getUser_id()).isEqualTo(event.getTarget());
assertThat(dto.getEvent_id()).isEqualTo(null); assertThat(dto.getEvent_id()).isEqualTo(null);
assertThat(dto.getEvent_type()).isEqualTo("CreateGroupEvent"); assertThat(dto.getEvent_type()).isEqualTo("CreateGroupEvent");
} }

View File

@ -3,9 +3,9 @@ package mops.gruppen2.domain.service;
import mops.gruppen2.Gruppen2Application; import mops.gruppen2.Gruppen2Application;
import mops.gruppen2.TestBuilder; import mops.gruppen2.TestBuilder;
import mops.gruppen2.domain.event.Event; import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.model.Group; import mops.gruppen2.domain.model.group.Group;
import mops.gruppen2.domain.model.Type; import mops.gruppen2.domain.model.group.Type;
import mops.gruppen2.domain.model.User; import mops.gruppen2.domain.model.group.User;
import mops.gruppen2.persistance.EventRepository; import mops.gruppen2.persistance.EventRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@ -82,7 +82,7 @@ class GroupServiceTest {
List<Group> groups = ProjectionService.projectGroups(eventList); List<Group> groups = ProjectionService.projectGroups(eventList);
assertThat(groups).hasSize(1); assertThat(groups).hasSize(1);
assertThat(groups.get(0).getMembers()).hasSize(5); assertThat(groups.get(0).getMemberships()).hasSize(5);
assertThat(groups.get(0).getType()).isEqualTo(Type.PRIVATE); assertThat(groups.get(0).getType()).isEqualTo(Type.PRIVATE);
} }
@ -95,7 +95,7 @@ class GroupServiceTest {
List<Group> groups = ProjectionService.projectGroups(eventList); List<Group> groups = ProjectionService.projectGroups(eventList);
assertThat(groups).hasSize(20); assertThat(groups).hasSize(20);
assertThat(groups.stream().map(group -> group.getMembers().size()).reduce(Integer::sum).get()).isEqualTo(70); assertThat(groups.stream().map(group -> group.getMemberships().size()).reduce(Integer::sum).get()).isEqualTo(70);
} }
//TODO: EventStoreServiceTest //TODO: EventStoreServiceTest