1

Merge pull request #4 from ChUrl/REFACTOR-controllers

Refactor controllers
This commit is contained in:
Christoph
2020-04-09 18:41:51 +02:00
committed by GitHub
29 changed files with 536 additions and 458 deletions

View File

@ -41,7 +41,7 @@ pmd {
}
checkstyle {
toolVersion = "8.28"
toolVersion = "8.30"
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
ignoreFailures = true
}
@ -72,9 +72,12 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.security.oauth:spring-security-oauth2:2.4.0.RELEASE'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.keycloak:keycloak-spring-boot-starter:9.0.0'
implementation 'org.keycloak.bom:keycloak-adapter-bom:9.0.0'
implementation 'mops:styleguide:2.1.0'
@ -85,7 +88,7 @@ dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'

View File

@ -2,47 +2,11 @@ package mops.gruppen2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Collections;
@SpringBootApplication
@EnableCaching
@EnableSwagger2
public class Gruppen2Application {
public static void main(String[] args) {
SpringApplication.run(Gruppen2Application.class, args);
}
@Bean
public Docket productAPI() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.ant("/gruppen2/api/**"))
.apis(RequestHandlerSelectors.basePackage("mops.gruppen2"))
.build()
.apiInfo(apiMetadata());
}
private ApiInfo apiMetadata() {
return new ApiInfo(
"Gruppenbildung API",
"API zum anfragen/aktualisieren der Gruppendaten.",
"0.0.1",
"Free to use",
new Contact("gruppen2", "https://github.com/hhu-propra2/abschlussprojekt-it-bois", ""),
"",
"",
Collections.emptyList()
);
}
}

View File

@ -0,0 +1,62 @@
package mops.gruppen2.aspect;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.Trace;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Log4j2
@Profile("dev")
@Aspect
@Component
public class LogAspect {
// ######################################### POINTCUT ########################################
@Pointcut("within(@mops.gruppen2.aspect.annotation.TraceMethodCalls *)")
public void beanAnnotatedWithMonitor() {}
@Pointcut("execution(public * *(..))")
public void publicMethod() {}
@Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void logMethodCalls() {}
// ###################################### ANNOTATIONS ########################################
@Before("@annotation(mops.gruppen2.aspect.annotation.Trace)")
public void logCustom(JoinPoint joinPoint) {
log.trace(((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Trace.class).value());
}
@Before("@annotation(mops.gruppen2.aspect.annotation.TraceMethodCall) || logMethodCalls()")
public void logMethodCall(JoinPoint joinPoint) {
log.trace("Methodenaufruf: {} ({})",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName().replace("mops.gruppen2.", ""));
System.out.println();
}
@Around("@annotation(mops.gruppen2.aspect.annotation.TraceExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
joinPoint.proceed();
long stop = System.currentTimeMillis();
log.trace("Ausführungsdauer: {} Millis", (stop - start));
return joinPoint.proceed();
}
}

View File

@ -0,0 +1,16 @@
package mops.gruppen2.aspect.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Schreibt eine benutzerdefinierte Nachricht in den Trace-Stream bei Methodenaufruf
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trace {
String value();
}

View File

@ -0,0 +1,15 @@
package mops.gruppen2.aspect.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Schreibt die Methodenausführdauer in den Trace-Stream
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceExecutionTime {
}

View File

@ -0,0 +1,14 @@
package mops.gruppen2.aspect.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Schreibt eine Nachricht bei Methodenausführung in den Trace-Stream
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceMethodCall {
}

View File

@ -0,0 +1,14 @@
package mops.gruppen2.aspect.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Schreibt eine Nachricht für jede ausgeführte Methode einer Klasse in den Trace-Stream
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceMethodCalls {
}

View File

@ -0,0 +1,45 @@
package mops.gruppen2.config;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Collections;
@Profile("dev")
@Configuration
@EnableCaching
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket productAPI() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.ant("/gruppen2/api/**"))
.apis(RequestHandlerSelectors.basePackage("mops.gruppen2"))
.build()
.apiInfo(apiMetadata());
}
private ApiInfo apiMetadata() {
return new ApiInfo(
"Gruppenbildung API",
"API zum anfragen/aktualisieren der Gruppendaten.",
"0.0.1",
"Free to use",
new Contact("gruppen2", "https://github.com/hhu-propra2/abschlussprojekt-it-bois", ""),
"",
"",
Collections.emptyList()
);
}
}

View File

@ -4,10 +4,10 @@ package mops.gruppen2.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.api.GroupRequestWrapper;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.service.APIService;
import mops.gruppen2.service.EventStoreService;
import mops.gruppen2.service.IdService;
@ -22,12 +22,12 @@ import java.util.List;
import java.util.UUID;
/**
* Api zum Datenabgleich mit Gruppenfindung.
* Api zum Datenabgleich.
*/
//TODO: API-Service?
@Log4j2
@TraceMethodCalls
@RestController
@RequestMapping("/gruppen2/api")
@Log4j2
public class APIController {
private final EventStoreService eventStoreService;
@ -38,31 +38,43 @@ public class APIController {
this.projectionService = projectionService;
}
@GetMapping("/updateGroups/{lastEventId}")
/**
* Erzeugt eine Liste aus Gruppen, welche sich seit einer übergebenen Event-Id geändert haben.
* Die Gruppen werden vollständig projiziert, enthalten also alle Informationen zum entsprechenden Zeitpunkt.
*
* @param eventId Die Event-ID, welche der Anfragesteller beim letzten Aufruf erhalten hat
*/
@GetMapping("/update/{id}")
@Secured("ROLE_api_user")
@ApiOperation("Gibt alle Gruppen zurück, in denen sich etwas geändert hat")
public GroupRequestWrapper updateGroups(@ApiParam("Letzter Status des Anfragestellers")
@PathVariable long lastEventId) throws EventException {
log.info("ApiRequest to /updateGroups\n");
@ApiOperation("Gibt veränderte Gruppen zurück")
public GroupRequestWrapper update(@ApiParam("Letzte gespeicherte EventId des Anfragestellers")
@PathVariable("id") long eventId) {
return APIService.wrap(eventStoreService.findMaxEventId(),
projectionService.projectNewGroups(lastEventId));
projectionService.projectNewGroups(eventId));
}
@GetMapping("/getGroupIdsOfUser/{userId}")
/**
* Gibt die Gruppen-IDs von Gruppen, in welchen der übergebene Nutzer teilnimmt, zurück.
*/
@GetMapping("/usergroups/{id}")
@Secured("ROLE_api_user")
@ApiOperation("Gibt alle Gruppen zurück, in denen sich ein Teilnehmer befindet")
public List<String> getGroupIdsOfUser(@ApiParam("Teilnehmer dessen groupIds zurückgegeben werden sollen")
@PathVariable String userId) {
log.info("ApiRequest to /getGroupIdsOfUser\n");
@ApiOperation("Gibt Gruppen zurück, in welchen ein Nutzer teilnimmt")
public List<String> usergroups(@ApiParam("Nutzer-Id")
@PathVariable("id") String userId) {
return IdService.uuidsToString(eventStoreService.findExistingUserGroups(new User(userId)));
}
@GetMapping("/getGroup/{groupId}")
/**
* Konstruiert eine einzelne, vollständige Gruppe.
*/
@GetMapping("/group/{id}")
@Secured("ROLE_api_user")
@ApiOperation("Gibt die Gruppe mit der als Parameter mitgegebenden groupId zurück")
public Group getGroupById(@ApiParam("GruppenId der gefordeten Gruppe")
@PathVariable String groupId) throws EventException {
log.info("ApiRequest to /getGroup\n");
public Group getGroupById(@ApiParam("Gruppen-Id der gefordeten Gruppe")
@PathVariable("id") String groupId) {
return projectionService.projectSingleGroup(UUID.fromString(groupId));
}

View File

@ -1,7 +1,7 @@
package mops.gruppen2.controller;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Account;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
@ -9,7 +9,6 @@ import mops.gruppen2.service.CsvService;
import mops.gruppen2.service.GroupService;
import mops.gruppen2.service.IdService;
import mops.gruppen2.service.ProjectionService;
import mops.gruppen2.service.ValidationService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
@ -18,7 +17,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.security.RolesAllowed;
@ -29,10 +27,10 @@ import static mops.gruppen2.service.ControllerService.getUserLimit;
import static mops.gruppen2.service.ControllerService.getVisibility;
@SuppressWarnings("SameReturnValue")
@Controller
@SessionScope
@RequestMapping("/gruppen2")
@Log4j2
@TraceMethodCalls
@Controller
@RequestMapping("/gruppen2")
public class GroupCreationController {
private final GroupService groupService;
@ -43,37 +41,31 @@ public class GroupCreationController {
this.projectionService = projectionService;
}
@RolesAllowed({"ROLE_orga", "ROLE_actuator"})
//TODO: /create/orga
@RolesAllowed("ROLE_orga")
@GetMapping("/createOrga")
public String createGroupAsOrga(KeycloakAuthenticationToken token,
Model model) {
public String getCreateOrgaPage(Model model) {
log.info("GET to /createOrga\n");
model.addAttribute("account", new Account(token));
model.addAttribute("lectures", projectionService.projectLectures());
return "createOrga";
}
@RolesAllowed({"ROLE_orga", "ROLE_actuator"})
//TODO: /create/orga
@RolesAllowed("ROLE_orga")
@PostMapping("/createOrga")
@CacheEvict(value = "groups", allEntries = true)
public String postCrateGroupAsOrga(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("visibility") boolean isPrivate,
@RequestParam("lecture") boolean isLecture,
@RequestParam("maxInfiniteUsers") boolean isInfinite,
@RequestParam("userMaximum") long userLimit,
@RequestParam("parent") String parent,
@RequestParam(value = "file", required = false) MultipartFile file) {
log.info("POST to /createOrga\n");
Account account = new Account(token);
User user = new User(account);
public String postCreateOrga(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("visibility") boolean isPrivate,
@RequestParam("lecture") boolean isLecture,
@RequestParam("maxInfiniteUsers") boolean isInfinite,
@RequestParam("userMaximum") long userLimit,
@RequestParam("parent") String parent,
@RequestParam(value = "file", required = false) MultipartFile file) {
User user = new User(token);
Group group = groupService.createGroup(user,
title,
description,
@ -87,37 +79,29 @@ public class GroupCreationController {
return "redirect:/gruppen2/details/" + IdService.uuidToString(group.getId());
}
//TODO: /create/student
@RolesAllowed("ROLE_studentin")
@GetMapping("/createStudent")
public String createGroupAsStudent(KeycloakAuthenticationToken token,
Model model) {
public String getCreateStudentPage(Model model) {
log.info("GET to /createStudent\n");
model.addAttribute("account", new Account(token));
model.addAttribute("lectures", projectionService.projectLectures());
return "createStudent";
}
//TODO: /create/student
@RolesAllowed("ROLE_studentin")
@PostMapping("/createStudent")
@CacheEvict(value = "groups", allEntries = true)
public String postCreateGroupAsStudent(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("visibility") boolean isPrivate,
@RequestParam("maxInfiniteUsers") boolean isInfinite,
@RequestParam("userMaximum") long userLimit,
@RequestParam("parent") String parent) {
public String postCreateStudent(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("visibility") boolean isPrivate,
@RequestParam("maxInfiniteUsers") boolean isInfinite,
@RequestParam("userMaximum") long userLimit,
@RequestParam("parent") String parent) {
log.info("POST to /createStudent\n");
ValidationService.validateTitle(title);
ValidationService.validateDescription(description);
Account account = new Account(token);
User user = new User(account);
User user = new User(token);
Group group = groupService.createGroup(user,
title,
description,

View File

@ -1,11 +1,9 @@
package mops.gruppen2.controller;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Account;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.service.CsvService;
import mops.gruppen2.service.GroupService;
import mops.gruppen2.service.IdService;
@ -21,7 +19,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.security.RolesAllowed;
@ -29,10 +26,10 @@ import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
@SuppressWarnings("SameReturnValue")
@Controller
@SessionScope
@RequestMapping("/gruppen2")
@Log4j2
@TraceMethodCalls
@Controller
@RequestMapping("/gruppen2")
public class GroupDetailsController {
private final InviteService inviteService;
@ -45,132 +42,182 @@ public class GroupDetailsController {
this.projectionService = projectionService;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
//TODO: /details/{id}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/{id}")
public String showGroupDetails(KeycloakAuthenticationToken token,
Model model,
HttpServletRequest request,
@PathVariable("id") String groupId) {
log.info("GET to /details\n");
Account account = new Account(token);
User user = new User(account);
model.addAttribute("account", account);
public String getDetailsPage(KeycloakAuthenticationToken token,
Model model,
HttpServletRequest request,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
model.addAttribute("group", group);
// Parent Badge
UUID parentId = group.getParent();
Group parent = projectionService.projectParent(parentId);
// Detailseite für private Gruppen
if (!ValidationService.checkIfGroupAccess(group, user)) {
return "detailsNoMember";
}
// Invite Link
String actualURL = request.getRequestURL().toString();
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
String link = serverURL + "gruppen2/join/" + inviteService.getLinkByGroup(group);
model.addAttribute("roles", group.getRoles());
model.addAttribute("user", user);
model.addAttribute("admin", Role.ADMIN);
model.addAttribute("public", Visibility.PUBLIC);
model.addAttribute("private", Visibility.PRIVATE);
model.addAttribute("group", group);
model.addAttribute("parent", parent);
model.addAttribute("link", link);
// Invitelink Anzeige für Admins
if (ValidationService.checkIfAdmin(group, user)) {
String actualURL = request.getRequestURL().toString();
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
model.addAttribute("link", serverURL + "gruppen2/acceptinvite/" + inviteService.getLinkByGroup(group));
// Detailseite für nicht-Mitglieder
if (!ValidationService.checkIfMember(group, user)) {
return "detailsNoMember";
}
return "detailsMember";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@GetMapping("/details/changeMetadata/{id}")
public String changeMetadata(KeycloakAuthenticationToken token,
//TODO: /details/{id}/join
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/join")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsJoin(KeycloakAuthenticationToken token,
@RequestParam("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.addUser(user, group);
return "redirect:/gruppen2/details/" + groupId;
}
//TODO: /details/{id}/leave
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/leave")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsLeave(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteUser(user, group);
return "redirect:/gruppen2";
}
//TODO: /details/{id}/destroy
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/delete")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsDestroy(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteGroup(user, group);
return "redirect:/gruppen2";
}
//TODO: /details/{id}/meta
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/meta/{id}")
public String getDetailsMeta(KeycloakAuthenticationToken token,
Model model,
@PathVariable("id") String groupId) {
log.info("GET to /details/changeMetadata\n");
Account account = new Account(token);
User user = new User(account);
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoAdmin(group, user);
model.addAttribute("account", account);
model.addAttribute("title", group.getTitle());
model.addAttribute("description", group.getDescription());
model.addAttribute("admin", Role.ADMIN);
model.addAttribute("roles", group.getRoles());
model.addAttribute("groupId", group.getId());
model.addAttribute("user", user);
model.addAttribute("group", group);
return "changeMetadata";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/details/changeMetadata")
//TODO: /details/{id}/meta/update
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/meta")
@CacheEvict(value = "groups", allEntries = true)
public String postChangeMetadata(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("groupId") String groupId) {
log.info("POST to /details/changeMetadata\n");
Account account = new Account(token);
User user = new User(account);
public String postDetailsMetaUpdate(KeycloakAuthenticationToken token,
@RequestParam("title") String title,
@RequestParam("description") String description,
@RequestParam("groupId") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoAdmin(group, user);
groupService.updateTitle(user, group, title);
groupService.updateDescription(user, group, description);
return "redirect:/gruppen2/details/" + groupId;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
//TODO: /details/{id}/members
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/members/{id}")
public String editMembers(KeycloakAuthenticationToken token,
Model model,
@PathVariable("id") String groupId) {
log.info("GET to /details/members\n");
Account account = new Account(token);
User user = new User(account);
public String getDetailsMembers(KeycloakAuthenticationToken token,
Model model,
@PathVariable("id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoAdmin(group, user);
model.addAttribute("account", account);
model.addAttribute("members", group.getMembers());
model.addAttribute("group", group);
model.addAttribute("admin", Role.ADMIN);
return "editMembers";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/details/members/changeRole")
//TODO: /details/{id}/members/update/userlimit
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/members/setuserlimit")
@CacheEvict(value = "groups", allEntries = true)
public String changeRole(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId,
@RequestParam("user_id") String userId) {
log.info("POST to /details/members/changeRole\n");
Account account = new Account(token);
User user = new User(account);
public String postDetailsMembersUpdateUserLimit(KeycloakAuthenticationToken token,
@RequestParam("maximum") long userLimit,
@RequestParam("group_id") String groupId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateUserLimit(user, group, userLimit);
return "redirect:/gruppen2/details/members/" + groupId;
}
//TODO: /details/{id}/members/update/csv
@RolesAllowed("ROLE_orga")
@PostMapping("/details/members/csv")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersUpdateCsv(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId,
@RequestParam(value = "file", required = false) MultipartFile file) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(IdService.stringToUUID(groupId));
groupService.addUsersToGroup(CsvService.readCsvFile(file), group, user);
return "redirect:/gruppen2/details/members/" + groupId;
}
//TODO: Method + view for /details/{id}/members/{id}
//TODO: /details/{id}/members/{id}/update/role
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/members/togglerole")
@CacheEvict(value = "groups", allEntries = true)
public String postDetailsMembersUpdateRole(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId,
@RequestParam("user_id") String userId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfNoAdmin(group, user);
groupService.toggleMemberRole(new User(userId), group);
// Falls sich der User selbst die Rechte genommen hat
@ -181,114 +228,24 @@ public class GroupDetailsController {
return "redirect:/gruppen2/details/members/" + groupId;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/details/members/changeMaximum")
//TODO: /details/{id}/members/{id}/delete
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@PostMapping("/details/members/deleteuser")
@CacheEvict(value = "groups", allEntries = true)
public String changeMaxSize(KeycloakAuthenticationToken token,
@RequestParam("maximum") long userLimit,
@RequestParam("group_id") String groupId) {
log.info("POST to /details/members/changeMaximum\n");
Account account = new Account(token);
User user = new User(account);
public String postDetailsMembersDelete(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId,
@RequestParam("user_id") String userId) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.updateUserLimit(user, group, userLimit);
return "redirect:/gruppen2/details/members/" + groupId;
}
ValidationService.throwIfNoAdmin(group, user);
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/details/members/deleteUser")
@CacheEvict(value = "groups", allEntries = true)
public String deleteUser(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId,
@RequestParam("user_id") String userId) {
log.info("POST to /details/members/deleteUser\n");
Account account = new Account(token);
User user = new User(account);
// Der eingeloggte User kann sich nicht selbst entfernen
// Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen)
if (!userId.equals(user.getId())) {
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteUser(new User(userId), group);
}
return "redirect:/gruppen2/details/members/" + groupId;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/detailsBeitreten")
@CacheEvict(value = "groups", allEntries = true)
public String joinGroup(KeycloakAuthenticationToken token,
Model model,
@RequestParam("id") String groupId) {
log.info("POST to /detailsBeitreten\n");
Account account = new Account(token);
User user = new User(account);
model.addAttribute("account", account);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.addUser(user, group);
return "redirect:/gruppen2";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/leaveGroup")
@CacheEvict(value = "groups", allEntries = true)
public String leaveGroup(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId) {
log.info("POST to /leaveGroup\n");
Account account = new Account(token);
User user = new User(account);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteUser(user, group);
return "redirect:/gruppen2";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/deleteGroup")
@CacheEvict(value = "groups", allEntries = true)
public String deleteGroup(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId) {
log.info("POST to /deleteGroup\n");
Account account = new Account(token);
User user = new User(account);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
groupService.deleteGroup(user, group);
return "redirect:/gruppen2";
}
@RolesAllowed({"ROLE_orga", "ROLE_actuator"})
@PostMapping("/details/members/addUsersFromCsv")
@CacheEvict(value = "groups", allEntries = true)
public String addUsersFromCsv(KeycloakAuthenticationToken token,
@RequestParam("group_id") String groupId,
@RequestParam(value = "file", required = false) MultipartFile file) {
log.info("POST to /details/members/addUsersFromCsv\n");
Account account = new Account(token);
User user = new User(account);
Group group = projectionService.projectSingleGroup(IdService.stringToUUID(groupId));
groupService.addUsersToGroup(CsvService.readCsvFile(file), group, user);
return "redirect:/gruppen2/details/members/" + groupId;
}
}

View File

@ -1,8 +1,7 @@
package mops.gruppen2.controller;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.aspect.annotation.TraceMethodCall;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.exception.PageNotFoundException;
import mops.gruppen2.service.ProjectionService;
@ -16,8 +15,8 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@SuppressWarnings("SameReturnValue")
@Controller
@Log4j2
@Controller
public class GruppenfindungController {
private final ProjectionService projectionService;
@ -26,25 +25,21 @@ public class GruppenfindungController {
this.projectionService = projectionService;
}
// For convenience
@GetMapping("")
public String redirect() {
return "redirect:/gruppen2";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@TraceMethodCall
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/gruppen2")
public String index(KeycloakAuthenticationToken token,
Model model) {
public String getIndexPage(KeycloakAuthenticationToken token,
Model model) {
log.info("GET to /gruppen2\n");
User user = new User(token);
Account account = new Account(token);
User user = new User(account);
model.addAttribute("account", account);
model.addAttribute("gruppen", projectionService.projectUserGroups(user));
model.addAttribute("user", user);
model.addAttribute("lecture", GroupType.LECTURE);
return "index";
}

View File

@ -0,0 +1,35 @@
package mops.gruppen2.controller;
import mops.gruppen2.domain.Account;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.Role;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice
public class ModelAttributeControllerAdvice {
// Add modelAttributes before each @RequestMapping
@ModelAttribute
public void modelAttributes(KeycloakAuthenticationToken token,
Model model) {
// Prevent NullPointerException if not logged in
if (token != null) {
model.addAttribute("account", new Account(token));
model.addAttribute("user", new User(token));
}
model.addAttribute("member", Role.MEMBER);
model.addAttribute("admin", Role.ADMIN);
model.addAttribute("public", Visibility.PUBLIC);
model.addAttribute("private", Visibility.PRIVATE);
model.addAttribute("lecture", GroupType.LECTURE);
model.addAttribute("simple", GroupType.SIMPLE);
}
}

View File

@ -1,131 +1,77 @@
package mops.gruppen2.controller;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.Account;
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
import mops.gruppen2.domain.Group;
import mops.gruppen2.domain.GroupType;
import mops.gruppen2.domain.User;
import mops.gruppen2.domain.Visibility;
import mops.gruppen2.service.GroupService;
import mops.gruppen2.service.InviteService;
import mops.gruppen2.service.ProjectionService;
import mops.gruppen2.service.SearchService;
import mops.gruppen2.service.ValidationService;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.annotation.SessionScope;
import javax.annotation.security.RolesAllowed;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@SuppressWarnings("SameReturnValue")
@Controller
@SessionScope
@RequestMapping("/gruppen2")
@Log4j2
@TraceMethodCalls
@Controller
@RequestMapping("/gruppen2")
public class SearchAndInviteController {
private final InviteService inviteService;
private final GroupService groupService;
private final ProjectionService projectionService;
private final SearchService searchService;
public SearchAndInviteController(InviteService inviteService, GroupService groupService, ProjectionService projectionService, SearchService searchService) {
public SearchAndInviteController(InviteService inviteService, ProjectionService projectionService, SearchService searchService) {
this.inviteService = inviteService;
this.groupService = groupService;
this.projectionService = projectionService;
this.searchService = searchService;
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
//TODO: /search
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/searchPage")
public String findGroup(KeycloakAuthenticationToken token,
Model model) {
log.info("GET to /searchPage\n");
Account account = new Account(token);
model.addAttribute("account", account);
model.addAttribute("gruppen", Collections.emptyList()); // TODO: verschönern
model.addAttribute("inviteService", inviteService); //TODO: don't inject service
public String getSearchPage(Model model) {
// Noch keine Suche gestartet: leeres Suchergebnis
model.addAttribute("gruppen", Collections.emptyList());
return "search";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
//TODO: /search/{string}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/search")
public String search(KeycloakAuthenticationToken token,
Model model,
@RequestParam("suchbegriff") String search) {
log.info("GET to /search\n");
Account account = new Account(token);
User user = new User(account);
public String getSearch(KeycloakAuthenticationToken token,
Model model,
@RequestParam("suchbegriff") String search) {
User user = new User(token);
List<Group> groups = searchService.searchPublicGroups(search, user);
model.addAttribute("account", account);
model.addAttribute("gruppen", groups);
model.addAttribute("inviteService", inviteService); //TODO: don't inject service
return "search";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@GetMapping("/detailsSearch")
public String showGroupDetailsNoMember(KeycloakAuthenticationToken token,
Model model,
@RequestParam("id") String groupId) {
log.info("GET to /detailsSearch\n");
Account account = new Account(token);
User user = new User(account);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
// Parent Badge
UUID parentId = group.getParent();
Group parent = projectionService.projectParent(parentId);
model.addAttribute("account", account);
if (ValidationService.checkIfMember(group, user)) {
return "redirect:/gruppen2/details/" + groupId;
}
model.addAttribute("group", group);
model.addAttribute("parentId", parentId);
model.addAttribute("parent", parent);
model.addAttribute("lecture", GroupType.LECTURE);
return "detailsNoMember";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@GetMapping("/acceptinvite/{link}")
public String acceptInvite(KeycloakAuthenticationToken token,
Model model,
@PathVariable("link") String link) {
log.info("GET to /acceptInvite\n");
Account account = new Account(token);
User user = new User(account);
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/join/{link}")
public String getJoin(KeycloakAuthenticationToken token,
Model model,
@PathVariable("link") String link) {
User user = new User(token);
Group group = projectionService.projectSingleGroup(inviteService.getGroupIdFromLink(link));
model.addAttribute("account", account);
model.addAttribute("group", group);
// Gruppe öffentlich
@ -140,24 +86,4 @@ public class SearchAndInviteController {
return "joinprivate";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
@PostMapping("/acceptinvite")
@CacheEvict(value = "groups", allEntries = true)
public String postAcceptInvite(KeycloakAuthenticationToken token,
@RequestParam("id") String groupId) {
log.info("POST to /acceptInvite\n");
Account account = new Account(token);
User user = new User(account);
Group group = projectionService.projectSingleGroup(UUID.fromString(groupId));
ValidationService.throwIfMember(group, user);
ValidationService.throwIfGroupFull(group);
groupService.addUser(user, group);
return "redirect:/gruppen2/details/" + groupId;
}
}

View File

@ -5,6 +5,8 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@Getter
@AllArgsConstructor
@ -22,11 +24,12 @@ public class User {
@ToString.Exclude
private String email;
public User(Account account) {
id = account.getName();
givenname = account.getGivenname();
familyname = account.getFamilyname();
email = account.getEmail();
public User(KeycloakAuthenticationToken token) {
KeycloakPrincipal principal = (KeycloakPrincipal) token.getPrincipal();
id = principal.getName();
givenname = principal.getKeycloakSecurityContext().getIdToken().getGivenName();
familyname = principal.getKeycloakSecurityContext().getIdToken().getFamilyName();
email = principal.getKeycloakSecurityContext().getIdToken().getEmail();
}
/**

View File

@ -23,12 +23,12 @@ public class UpdateGroupDescriptionEvent extends Event {
public UpdateGroupDescriptionEvent(UUID groupId, String userId, String newGroupDescription) {
super(groupId, userId);
this.newGroupDescription = newGroupDescription.trim();
this.newGroupDescription = newGroupDescription;
}
public UpdateGroupDescriptionEvent(Group group, User user, String newGroupDescription) {
super(group.getId(), user.getId());
this.newGroupDescription = newGroupDescription.trim();
this.newGroupDescription = newGroupDescription;
}
@Override

View File

@ -23,12 +23,12 @@ public class UpdateGroupTitleEvent extends Event {
public UpdateGroupTitleEvent(UUID groupId, String userId, String newGroupTitle) {
super(groupId, userId);
this.newGroupTitle = newGroupTitle.trim();
this.newGroupTitle = newGroupTitle;
}
public UpdateGroupTitleEvent(Group group, User user, String newGroupTitle) {
super(group.getId(), user.getId());
this.newGroupTitle = newGroupTitle.trim();
this.newGroupTitle = newGroupTitle;
}
@Override

View File

@ -82,6 +82,7 @@ public class GroupService {
* Fügt eine Liste von Usern zu einer Gruppe hinzu.
* Duplikate werden übersprungen, die erzeugten Events werden gespeichert.
* Dabei wird das Teilnehmermaximum eventuell angehoben.
* Prüft, ob der User Admin ist.
*
* @param newUsers Userliste
* @param group Gruppe
@ -109,6 +110,7 @@ public class GroupService {
/**
* Wechselt die Rolle eines Teilnehmers von Admin zu Member oder andersherum.
* Überprüft, ob der User Mitglied ist und ob er der letzte Admin ist.
*
* @param user Teilnehmer, welcher geändert wird
* @param group Gruppe, in welcher sih der Teilnehmer befindet
@ -145,6 +147,10 @@ public class GroupService {
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) {
ValidationService.throwIfMember(group, user);
ValidationService.throwIfGroupFull(group);
@ -166,6 +172,10 @@ public class GroupService {
}
}
/**
* Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist.
*/
public void deleteUser(User user, Group group) throws EventException {
ValidationService.throwIfNoMember(group, user);
ValidationService.throwIfLastAdmin(user, group);
@ -180,6 +190,10 @@ public class GroupService {
}
}
/**
* Erzeugt, speichert ein DeleteGroupEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Admin ist.
*/
public void deleteGroup(User user, Group group) {
ValidationService.throwIfNoAdmin(group, user);
@ -190,35 +204,67 @@ public class GroupService {
eventStoreService.saveEvent(event);
}
/**
* Erzeugt, speichert ein UpdateTitleEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Admin ist und ob der Titel valide ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
public void updateTitle(User user, Group group, String title) {
ValidationService.throwIfNoAdmin(group, user);
ValidationService.validateTitle(title);
ValidationService.validateTitle(title.trim());
Event event = new UpdateGroupTitleEvent(group, user, title);
if (title.trim().equals(group.getTitle())) {
return;
}
Event event = new UpdateGroupTitleEvent(group, user, title.trim());
event.apply(group);
eventStoreService.saveEvent(event);
}
/**
* Erzeugt, speichert ein UpdateDescriptiopnEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Admin ist und ob die Beschreibung valide ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
public void updateDescription(User user, Group group, String description) {
ValidationService.throwIfNoAdmin(group, user);
ValidationService.validateDescription(description);
ValidationService.validateDescription(description.trim());
Event event = new UpdateGroupDescriptionEvent(group, user, description);
if (description.trim().equals(group.getDescription())) {
return;
}
Event event = new UpdateGroupDescriptionEvent(group, user, description.trim());
event.apply(group);
eventStoreService.saveEvent(event);
}
/**
* Erzeugt, speichert ein UpdateRoleEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Mitglied ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
private void updateRole(User user, Group group, Role role) {
ValidationService.throwIfNoMember(group, user);
if (role == group.getRoles().get(user.getId())) {
return;
}
Event event = new UpdateRoleEvent(group, user, role);
event.apply(group);
eventStoreService.saveEvent(event);
}
/**
* Erzeugt, speichert ein UpdateUserLimitEvent und wendet es auf eine Gruppe an.
* Prüft, ob der Nutzer Admin ist und ob das Limit valide ist.
* Bei keiner Änderung wird nichts erzeugt.
*/
public void updateUserLimit(User user, Group group, long userLimit) {
ValidationService.throwIfNoAdmin(group, user);
ValidationService.validateUserLimit(userLimit, group);

View File

@ -66,11 +66,6 @@ public final class ValidationService {
.count() == 1;
}
public static boolean checkIfGroupAccess(Group group, User user) {
return (group.getVisibility() == Visibility.PRIVATE && checkIfMember(group, user))
|| group.getVisibility() == Visibility.PUBLIC;
}
// ######################################## THROW ############################################
@ -112,16 +107,10 @@ public final class ValidationService {
}
}
public static void throwIfNoGroupAccess(Group group, User user) {
if (!checkIfGroupAccess(group, user)) {
log.error("Der User {} hat keinen Zugriff auf Gruppe {}!", user, group);
throw new NoAccessException(group.toString());
}
}
// ##################################### VALIDATE FIELDS #####################################
//TODO: max title length?
public static void validateTitle(String title) {
if (title == null || title.trim().isEmpty()) {

View File

@ -1,6 +1,6 @@
# Logging
logging.application.name = gruppen2
logging.pattern.console = [${logging.application.name}], %magenta(%-5level), %d{dd-MM-yyyy HH:mm:ss.SSS},\t%blue(%msg)\n\t\t\t\t\t\t\t\t\t\t\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
logging.level.mops.gruppen2 = trace
logging.level.org.springframework.jdbc.core = info

View File

@ -1,6 +1,6 @@
# Logging
logging.application.name = gruppen2
logging.pattern.console = [${logging.application.name}],%magenta(%-5level), %d{dd-MM-yyyy HH:mm:ss.SSS}, %highlight(%msg),%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
logging.level.mops.gruppen2 = info
logging.level.org.springframework.jdbc.core = info

View File

@ -46,20 +46,20 @@
<div class="form-group">
<label for="title">Titel</label>
<input class="form-control" id="title" required
th:name="title" th:value="${title}" type="text">
th:name="title" th:value="${group.getTitle()}" type="text">
</div>
<div class="form-group">
<label for="description">Beschreibung</label>
<textarea class="form-control" id="description" required rows="3"
th:name="description"
th:text="${description}"></textarea>
th:text="${group.getDescription()}"></textarea>
</div>
<div class="form-group pt-4">
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;"
th:if="${roles.get(user.getId()) == admin}"
th:if="${group.getRoles().get(user.getId()) == admin}"
th:name="groupId"
th:value="${groupId}"
th:value="${group.getId()}"
type="submit">Speichern
</button>
</div>

View File

@ -43,7 +43,7 @@
<a class="fa fa-pencil"
style="font-size:30px; width: 5%;"
th:href="@{/gruppen2/details/changeMetadata/{id}(id=${group.getId()})}"
th:if="${roles.get(user.getId()) == admin}"></a>
th:if="${group.getRoles().get(user.getId()) == admin}"></a>
</div>
</div>
<h3>
@ -57,7 +57,7 @@
th:text="${parent?.getTitle()}">Parent</span>
<div class="input-group mb-3" style="margin-top: 10px;"
th:if="${roles.get(user.getId()) == admin}">
th:if="${group.getRoles().get(user.getId()) == admin}">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default"
style="background: #52a1eb;">Einladungslink:</span>

View File

@ -44,7 +44,7 @@
<div class="form-group mt-2" th:if="${group.getMembers().size() < group.getUserLimit()}">
<h3>Möchtest du dieser privaten Gruppe beitreten?</h3>
<div class="text-right">
<form method="post" th:action="@{/gruppen2/acceptinvite}">
<form method="post" th:action="@{/gruppen2/join}">
<input name="id" th:value="${group.getId()}" type="hidden"/>
<button class="btn btn-primary"
style="background: #52a1eb; border-style: none;"

View File

@ -53,9 +53,9 @@
</form>
</div>
<br>
<table class="table">
<table class="table" th:if='${!gruppen.isEmpty()}'>
<!-- Erscheint dann, wenn man "Suchen" Button klickt und Ergebnisse angezeigt werden, aber so solls aussehen -->
<thead th:if='${!gruppen.isEmpty()}'>
<thead>
<tr>
<th scope="col">Gruppenname</th>
<th scope="col">Beschreibung</th>
@ -67,8 +67,8 @@
<th scope="row">
<span class="badge badge-pill badge-success"
style="background: lightseagreen; margin-right: 25px;"
th:if='${gruppe.getType() == gruppe.getType().LECTURE}'>Veranstaltung</span>
<a th:href="@{/gruppen2/detailsSearch(id=${gruppe.getId()})}"
th:if='${gruppe.getType() == lecture}'>Veranstaltung</span>
<a th:href="@{/gruppen2/details/{id}(id=${gruppe.getId()})}"
th:text="${#strings.abbreviate(gruppe.getTitle(), 50)}">Gruppenname</a>
</th>
<td style="" th:text="${#strings.abbreviate(gruppe.getDescription(), 50)}">

View File

@ -23,7 +23,8 @@ class ControllerTest {
public static final ArchRule controllerClassesShouldHaveControllerInName = classes()
.that().areAnnotatedWith(Controller.class)
.or().areAnnotatedWith(RestController.class)
.should().haveSimpleNameEndingWith("Controller");
.should().haveSimpleNameEndingWith("Controller")
.orShould().haveSimpleNameEndingWith("ControllerAdvice");
@ArchTest
public static final ArchRule controllerClassesShouldBeInControllerPackage = classes()
@ -34,7 +35,8 @@ class ControllerTest {
@ArchTest
public static final ArchRule classesInControllerPackageShouldHaveControllerInName = classes()
.that().resideInAPackage("..controller..")
.should().haveSimpleNameEndingWith("Controller");
.should().haveSimpleNameEndingWith("Controller")
.orShould().haveSimpleNameEndingWith("ControllerAdvice");
@ArchTest
public static final ArchRule controllerClassesShouldNotDependOnEachOther = noClasses()

View File

@ -53,10 +53,10 @@ class APIControllerTest {
@Test
@WithMockUser(username = "api_user", roles = "api_user")
void updateGroup_noGroup() {
assertThat(apiController.updateGroups(0L).getGroupList()).hasSize(0);
assertThat(apiController.updateGroups(4L).getGroupList()).hasSize(0);
assertThat(apiController.updateGroups(10L).getGroupList()).hasSize(0);
assertThat(apiController.updateGroups(0L).getStatus()).isEqualTo(0);
assertThat(apiController.update(0L).getGroupList()).hasSize(0);
assertThat(apiController.update(4L).getGroupList()).hasSize(0);
assertThat(apiController.update(10L).getGroupList()).hasSize(0);
assertThat(apiController.update(0L).getStatus()).isEqualTo(0);
}
@Test
@ -69,10 +69,10 @@ class APIControllerTest {
addUserEvent(uuidMock(0)),
addUserEvent(uuidMock(0)));
assertThat(apiController.updateGroups(0L).getGroupList()).hasSize(1);
assertThat(apiController.updateGroups(4L).getGroupList()).hasSize(1);
assertThat(apiController.updateGroups(10L).getGroupList()).hasSize(0);
assertThat(apiController.updateGroups(0L).getStatus()).isEqualTo(6);
assertThat(apiController.update(0L).getGroupList()).hasSize(1);
assertThat(apiController.update(4L).getGroupList()).hasSize(1);
assertThat(apiController.update(10L).getGroupList()).hasSize(0);
assertThat(apiController.update(0L).getStatus()).isEqualTo(6);
}
@ -89,17 +89,17 @@ class APIControllerTest {
addUserEvent(uuidMock(1)),
addUserEvent(uuidMock(1)));
assertThat(apiController.updateGroups(0L).getGroupList()).hasSize(2);
assertThat(apiController.updateGroups(4L).getGroupList()).hasSize(1);
assertThat(apiController.updateGroups(6L).getGroupList()).hasSize(1);
assertThat(apiController.updateGroups(7L).getGroupList()).hasSize(1);
assertThat(apiController.updateGroups(0L).getStatus()).isEqualTo(9);
assertThat(apiController.update(0L).getGroupList()).hasSize(2);
assertThat(apiController.update(4L).getGroupList()).hasSize(1);
assertThat(apiController.update(6L).getGroupList()).hasSize(1);
assertThat(apiController.update(7L).getGroupList()).hasSize(1);
assertThat(apiController.update(0L).getStatus()).isEqualTo(9);
}
@Test
@WithMockUser(username = "api_user", roles = "api_user")
void getGroupsOfUser_noGroup() {
assertThat(apiController.getGroupIdsOfUser("A")).isEmpty();
assertThat(apiController.usergroups("A")).isEmpty();
}
@Test
@ -110,7 +110,7 @@ class APIControllerTest {
createPrivateGroupEvent(uuidMock(2)),
addUserEvent(uuidMock(0), "A"));
assertThat(apiController.getGroupIdsOfUser("A")).hasSize(1);
assertThat(apiController.usergroups("A")).hasSize(1);
}
@Test
@ -120,7 +120,7 @@ class APIControllerTest {
addUserEvent(uuidMock(0), "A"),
deleteUserEvent(uuidMock(0), "A"));
assertThat(apiController.getGroupIdsOfUser("A")).isEmpty();
assertThat(apiController.usergroups("A")).isEmpty();
}
@Disabled
@ -131,7 +131,7 @@ class APIControllerTest {
addUserEvent(uuidMock(0), "A"),
deleteGroupEvent(uuidMock(0)));
assertThat(apiController.getGroupIdsOfUser("A")).isEmpty();
assertThat(apiController.usergroups("A")).isEmpty();
}
@Test
@ -146,8 +146,8 @@ class APIControllerTest {
addUserEvent(uuidMock(2), "A"),
addUserEvent(uuidMock(2), "B"));
assertThat(apiController.getGroupIdsOfUser("A")).hasSize(3);
assertThat(apiController.getGroupIdsOfUser("B")).hasSize(2);
assertThat(apiController.usergroups("A")).hasSize(3);
assertThat(apiController.usergroups("B")).hasSize(2);
}
@Test

View File

@ -26,11 +26,9 @@ class UpdateGroupDescriptionEventTest {
void applyEvent_badDescription() {
Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEventA = new UpdateGroupDescriptionEvent(uuidMock(0), "A", "");
Event updateEventB = new UpdateGroupDescriptionEvent(uuidMock(0), "A", " ");
Group group = TestBuilder.apply(createEvent);
assertThrows(BadParameterException.class, () -> updateEventA.apply(group));
assertThrows(BadParameterException.class, () -> updateEventB.apply(group));
}
}

View File

@ -26,11 +26,9 @@ class UpdateGroupTitleEventTest {
void applyEvent_badDescription() {
Event createEvent = createPublicGroupEvent(uuidMock(0));
Event updateEventA = new UpdateGroupTitleEvent(uuidMock(0), "A", "");
Event updateEventB = new UpdateGroupTitleEvent(uuidMock(0), "A", " ");
Group group = TestBuilder.apply(createEvent);
assertThrows(BadParameterException.class, () -> updateEventA.apply(group));
assertThrows(BadParameterException.class, () -> updateEventB.apply(group));
}
}