1
.gitignore
vendored
1
.gitignore
vendored
@ -34,3 +34,4 @@ out/
|
||||
.flooignore
|
||||
|
||||
/mysql/db/storage/
|
||||
/mysql/keycloak/
|
||||
|
37
README.adoc
37
README.adoc
@ -8,6 +8,43 @@ Private Gruppen kann man nur über einen Beitrittslink beitreten.
|
||||
Öffentliche Gruppen kann man ohne diesen beitreten.
|
||||
Man kann nach Öffentlichen Gruppen über eine Suchfunktion suchen.
|
||||
|
||||
== Stand
|
||||
|
||||
=== Probleme
|
||||
|
||||
* Momentan ist kein Controller getestet
|
||||
* Das Styling ist inkonsistent und skaliert nicht gut auf kleineren screens
|
||||
* Schlecht dokumentiert und Arc42 nicht aktuell
|
||||
* Integrationen nicht implementiert
|
||||
* Gruppenoptionen nicht implementiert
|
||||
* Snapshotting nicht implementiert
|
||||
* Caching funktioniert, aber teilweise nicht konsequent verwendet
|
||||
* Gruppenbeschreibung mit markup nicht implementiert
|
||||
* Invitelink kann nicht regeneriert werden
|
||||
* Seit Implementierung eines Caches ist die API kaputt
|
||||
|
||||
=== Fertig
|
||||
|
||||
* Fast die komplette Logik + Templates + Controller überholt
|
||||
* Verwendung eines Caches anstatt vieler Datenbankanfragen
|
||||
* UI: Templates mit Fragmenten und auslagertem styling vereinfacht, einige Seiten zusammengefasst
|
||||
* Services komplett umstrukturiert, ergeben inhaltlich mehr Sinn und sind kleiner
|
||||
* Konsequenteres Logging, aspektorientiertes Logging
|
||||
* Aktivere Gruppenobjekte mit mehr Validierung
|
||||
* Extratabelle für Invitelinks entfernt, Datenbank vereinfacht
|
||||
* Man kann Änderungen an Gruppen nachvollziehen mit Zeitstempeln und einer Übersichtsseite
|
||||
* Gruppen und Teilnehmerlisten können exportiert werden
|
||||
|
||||
=== Heroku
|
||||
|
||||
Die letzte Version der Anwendung ist unter gruppenfindung.herokuapp.com zu erreichen.
|
||||
Es existieren zwei Defaultuser:
|
||||
|
||||
* Username: orga, Passwort: orga
|
||||
* Username: studentin, Passwort: studentin
|
||||
|
||||
== Ursprüngliche Readme
|
||||
|
||||
=== Problem
|
||||
|
||||
Die meisten Teilsysteme von MOPS arbeiten mit Gruppierungen von Studenten: Materialien für Lerngruppen/Veranstaltungen, Gruppenportfolios, Gruppenabstimmungen etc.
|
||||
|
51
build.gradle
51
build.gradle
@ -1,11 +1,9 @@
|
||||
import com.github.spotbugs.SpotBugsTask
|
||||
|
||||
plugins {
|
||||
id 'org.springframework.boot' version '2.2.5.RELEASE'
|
||||
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
|
||||
id 'java'
|
||||
|
||||
id 'com.github.spotbugs' version '3.0.0'
|
||||
id 'com.github.spotbugs' version '4.0.1'
|
||||
id 'checkstyle'
|
||||
id 'pmd'
|
||||
}
|
||||
@ -14,34 +12,43 @@ group = 'mops'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
sourceCompatibility = '11'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://s3.cs.hhu.de/public/mops/'
|
||||
metadataSources {
|
||||
artifact()
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
spotbugs {
|
||||
toolVersion = '4.0.1'
|
||||
ignoreFailures = false
|
||||
reportLevel = "high"
|
||||
effort = "max"
|
||||
toolVersion = '4.0.0-RC1'
|
||||
showProgress = true
|
||||
}
|
||||
|
||||
tasks.withType(SpotBugsTask) {
|
||||
spotbugsMain {
|
||||
reports {
|
||||
xml.enabled = false
|
||||
html.enabled = true
|
||||
html {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pmd {
|
||||
consoleOutput = true
|
||||
ignoreFailures = true
|
||||
toolVersion = "6.21.0"
|
||||
toolVersion = "6.22.0"
|
||||
rulePriority = 5
|
||||
ruleSets = ["category/java/errorprone.xml",
|
||||
"category/java/bestpractices.xml",
|
||||
"category/java/security.xml",
|
||||
"category/java/performance.xml",
|
||||
"category/java/design.xml"]
|
||||
ruleSetFiles = files("config/pmd/ruleset.xml")
|
||||
ruleSets = []
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.28"
|
||||
toolVersion = "8.30"
|
||||
configFile = file("${rootDir}/config/checkstyle/checkstyle.xml")
|
||||
ignoreFailures = true
|
||||
}
|
||||
@ -56,25 +63,18 @@ configurations {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://s3.cs.hhu.de/public/mops/'
|
||||
metadataSources {
|
||||
artifact()
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
|
||||
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'
|
||||
@ -82,10 +82,11 @@ dependencies {
|
||||
implementation 'io.springfox:springfox-swagger-ui:2.9.2'
|
||||
implementation 'com.github.javafaker:javafaker:1.0.2'
|
||||
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.10.3'
|
||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.3'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
|
||||
runtimeOnly 'com.h2database:h2'
|
||||
runtimeOnly 'mysql:mysql-connector-java'
|
||||
|
||||
|
@ -223,7 +223,7 @@
|
||||
<property name="braceAdjustment" value="0"/>
|
||||
<property name="caseIndent" value="4"/>
|
||||
<property name="throwsIndent" value="4"/>
|
||||
<property name="lineWrappingIndentation" value="8"/>
|
||||
<!--<property name="lineWrappingIndentation" value="8"/>-->
|
||||
<property name="arrayInitIndent" value="4"/>
|
||||
</module>
|
||||
<!-- <module name="VariableDeclarationUsageDistance"/>-->
|
||||
|
335
config/pmd/ruleset.xml
Normal file
335
config/pmd/ruleset.xml
Normal file
@ -0,0 +1,335 @@
|
||||
<ruleset name="quickstart"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
||||
|
||||
<description>PMD Rules</description>
|
||||
|
||||
<!-- BEST PRACTICES -->
|
||||
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AccessorClassGeneration" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AccessorMethodGeneration" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/ArrayIsStoredDirectly" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidPrintStackTrace" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/AvoidReassigningParameters" /> -->
|
||||
<rule ref="category/java/bestpractices.xml/AvoidStringBufferField"/>
|
||||
<rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP"/>
|
||||
<rule ref="category/java/bestpractices.xml/CheckResultSet"/>
|
||||
<rule ref="category/java/bestpractices.xml/ConstantsInInterface"/>
|
||||
<rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt"/>
|
||||
<rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/GuardLogStatement"/>-->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseAfterAnnotation" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseBeforeAnnotation" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseTestAnnotation" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnitAssertionsShouldIncludeMessage" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnitTestContainsTooManyAsserts" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnitTestsShouldIncludeAssert" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/JUnitUseExpected" /> -->
|
||||
<rule ref="category/java/bestpractices.xml/LooseCoupling"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/MethodReturnsInternalArray" /> -->
|
||||
<rule ref="category/java/bestpractices.xml/MissingOverride"/>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/OneDeclarationPerLine"/>
|
||||
<rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInCaseInsensitiveComparisons"/>
|
||||
<rule ref="category/java/bestpractices.xml/PositionLiteralsFirstInComparisons"/>
|
||||
<rule ref="category/java/bestpractices.xml/PreserveStackTrace"/>
|
||||
|
||||
<!-- <rule ref="category/java/bestpractices.xml/ReplaceEnumerationWithIterator" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/ReplaceHashtableWithMap" /> -->
|
||||
<!-- <rule ref="category/java/bestpractices.xml/ReplaceVectorWithList" /> -->
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/SwitchStmtsShouldHaveDefault"/>
|
||||
|
||||
<!-- <rule ref="category/java/bestpractices.xml/SystemPrintln" /> -->
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/UnusedImports"/>
|
||||
<rule ref="category/java/errorprone.xml/ImportFromSamePackage"/>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
|
||||
<rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertEqualsInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertNullInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertSameInsteadOfAssertTrue"/>
|
||||
<rule ref="category/java/bestpractices.xml/UseAssertTrueInsteadOfAssertEquals"/>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/UseCollectionIsEmpty"/>
|
||||
<!-- <rule ref="category/java/bestpractices.xml/UseVarargs" /> -->
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/AtLeastOneConstructor" /> -->
|
||||
<rule ref="category/java/codestyle.xml/AvoidDollarSigns"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/AvoidFinalLocalVariable" /> -->
|
||||
|
||||
<rule ref="category/java/codestyle.xml/AvoidProtectedFieldInFinalClass"/>
|
||||
|
||||
<rule ref="category/java/codestyle.xml/AvoidProtectedMethodInFinalClassNotExtending"/>
|
||||
|
||||
<!-- NAMING CONVENTIONS -->
|
||||
|
||||
<rule ref="category/java/codestyle.xml/FormalParameterNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/ClassNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/LocalVariableNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/MethodNamingConventions"/>
|
||||
<rule ref="category/java/codestyle.xml/PackageCase"/>
|
||||
|
||||
<!-- UNIMPLEMENTED -->
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/FieldNamingConventions" /> -->
|
||||
<rule ref="category/java/codestyle.xml/GenericsNaming"/>
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/LongVariable" /> -->
|
||||
<rule ref="category/java/codestyle.xml/ShortClassName"/>
|
||||
<rule ref="category/java/codestyle.xml/ShortMethodName"/>
|
||||
<rule ref="category/java/codestyle.xml/ShortVariable"/>
|
||||
|
||||
<!-- OTHER -->
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/AvoidUsingNativeCode" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/BooleanGetMethodName" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/CallSuperInConstructor" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/CommentDefaultAccessModifier" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/ConfusingTernary" /> -->
|
||||
<rule ref="category/java/codestyle.xml/ControlStatementBraces"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/DefaultPackage" /> -->
|
||||
|
||||
<rule ref="category/java/codestyle.xml/DontImportJavaLang"/>
|
||||
<rule ref="category/java/codestyle.xml/DuplicateImports"/>
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/EmptyMethodInAbstractClassShouldBeAbstract" /> -->
|
||||
|
||||
<rule ref="category/java/codestyle.xml/ExtendsObject"/>
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/FieldDeclarationsShouldBeAtStartOfClass" /> -->
|
||||
<rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop"/>
|
||||
|
||||
<rule ref="category/java/codestyle.xml/IdenticalCatchBranches"/>
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/LocalVariableCouldBeFinal" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/MethodArgumentCouldBeFinal" /> -->
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/MIsLeadingVariableName" /> -->
|
||||
<rule ref="category/java/codestyle.xml/NoPackage"/>
|
||||
<!-- <rule ref="category/java/codestyle.xml/OnlyOneReturn" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/PrematureDeclaration" /> -->
|
||||
|
||||
<!-- <rule ref="category/java/codestyle.xml/SuspiciousConstantFieldName" /> -->
|
||||
<!-- <rule ref="category/java/codestyle.xml/TooManyStaticImports" /> -->
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryAnnotationValueElement"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryConstructor"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryModifier"/>
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryReturn"/>
|
||||
<rule ref="category/java/codestyle.xml/UselessParentheses"/>
|
||||
<rule ref="category/java/codestyle.xml/UselessQualifiedThis"/>
|
||||
|
||||
<rule ref="category/java/design.xml/AbstractClassWithoutAnyMethod"/>
|
||||
<!-- <rule ref="category/java/design.xml/AvoidCatchingGenericException" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/AvoidDeeplyNestedIfStmts" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/AvoidRethrowingException" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/AvoidThrowingNewInstanceOfSameException" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/AvoidThrowingNullPointerException" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/AvoidThrowingRawExceptionTypes" /> -->
|
||||
<rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal"/>
|
||||
<!-- <rule ref="category/java/design.xml/CollapsibleIfStatements" /> -->
|
||||
<rule ref="category/java/design.xml/CouplingBetweenObjects"/>
|
||||
<!-- <rule ref="category/java/design.xml/DataClass" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/DoNotExtendJavaLangError" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/ExceptionAsFlowControl" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/ExcessiveClassLength" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/ExcessiveImports" /> -->
|
||||
<rule ref="category/java/design.xml/ExcessiveMethodLength"/>
|
||||
<rule ref="category/java/design.xml/ExcessiveParameterList"/>
|
||||
<rule ref="category/java/design.xml/ExcessivePublicCount"/>
|
||||
<rule ref="category/java/design.xml/FinalFieldCouldBeStatic"/>
|
||||
<!-- <rule ref="category/java/design.xml/GodClass" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/ImmutableField" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/LawOfDemeter"/>-->
|
||||
<rule ref="category/java/design.xml/LogicInversion"/>
|
||||
|
||||
<rule ref="category/java/design.xml/CyclomaticComplexity"/>
|
||||
<!-- <rule ref="category/java/design.xml/NcssCount" /> -->
|
||||
<rule ref="category/java/design.xml/NPathComplexity"/>
|
||||
|
||||
<!-- <rule ref="category/java/design.xml/SignatureDeclareThrowsException" /> -->
|
||||
|
||||
<rule ref="category/java/design.xml/SimplifiedTernary"/>
|
||||
|
||||
<!-- <rule ref="category/java/design.xml/SimplifyBooleanAssertion" /> -->
|
||||
<!-- <rule ref="category/java/design.xml/SimplifyBooleanExpressions" /> -->
|
||||
|
||||
<rule ref="category/java/design.xml/SimplifyBooleanReturns"/>
|
||||
<rule ref="category/java/design.xml/SimplifyConditional"/>
|
||||
<rule ref="category/java/design.xml/SingularField"/>
|
||||
<!-- <rule ref="category/java/design.xml/SwitchDensity" /> -->
|
||||
<rule ref="category/java/design.xml/TooManyFields"/>
|
||||
<rule ref="category/java/design.xml/TooManyMethods"/>
|
||||
<rule ref="category/java/design.xml/UselessOverridingMethod"/>
|
||||
<!-- <rule ref="category/java/design.xml/UseObjectForClearerAPI" /> -->
|
||||
<rule ref="category/java/design.xml/UseUtilityClass"/>
|
||||
|
||||
<!-- <rule ref="category/java/documentation.xml/CommentContent" /> -->
|
||||
<!-- <rule ref="category/java/documentation.xml/CommentRequired" /> -->
|
||||
<!-- <rule ref="category/java/documentation.xml/CommentSize" /> -->
|
||||
<rule ref="category/java/documentation.xml/UncommentedEmptyConstructor"/>
|
||||
<rule ref="category/java/documentation.xml/UncommentedEmptyMethodBody"/>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/AssignmentInOperand">
|
||||
<properties>
|
||||
<property name="allowWhile" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="category/java/errorprone.xml/AssignmentToNonFinalStatic"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidAccessibilityAlteration"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidAssertAsIdentifier" /> -->
|
||||
<rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidCallingFinalize" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidCatchingNPE" /> -->
|
||||
<rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingMethodName" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingTypeName" /> -->
|
||||
<rule ref="category/java/errorprone.xml/AvoidInstanceofChecksInCatchClause"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidLiteralsInIfCondition" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/AvoidLosingExceptionInformation" /> -->
|
||||
<rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators"/>
|
||||
<rule ref="category/java/errorprone.xml/AvoidUsingOctalValues"/>
|
||||
<rule ref="category/java/errorprone.xml/BadComparison"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/BeanMembersShouldSerialize" /> -->
|
||||
<rule ref="category/java/errorprone.xml/BrokenNullCheck"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/CallSuperFirst" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/CallSuperLast" /> -->
|
||||
<rule ref="category/java/errorprone.xml/CheckSkipResult"/>
|
||||
<rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneMethodMustBePublic"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneMethodMustImplementCloneable"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneMethodReturnTypeMustMatchClassName"/>
|
||||
<rule ref="category/java/errorprone.xml/CloneThrowsCloneNotSupportedException"/>
|
||||
<rule ref="category/java/errorprone.xml/CloseResource"/>
|
||||
<rule ref="category/java/errorprone.xml/CompareObjectsWithEquals"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/ConstructorCallsOverridableMethod" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/DataflowAnomalyAnalysis" /> -->
|
||||
<rule ref="category/java/errorprone.xml/DoNotCallGarbageCollectionExplicitly"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/DoNotCallSystemExit" /> -->
|
||||
|
||||
<rule ref="category/java/errorprone.xml/DoNotExtendJavaLangThrowable"/>
|
||||
<rule ref="category/java/design.xml/DoNotExtendJavaLangError"/>
|
||||
|
||||
<!-- <rule ref="category/java/errorprone.xml/DoNotHardCodeSDCard" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/DoNotThrowExceptionInFinally" /> -->
|
||||
|
||||
<!-- <rule ref="category/java/errorprone.xml/DontImportSun" /> -->
|
||||
|
||||
<rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyCatchBlock"/>
|
||||
|
||||
<!-- EMPTY RULES -->
|
||||
|
||||
<rule ref="category/java/errorprone.xml/EmptyFinalizer"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyFinallyBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyIfStmt"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyInitializer"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyStatementBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyStatementNotInLoop"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptySwitchStatements"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptySynchronizedBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyTryBlock"/>
|
||||
<rule ref="category/java/errorprone.xml/EmptyWhileStmt"/>
|
||||
|
||||
<rule ref="category/java/errorprone.xml/EqualsNull"/>
|
||||
|
||||
<!-- <rule ref="category/java/errorprone.xml/FinalizeDoesNotCallSuperFinalize" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/FinalizeOnlyCallsSuperFinalize" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/FinalizeOverloaded" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/FinalizeShouldBeProtected" /> -->
|
||||
<rule ref="category/java/errorprone.xml/IdempotentOperations"/>
|
||||
<rule ref="category/java/errorprone.xml/InstantiationToGetClass"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/InvalidSlf4jMessageFormat" /> -->
|
||||
<rule ref="category/java/errorprone.xml/JumbledIncrementer"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/JUnitSpelling" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/JUnitStaticSuite" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/LoggerIsNotStaticFinal" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/MethodWithSameNameAsEnclosingClass" /> -->
|
||||
<rule ref="category/java/errorprone.xml/MisplacedNullCheck"/>
|
||||
<rule ref="category/java/errorprone.xml/MissingBreakInSwitch"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/MissingSerialVersionUID" /> -->
|
||||
<rule ref="category/java/errorprone.xml/MissingStaticMethodInNonInstantiatableClass"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/MoreThanOneLogger" /> -->
|
||||
<rule ref="category/java/errorprone.xml/NonCaseLabelInSwitchStatement"/>
|
||||
<rule ref="category/java/errorprone.xml/NonStaticInitializer"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/NullAssignment" /> -->
|
||||
<rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode"/>
|
||||
<rule ref="category/java/errorprone.xml/ProperCloneImplementation"/>
|
||||
<rule ref="category/java/errorprone.xml/ProperLogger"/>
|
||||
<rule ref="category/java/errorprone.xml/ReturnEmptyArrayRatherThanNull"/>
|
||||
<rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/SimpleDateFormatNeedsLocale" /> -->
|
||||
<rule ref="category/java/errorprone.xml/SingleMethodSingleton"/>
|
||||
<rule ref="category/java/errorprone.xml/SingletonClassReturningNewInstance"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/StaticEJBFieldShouldBeFinal" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/StringBufferInstantiationWithChar" /> -->
|
||||
<rule ref="category/java/errorprone.xml/SuspiciousEqualsMethodName"/>
|
||||
<rule ref="category/java/errorprone.xml/SuspiciousHashcodeMethodName"/>
|
||||
<rule ref="category/java/errorprone.xml/SuspiciousOctalEscape"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/TestClassWithoutTestCases" /> -->
|
||||
<rule ref="category/java/errorprone.xml/UnconditionalIfStatement"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/UnnecessaryBooleanAssertion" /> -->
|
||||
<!-- <rule ref="category/java/errorprone.xml/UnnecessaryCaseChange" /> -->
|
||||
<rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary"/>
|
||||
<rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/UseCorrectExceptionLogging" /> -->
|
||||
<rule ref="category/java/errorprone.xml/UseEqualsToCompareStrings"/>
|
||||
<rule ref="category/java/errorprone.xml/UselessOperationOnImmutable"/>
|
||||
<rule ref="category/java/errorprone.xml/UseLocaleWithCaseConversions"/>
|
||||
<!-- <rule ref="category/java/errorprone.xml/UseProperClassLoader" /> -->
|
||||
|
||||
<!-- <rule ref="category/java/multithreading.xml/AvoidSynchronizedAtMethodLevel" /> -->
|
||||
<rule ref="category/java/multithreading.xml/AvoidThreadGroup"/>
|
||||
<rule ref="category/java/multithreading.xml/AvoidUsingVolatile"/>
|
||||
<!-- <rule ref="category/java/multithreading.xml/DoNotUseThreads" /> -->
|
||||
<rule ref="category/java/multithreading.xml/DontCallThreadRun"/>
|
||||
<rule ref="category/java/multithreading.xml/DoubleCheckedLocking"/>
|
||||
<rule ref="category/java/multithreading.xml/NonThreadSafeSingleton"/>
|
||||
<!-- <rule ref="category/java/multithreading.xml/UnsynchronizedStaticDateFormatter"/>-->
|
||||
<!-- <rule ref="category/java/multithreading.xml/UseConcurrentHashMap" /> -->
|
||||
<rule ref="category/java/multithreading.xml/UseNotifyAllInsteadOfNotify"/>
|
||||
|
||||
<!-- <rule ref="category/java/performance.xml/AddEmptyString" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/AppendCharacterWithChar" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/AvoidArrayLoops" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/AvoidFileStream" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/AvoidInstantiatingObjectsInLoops" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/AvoidUsingShortType" /> -->
|
||||
<rule ref="category/java/performance.xml/BigIntegerInstantiation"/>
|
||||
<rule ref="category/java/performance.xml/BooleanInstantiation"/>
|
||||
<!-- <rule ref="category/java/performance.xml/ByteInstantiation" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/ConsecutiveAppendsShouldReuse" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/ConsecutiveLiteralAppends" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/InefficientEmptyStringCheck" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/InefficientStringBuffering" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/InsufficientStringBufferDeclaration" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/IntegerInstantiation" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/LongInstantiation" /> -->
|
||||
<rule ref="category/java/performance.xml/OptimizableToArrayCall"/>
|
||||
<!-- <rule ref="category/java/performance.xml/RedundantFieldInitializer" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/SimplifyStartsWith" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/ShortInstantiation" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/StringInstantiation" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/StringToString" /> -->
|
||||
<rule ref="category/java/performance.xml/TooFewBranchesForASwitchStatement"/>
|
||||
<!-- <rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/UseArrayListInsteadOfVector" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/UseArraysAsList" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/UseIndexOfChar" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/UselessStringValueOf" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/UseStringBufferForStringAppends" /> -->
|
||||
<!-- <rule ref="category/java/performance.xml/UseStringBufferLength" /> -->
|
||||
</ruleset>
|
@ -1,23 +1,50 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
dbmysql:
|
||||
image: mysql:5.7
|
||||
image: mysql:8.0
|
||||
container_name: 'dbmysql'
|
||||
environment:
|
||||
MYSQL_DATABASE: 'gruppen2'
|
||||
MYSQL_USER: 'root'
|
||||
MYSQL_ROOT_PASSWORD: 'geheim'
|
||||
MYSQL_DATABASE: 'gruppen'
|
||||
MYSQL_USER: 'gruppen'
|
||||
MYSQL_PASSWORD: 'password'
|
||||
MYSQL_ROOT_PASSWORD: 'root'
|
||||
restart: always
|
||||
volumes:
|
||||
- './mysql/db/storage:/var/lib/mysql'
|
||||
- './mysql/db/entrypoint:/docker-entrypoint-initdb.d/'
|
||||
ports:
|
||||
- '3306:3306'
|
||||
|
||||
keymysql:
|
||||
image: mysql:8.0
|
||||
container_name: 'keymysql'
|
||||
environment:
|
||||
MYSQL_DATABASE: 'keycloak'
|
||||
MYSQL_USER: 'keycloak'
|
||||
MYSQL_PASSWORD: 'password'
|
||||
MYSQL_ROOT_PASSWORD: 'root'
|
||||
volumes:
|
||||
- './mysql/keycloak/storage:/var/lib/mysql'
|
||||
keycloak:
|
||||
image: jboss/keycloak
|
||||
container_name: 'keycloak'
|
||||
depends_on:
|
||||
- keymysql
|
||||
environment:
|
||||
DB_VENDOR: 'MYSQL'
|
||||
DB_ADDR: 'keymysql'
|
||||
DB_DATABASE: 'keycloak'
|
||||
DB_USER: 'keycloak'
|
||||
DB_PASSWORD: 'password'
|
||||
KEYCLOAK_USER: 'admin'
|
||||
KEYCLOAK_PASSWORD: 'admin'
|
||||
ports:
|
||||
- '8082:8080'
|
||||
|
||||
gruppenapp:
|
||||
build: .
|
||||
container_name: 'gruppenapp'
|
||||
depends_on:
|
||||
- dbmysql
|
||||
- keycloak
|
||||
command: ["/app/wait-for-it.sh", "dbmysql:3306", "--", "java", "-Dspring.profiles.active=docker", "-jar", "/app/gruppen2.jar"]
|
||||
ports:
|
||||
- '8081:8080'
|
||||
|
20
gruppe.yml
20
gruppe.yml
@ -1,20 +0,0 @@
|
||||
# Bei "schicht" können Sie 'vormittags', 'nachmittags' oder 'egal' eintragen.
|
||||
|
||||
gruppe: IT-Bois
|
||||
url: https://github.com/hhu-propra2/abschlussprojekt-it-bois
|
||||
names:
|
||||
- killerber4t
|
||||
- tomvahl
|
||||
- AndiBuls
|
||||
- XXNitram
|
||||
- LukasEttel
|
||||
- Mahgs
|
||||
- ChUrl
|
||||
- kasch309
|
||||
notebook: true
|
||||
schicht: nachmittags
|
||||
projektauswahl:
|
||||
- Gruppenbildung
|
||||
- Punkteübersicht
|
||||
- Materialsammlung
|
||||
- Korrektorinnen Bewerbung
|
2
lombok.config
Normal file
2
lombok.config
Normal file
@ -0,0 +1,2 @@
|
||||
lombok.anyConstructor.addConstructorProperties = true
|
||||
lombok.equalsAndHashCode.callSuper = call
|
@ -1,15 +1,10 @@
|
||||
CREATE TABLE event
|
||||
(
|
||||
event_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
group_id VARCHAR(36) NOT NULL,
|
||||
user_id VARCHAR(50),
|
||||
event_type VARCHAR(36),
|
||||
event_payload JSON
|
||||
);
|
||||
|
||||
CREATE TABLE invite
|
||||
(
|
||||
invite_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
group_id VARCHAR(36) NOT NULL,
|
||||
invite_link VARCHAR(36) NOT NULL
|
||||
event_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
group_id VARCHAR(36) NOT NULL,
|
||||
group_version INT NOT NULL,
|
||||
exec_id VARCHAR(32) NOT NULL,
|
||||
target_id VARCHAR(32),
|
||||
event_date DATETIME NOT NULL,
|
||||
event_payload JSON NOT NULL
|
||||
);
|
||||
|
12
mysql/db/schema-heroku.sql
Normal file
12
mysql/db/schema-heroku.sql
Normal file
@ -0,0 +1,12 @@
|
||||
DROP TABLE IF EXISTS event;
|
||||
|
||||
CREATE TABLE event
|
||||
(
|
||||
event_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
group_id VARCHAR(36) NOT NULL,
|
||||
group_version INT NOT NULL,
|
||||
exec_id VARCHAR(32) NOT NULL,
|
||||
target_id VARCHAR(32),
|
||||
event_date DATETIME NOT NULL,
|
||||
event_payload TEXT NOT NULL
|
||||
);
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
62
src/main/java/mops/gruppen2/aspect/LogAspect.java
Normal file
62
src/main/java/mops/gruppen2/aspect/LogAspect.java
Normal 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 static void logCustom(JoinPoint joinPoint) {
|
||||
log.trace(((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Trace.class).value());
|
||||
}
|
||||
|
||||
@Before("@annotation(mops.gruppen2.aspect.annotation.TraceMethodCall) || logMethodCalls()")
|
||||
public static 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 static 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();
|
||||
}
|
||||
}
|
16
src/main/java/mops/gruppen2/aspect/annotation/Trace.java
Normal file
16
src/main/java/mops/gruppen2/aspect/annotation/Trace.java
Normal 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();
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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 {
|
||||
}
|
15
src/main/java/mops/gruppen2/config/FormatterConfig.java
Normal file
15
src/main/java/mops/gruppen2/config/FormatterConfig.java
Normal 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 FormatterConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
registry.addConverter(new StringToLimitConverter());
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package mops.gruppen2.config;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
|
||||
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -15,15 +16,16 @@ import org.springframework.web.client.RestTemplate;
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@KeycloakConfiguration
|
||||
public class KeycloakConfig {
|
||||
|
||||
@Value("${keycloak.resource}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${keycloak.credentials.secret}")
|
||||
@Value("2e2e5770-c454-4d31-be99-9d8c34c93089")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${hhu_keycloak.token-uri}")
|
||||
@Value("https://churl-keycloak.herokuapp.com/auth/realms/Gruppen/protocol/openid-connect/token")
|
||||
private String tokenUri;
|
||||
|
||||
@Bean
|
||||
|
@ -29,7 +29,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
|
||||
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
||||
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
|
43
src/main/java/mops/gruppen2/config/SwaggerConfig.java
Normal file
43
src/main/java/mops/gruppen2/config/SwaggerConfig.java
Normal file
@ -0,0 +1,43 @@
|
||||
package mops.gruppen2.config;
|
||||
|
||||
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
|
||||
@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()
|
||||
);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
package mops.gruppen2.controller;
|
||||
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.api.GroupRequestWrapper;
|
||||
import mops.gruppen2.domain.event.Event;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.service.APIFormatterService;
|
||||
import mops.gruppen2.service.EventService;
|
||||
import mops.gruppen2.service.GroupService;
|
||||
import mops.gruppen2.service.UserService;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Api zum Datenabgleich mit Gruppenfindung.
|
||||
*/
|
||||
//TODO: API-Service?
|
||||
@RestController
|
||||
@RequestMapping("/gruppen2/api")
|
||||
public class APIController {
|
||||
|
||||
private final EventService eventService;
|
||||
private final GroupService groupService;
|
||||
private final UserService userService;
|
||||
|
||||
public APIController(EventService eventService, GroupService groupService, UserService userService) {
|
||||
this.eventService = eventService;
|
||||
this.groupService = groupService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/updateGroups/{lastEventId}")
|
||||
@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 {
|
||||
List<Event> events = eventService.getNewEvents(lastEventId);
|
||||
|
||||
return APIFormatterService.wrap(eventService.getMaxEventId(), groupService.projectEventList(events));
|
||||
}
|
||||
|
||||
@GetMapping("/getGroupIdsOfUser/{userId}")
|
||||
@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) {
|
||||
return userService.getUserGroups(userId).stream()
|
||||
.map(group -> group.getId().toString())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("/getGroup/{groupId}")
|
||||
@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 {
|
||||
List<Event> eventList = eventService.getEventsOfGroup(UUID.fromString(groupId));
|
||||
List<Group> groups = groupService.projectEventList(eventList);
|
||||
|
||||
if (groups.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return groups.get(0);
|
||||
}
|
||||
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
package mops.gruppen2.controller;
|
||||
|
||||
import mops.gruppen2.domain.Account;
|
||||
import mops.gruppen2.service.ControllerService;
|
||||
import mops.gruppen2.service.GroupService;
|
||||
import mops.gruppen2.service.KeyCloakService;
|
||||
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.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;
|
||||
import java.util.UUID;
|
||||
|
||||
@Controller
|
||||
@SessionScope
|
||||
@RequestMapping("/gruppen2")
|
||||
public class GroupCreationController {
|
||||
|
||||
private final GroupService groupService;
|
||||
private final ControllerService controllerService;
|
||||
private final ValidationService validationService;
|
||||
|
||||
public GroupCreationController(GroupService groupService, ControllerService controllerService, ValidationService validationService) {
|
||||
this.groupService = groupService;
|
||||
this.controllerService = controllerService;
|
||||
this.validationService = validationService;
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_actuator"})
|
||||
@GetMapping("/createOrga")
|
||||
public String createGroupAsOrga(KeycloakAuthenticationToken token,
|
||||
Model model) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
|
||||
model.addAttribute("account", account);
|
||||
model.addAttribute("lectures", groupService.getAllLecturesWithVisibilityPublic());
|
||||
|
||||
return "createOrga";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_actuator"})
|
||||
@PostMapping("/createOrga")
|
||||
@CacheEvict(value = "groups", allEntries = true)
|
||||
public String postCrateGroupAsOrga(KeycloakAuthenticationToken token,
|
||||
@RequestParam("title") String title,
|
||||
@RequestParam("description") String description,
|
||||
@RequestParam(value = "visibility", required = false) Boolean visibility,
|
||||
@RequestParam(value = "lecture", required = false) Boolean lecture,
|
||||
@RequestParam("userMaximum") Long userMaximum,
|
||||
@RequestParam(value = "maxInfiniteUsers", required = false) Boolean maxInfiniteUsers,
|
||||
@RequestParam(value = "parent", required = false) String parent,
|
||||
@RequestParam(value = "file", required = false) MultipartFile file) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
UUID parentUUID = controllerService.getUUID(parent);
|
||||
|
||||
validationService.checkFields(description, title, userMaximum, maxInfiniteUsers);
|
||||
|
||||
controllerService.createGroupAsOrga(account,
|
||||
title,
|
||||
description,
|
||||
visibility,
|
||||
lecture,
|
||||
maxInfiniteUsers,
|
||||
userMaximum,
|
||||
parentUUID,
|
||||
file);
|
||||
return "redirect:/gruppen2";
|
||||
}
|
||||
|
||||
@RolesAllowed("ROLE_studentin")
|
||||
@GetMapping("/createStudent")
|
||||
public String createGroupAsStudent(KeycloakAuthenticationToken token,
|
||||
Model model) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
|
||||
model.addAttribute("account", account);
|
||||
model.addAttribute("lectures", groupService.getAllLecturesWithVisibilityPublic());
|
||||
|
||||
return "createStudent";
|
||||
}
|
||||
|
||||
@RolesAllowed("ROLE_studentin")
|
||||
@PostMapping("/createStudent")
|
||||
@CacheEvict(value = "groups", allEntries = true)
|
||||
public String postCreateGroupAsStudent(KeycloakAuthenticationToken token,
|
||||
@RequestParam("title") String title,
|
||||
@RequestParam("description") String description,
|
||||
@RequestParam("userMaximum") Long userMaximum,
|
||||
@RequestParam(value = "visibility", required = false) Boolean visibility,
|
||||
@RequestParam(value = "maxInfiniteUsers", required = false) Boolean maxInfiniteUsers,
|
||||
@RequestParam(value = "parent", required = false) String parent) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
UUID parentUUID = controllerService.getUUID(parent);
|
||||
|
||||
validationService.checkFields(description, title, userMaximum, maxInfiniteUsers);
|
||||
|
||||
controllerService.createGroup(account,
|
||||
title,
|
||||
description,
|
||||
visibility,
|
||||
null,
|
||||
maxInfiniteUsers,
|
||||
userMaximum,
|
||||
parentUUID);
|
||||
|
||||
return "redirect:/gruppen2";
|
||||
}
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
package mops.gruppen2.controller;
|
||||
|
||||
import mops.gruppen2.domain.Account;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.Role;
|
||||
import mops.gruppen2.domain.User;
|
||||
import mops.gruppen2.domain.Visibility;
|
||||
import mops.gruppen2.service.ControllerService;
|
||||
import mops.gruppen2.service.InviteService;
|
||||
import mops.gruppen2.service.KeyCloakService;
|
||||
import mops.gruppen2.service.UserService;
|
||||
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 org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.UUID;
|
||||
|
||||
@Controller
|
||||
@SessionScope
|
||||
@RequestMapping("/gruppen2")
|
||||
public class GroupDetailsController {
|
||||
|
||||
private final ControllerService controllerService;
|
||||
private final UserService userService;
|
||||
private final ValidationService validationService;
|
||||
private final InviteService inviteService;
|
||||
|
||||
public GroupDetailsController(ControllerService controllerService, UserService userService, ValidationService validationService, InviteService inviteService) {
|
||||
this.controllerService = controllerService;
|
||||
this.userService = userService;
|
||||
this.validationService = validationService;
|
||||
this.inviteService = inviteService;
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@GetMapping("/details/{id}")
|
||||
public String showGroupDetails(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
HttpServletRequest request,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
UUID parentId = group.getParent();
|
||||
String actualURL = request.getRequestURL().toString();
|
||||
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
|
||||
Group parent = controllerService.getParent(parentId);
|
||||
|
||||
validationService.throwIfGroupNotExisting(group.getTitle());
|
||||
|
||||
model.addAttribute("account", account);
|
||||
if (!validationService.checkIfUserInGroup(group, user)) {
|
||||
validationService.throwIfNoAccessToPrivate(group, user);
|
||||
model.addAttribute("group", group);
|
||||
model.addAttribute("parentId", parentId);
|
||||
model.addAttribute("parent", parent);
|
||||
return "detailsNoMember";
|
||||
}
|
||||
|
||||
model.addAttribute("parentId", parentId);
|
||||
model.addAttribute("parent", parent);
|
||||
model.addAttribute("group", 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);
|
||||
|
||||
if (validationService.checkIfAdmin(group, user)) {
|
||||
model.addAttribute("link", serverURL + "gruppen2/acceptinvite/" + inviteService.getLinkByGroupId(group.getId()));
|
||||
}
|
||||
|
||||
return "detailsMember";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@GetMapping("/details/changeMetadata/{id}")
|
||||
public String changeMetadata(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
Group group = userService.getGroupById(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);
|
||||
|
||||
return "changeMetadata";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@PostMapping("/details/changeMetadata")
|
||||
@CacheEvict(value = "groups", allEntries = true)
|
||||
public String postChangeMetadata(KeycloakAuthenticationToken token,
|
||||
@RequestParam("title") String title,
|
||||
@RequestParam("description") String description,
|
||||
@RequestParam("groupId") String groupId) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
validationService.throwIfNoAdmin(group, user);
|
||||
validationService.checkFields(title, description);
|
||||
|
||||
controllerService.changeMetaData(account, group, title, description);
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@GetMapping("/details/members/{id}")
|
||||
public String editMembers(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
User user = new User(account);
|
||||
|
||||
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")
|
||||
@CacheEvict(value = "groups", allEntries = true)
|
||||
public String changeRole(KeycloakAuthenticationToken token,
|
||||
@RequestParam("group_id") String groupId,
|
||||
@RequestParam("user_id") String userId) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
User principle = new User(account);
|
||||
User user = new User(userId, "", "", "");
|
||||
|
||||
validationService.throwIfNoAdmin(group, principle);
|
||||
|
||||
//TODO: checkIfAdmin checkt nicht, dass die rolle geändert wurde. oder die rolle wird nicht geändert
|
||||
|
||||
controllerService.changeRole(account, user, group);
|
||||
|
||||
if (!validationService.checkIfAdmin(group, principle)) {
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
|
||||
return "redirect:/gruppen2/details/members/" + groupId;
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@PostMapping("/details/members/changeMaximum")
|
||||
@CacheEvict(value = "groups", allEntries = true)
|
||||
public String changeMaxSize(KeycloakAuthenticationToken token,
|
||||
@RequestParam("maximum") Long maximum,
|
||||
@RequestParam("group_id") String groupId) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
validationService.throwIfNewMaximumIsValid(maximum, group);
|
||||
|
||||
controllerService.updateMaxUser(account, UUID.fromString(groupId), maximum);
|
||||
|
||||
return "redirect:/gruppen2/details/members/" + groupId;
|
||||
}
|
||||
|
||||
@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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User principle = new User(account);
|
||||
User user = new User(userId, "", "", "");
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
validationService.throwIfNoAdmin(group, principle);
|
||||
|
||||
controllerService.deleteUser(account, user, group);
|
||||
|
||||
if (!validationService.checkIfUserInGroup(group, principle)) {
|
||||
return "redirect:/gruppen2";
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
validationService.throwIfUserAlreadyInGroup(group, user);
|
||||
validationService.throwIfGroupFull(group);
|
||||
|
||||
controllerService.addUser(account, UUID.fromString(groupId));
|
||||
|
||||
model.addAttribute("account", account);
|
||||
|
||||
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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
controllerService.deleteUser(account, 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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
validationService.throwIfNoAdmin(group, user);
|
||||
|
||||
controllerService.deleteGroupEvent(user.getId(), UUID.fromString(groupId));
|
||||
|
||||
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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
controllerService.addUsersFromCsv(account, file, groupId);
|
||||
|
||||
return "redirect:/gruppen2/details/members/" + groupId;
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
package mops.gruppen2.controller;
|
||||
|
||||
import mops.gruppen2.domain.Account;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.User;
|
||||
import mops.gruppen2.domain.Visibility;
|
||||
import mops.gruppen2.service.ControllerService;
|
||||
import mops.gruppen2.service.InviteService;
|
||||
import mops.gruppen2.service.KeyCloakService;
|
||||
import mops.gruppen2.service.UserService;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Controller
|
||||
@SessionScope
|
||||
@RequestMapping("/gruppen2")
|
||||
public class SearchAndInviteController {
|
||||
|
||||
private final ValidationService validationService;
|
||||
private final InviteService inviteService;
|
||||
private final UserService userService;
|
||||
private final ControllerService controllerService;
|
||||
|
||||
public SearchAndInviteController(ValidationService validationService, InviteService inviteService, UserService userService, ControllerService controllerService) {
|
||||
this.validationService = validationService;
|
||||
this.inviteService = inviteService;
|
||||
this.userService = userService;
|
||||
this.controllerService = controllerService;
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@GetMapping("/findGroup")
|
||||
public String findGroup(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@RequestParam(value = "suchbegriff", required = false) String search) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
List<Group> groups = new ArrayList<>();
|
||||
groups = validationService.checkSearch(search, groups, account);
|
||||
|
||||
model.addAttribute("account", account);
|
||||
model.addAttribute("gruppen", groups);
|
||||
model.addAttribute("inviteService", inviteService);
|
||||
|
||||
return "search";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@GetMapping("/detailsSearch")
|
||||
public String showGroupDetailsNoMember(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@RequestParam("id") String groupId) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
UUID parentId = group.getParent();
|
||||
Group parent = controllerService.getParent(parentId);
|
||||
User user = new User(account);
|
||||
|
||||
model.addAttribute("account", account);
|
||||
if (validationService.checkIfUserInGroup(group, user)) {
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
|
||||
model.addAttribute("group", group);
|
||||
model.addAttribute("parentId", parentId);
|
||||
model.addAttribute("parent", parent);
|
||||
|
||||
return "detailsNoMember";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin", "ROLE_actuator"})
|
||||
@GetMapping("/acceptinvite/{link}")
|
||||
public String acceptInvite(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@PathVariable("link") String link) {
|
||||
|
||||
Group group = userService.getGroupById(inviteService.getGroupIdFromLink(link));
|
||||
|
||||
validationService.throwIfGroupNotExisting(group.getTitle());
|
||||
|
||||
model.addAttribute("account", KeyCloakService.createAccountFromPrincipal(token));
|
||||
model.addAttribute("group", group);
|
||||
|
||||
if (group.getVisibility() == Visibility.PUBLIC) {
|
||||
return "redirect:/gruppen2/details/" + group.getId();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
Group group = userService.getGroupById(UUID.fromString(groupId));
|
||||
|
||||
validationService.throwIfUserAlreadyInGroup(group, user);
|
||||
validationService.throwIfGroupFull(group);
|
||||
|
||||
controllerService.addUser(account, UUID.fromString(groupId));
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
package mops.gruppen2.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class Account {
|
||||
|
||||
String name; //user_id
|
||||
@ -13,4 +17,14 @@ public class Account {
|
||||
String givenname;
|
||||
String familyname;
|
||||
Set<String> roles;
|
||||
|
||||
public Account(KeycloakAuthenticationToken token) {
|
||||
KeycloakPrincipal principal = (KeycloakPrincipal) token.getPrincipal();
|
||||
name = principal.getName();
|
||||
email = principal.getKeycloakSecurityContext().getIdToken().getEmail();
|
||||
image = null;
|
||||
givenname = principal.getKeycloakSecurityContext().getIdToken().getGivenName();
|
||||
familyname = principal.getKeycloakSecurityContext().getIdToken().getFamilyName();
|
||||
roles = token.getAccount().getRoles();
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
package mops.gruppen2.domain;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repräsentiert den aggregierten Zustand einer Gruppe.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class Group {
|
||||
|
||||
//TODO: List to Hashmap
|
||||
private final List<User> members;
|
||||
private final Map<String, Role> roles;
|
||||
private UUID id;
|
||||
private String title;
|
||||
private String description;
|
||||
private Long userMaximum;
|
||||
private GroupType type;
|
||||
private Visibility visibility;
|
||||
private UUID parent;
|
||||
|
||||
public Group() {
|
||||
members = new ArrayList<>();
|
||||
roles = new HashMap<>();
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package mops.gruppen2.domain;
|
||||
|
||||
public enum GroupType {
|
||||
SIMPLE,
|
||||
LECTURE
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package mops.gruppen2.domain;
|
||||
|
||||
public enum Role {
|
||||
ADMIN,
|
||||
MEMBER
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
package mops.gruppen2.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(exclude = {"givenname", "familyname", "email"})
|
||||
public class User {
|
||||
|
||||
private String id;
|
||||
private String givenname;
|
||||
private String familyname;
|
||||
private String email;
|
||||
|
||||
public User(Account account) {
|
||||
id = account.getName();
|
||||
givenname = account.getGivenname();
|
||||
familyname = account.getFamilyname();
|
||||
email = account.getEmail();
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package mops.gruppen2.domain;
|
||||
|
||||
public enum Visibility {
|
||||
PUBLIC,
|
||||
PRIVATE
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package mops.gruppen2.domain.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;
|
||||
}
|
57
src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java
Normal file
57
src/main/java/mops/gruppen2/domain/event/AddMemberEvent.java
Normal file
@ -0,0 +1,57 @@
|
||||
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.UserExistsException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Fügt einen einzelnen Nutzer einer Gruppe hinzu.
|
||||
*/
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class AddMemberEvent extends Event {
|
||||
|
||||
@JsonProperty("user")
|
||||
User user;
|
||||
|
||||
public AddMemberEvent(UUID groupId, String exec, String target, User user) throws IdMismatchException {
|
||||
super(groupId, exec, target);
|
||||
this.user = user;
|
||||
|
||||
if (!target.equals(user.getId())) {
|
||||
throw new IdMismatchException("Der User passt nicht zur angegebenen userid.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {
|
||||
cache.usersPut(target, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws UserExistsException, GroupFullException {
|
||||
group.addMember(target, user);
|
||||
|
||||
log.trace("\t\t\t\t\tNeue Members: {}", group.getMembers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format() {
|
||||
return "Benutzer hinzugefügt: " + target + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.ADDMEMBER.toString();
|
||||
}
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.Role;
|
||||
import mops.gruppen2.domain.User;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.exception.GroupFullException;
|
||||
import mops.gruppen2.domain.exception.UserAlreadyExistsException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Fügt einen einzelnen Nutzer einer Gruppe hinzu.
|
||||
*/
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
public class AddUserEvent extends Event {
|
||||
|
||||
private String givenname;
|
||||
private String familyname;
|
||||
private String email;
|
||||
|
||||
public AddUserEvent(UUID groupId, String userId, String givenname, String familyname, String email) {
|
||||
super(groupId, userId);
|
||||
this.givenname = givenname;
|
||||
this.familyname = familyname;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws EventException {
|
||||
User user = new User(userId, givenname, familyname, email);
|
||||
|
||||
if (group.getMembers().contains(user)) {
|
||||
throw new UserAlreadyExistsException(getClass().toString());
|
||||
}
|
||||
|
||||
if (group.getMembers().size() >= group.getUserMaximum()) {
|
||||
throw new GroupFullException(getClass().toString());
|
||||
}
|
||||
|
||||
group.getMembers().add(user);
|
||||
group.getRoles().put(userId, Role.MEMBER);
|
||||
}
|
||||
}
|
@ -1,36 +1,56 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.GroupType;
|
||||
import mops.gruppen2.domain.Visibility;
|
||||
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.model.group.Group;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor// Value generiert den allArgsConstrucot nur, wenn keiner explizit angegeben ist
|
||||
public class CreateGroupEvent extends Event {
|
||||
|
||||
private Visibility groupVisibility;
|
||||
private UUID groupParent;
|
||||
private GroupType groupType;
|
||||
private Long groupUserMaximum;
|
||||
@JsonProperty("date")
|
||||
LocalDateTime date;
|
||||
|
||||
public CreateGroupEvent(UUID groupId, String userId, UUID parent, GroupType type, Visibility visibility, Long userMaximum) {
|
||||
super(groupId, userId);
|
||||
groupParent = parent;
|
||||
groupType = type;
|
||||
groupVisibility = visibility;
|
||||
groupUserMaximum = userMaximum;
|
||||
public CreateGroupEvent(UUID groupId, String exec, LocalDateTime date) {
|
||||
super(groupId, exec, null);
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) {
|
||||
group.setId(groupId);
|
||||
group.setParent(groupParent);
|
||||
group.setType(groupType);
|
||||
group.setVisibility(groupVisibility);
|
||||
group.setUserMaximum(groupUserMaximum);
|
||||
protected void updateCache(GroupCache cache, Group group) {
|
||||
cache.groupsPut(groupid, group);
|
||||
cache.linksPut(group.getLink(), group);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws BadArgumentException {
|
||||
group.setId(groupid);
|
||||
group.setCreator(exec);
|
||||
group.setCreationDate(date);
|
||||
|
||||
log.trace("\t\t\t\t\tNeue Gruppe: {}", group.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format() {
|
||||
return "Gruppe erstellt.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.CREATEGROUP.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + version + "," + groupid + "," + date + ")";
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
public class DeleteGroupEvent extends Event {
|
||||
|
||||
public DeleteGroupEvent(UUID groupId, String userId) {
|
||||
super(groupId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) {
|
||||
group.getRoles().clear();
|
||||
group.getMembers().clear();
|
||||
group.setTitle(null);
|
||||
group.setDescription(null);
|
||||
group.setVisibility(null);
|
||||
group.setType(null);
|
||||
group.setParent(null);
|
||||
group.setUserMaximum(0L);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.User;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.exception.UserNotFoundException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entfernt ein einzelnes Mitglied einer Gruppe.
|
||||
*/
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
public class DeleteUserEvent extends Event {
|
||||
|
||||
public DeleteUserEvent(UUID groupId, String userId) {
|
||||
super(groupId, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws EventException {
|
||||
for (User user : group.getMembers()) {
|
||||
if (user.getId().equals(this.userId)) {
|
||||
group.getMembers().remove(user);
|
||||
group.getRoles().remove(user.getId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new UserNotFoundException(this.getClass().toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.NoAccessException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class DestroyGroupEvent extends Event {
|
||||
|
||||
public DestroyGroupEvent(UUID groupId, String exec) {
|
||||
super(groupId, exec, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {
|
||||
cache.groupsRemove(groupid, group);
|
||||
}
|
||||
|
||||
@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 format() {
|
||||
return "Gruppe gelöscht.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.DESTROYGROUP.toString();
|
||||
}
|
||||
}
|
@ -1,51 +1,119 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.BadArgumentException;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.exception.GroupIdMismatchException;
|
||||
import mops.gruppen2.domain.exception.IdMismatchException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.fasterxml.jackson.annotation.JsonSubTypes.Type;
|
||||
import static com.fasterxml.jackson.annotation.JsonTypeInfo.As;
|
||||
import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
|
||||
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
property = "type"
|
||||
)
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = AddUserEvent.class, name = "AddUserEvent"),
|
||||
@JsonSubTypes.Type(value = CreateGroupEvent.class, name = "CreateGroupEvent"),
|
||||
@JsonSubTypes.Type(value = DeleteUserEvent.class, name = "DeleteUserEvent"),
|
||||
@JsonSubTypes.Type(value = UpdateGroupDescriptionEvent.class, name = "UpdateGroupDescriptionEvent"),
|
||||
@JsonSubTypes.Type(value = UpdateGroupTitleEvent.class, name = "UpdateGroupTitleEvent"),
|
||||
@JsonSubTypes.Type(value = UpdateRoleEvent.class, name = "UpdateRoleEvent"),
|
||||
@JsonSubTypes.Type(value = DeleteGroupEvent.class, name = "DeleteGroupEvent"),
|
||||
@JsonSubTypes.Type(value = UpdateUserMaxEvent.class, name = "UpdateUserMaxEvent")
|
||||
})
|
||||
@Log4j2
|
||||
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "class")
|
||||
@JsonSubTypes({@Type(value = AddMemberEvent.class, name = "ADDMEMBER"),
|
||||
@Type(value = CreateGroupEvent.class, name = "CREATEGROUP"),
|
||||
@Type(value = DestroyGroupEvent.class, name = "DESTROYGROUP"),
|
||||
@Type(value = KickMemberEvent.class, name = "KICKMEMBER"),
|
||||
@Type(value = SetDescriptionEvent.class, name = "SETDESCRIPTION"),
|
||||
@Type(value = SetInviteLinkEvent.class, name = "SETLINK"),
|
||||
@Type(value = SetLimitEvent.class, name = "SETLIMIT"),
|
||||
@Type(value = SetParentEvent.class, name = "SETPARENT"),
|
||||
@Type(value = SetTitleEvent.class, name = "SETTITLE"),
|
||||
@Type(value = SetTypeEvent.class, name = "SETTYPE"),
|
||||
@Type(value = UpdateRoleEvent.class, name = "UPDATEROLE")})
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor // Lombok needs a default constructor in the base class
|
||||
public abstract class Event {
|
||||
|
||||
protected UUID groupId;
|
||||
protected String userId;
|
||||
@JsonProperty("groupid")
|
||||
protected UUID groupid;
|
||||
|
||||
public void apply(Group group) throws EventException {
|
||||
checkGroupIdMatch(group.getId());
|
||||
applyEvent(group);
|
||||
@JsonProperty("version")
|
||||
protected long version; // Group-Version
|
||||
|
||||
@JsonProperty("exec")
|
||||
protected String exec;
|
||||
|
||||
@JsonProperty("target")
|
||||
protected String target;
|
||||
|
||||
@JsonProperty("date")
|
||||
protected LocalDateTime date;
|
||||
|
||||
//TODO: Eigentlich sollte die Gruppe aus dem Cache genommen werden, nicht übergeben
|
||||
public Event(UUID groupid, String exec, String target) {
|
||||
this.groupid = groupid;
|
||||
this.exec = exec;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
private void checkGroupIdMatch(UUID groupId) {
|
||||
if (groupId == null || this.groupId.equals(groupId)) {
|
||||
public void init(long version) {
|
||||
if (this.version != 0) {
|
||||
throw new BadArgumentException("Event wurde schon initialisiert. (" + type() + ")");
|
||||
}
|
||||
date = LocalDateTime.now();
|
||||
|
||||
log.trace("Event wurde initialisiert. (" + type() + "," + version + ")");
|
||||
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public void apply(Group group, GroupCache cache) throws EventException {
|
||||
log.trace("Event wird angewendet:\t{}", this);
|
||||
|
||||
if (version == 0) {
|
||||
throw new BadArgumentException("Event wurde nicht initialisiert.");
|
||||
}
|
||||
|
||||
checkGroupIdMatch(group.getId());
|
||||
group.updateVersion(version);
|
||||
applyEvent(group);
|
||||
updateCache(cache, group); // Update erst nachdem apply keine exception geworfen hat
|
||||
}
|
||||
|
||||
public void apply(Group group) throws EventException {
|
||||
log.trace("Event wird angewendet:\t{}", this);
|
||||
|
||||
if (version == 0) {
|
||||
throw new BadArgumentException("Event wurde nicht initialisiert.");
|
||||
}
|
||||
|
||||
checkGroupIdMatch(group.getId());
|
||||
group.updateVersion(version);
|
||||
applyEvent(group);
|
||||
|
||||
}
|
||||
|
||||
private void checkGroupIdMatch(UUID groupid) throws IdMismatchException {
|
||||
// CreateGroupEvents müssen die Id erst initialisieren
|
||||
if (this instanceof CreateGroupEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new GroupIdMismatchException(getClass().toString());
|
||||
if (!this.groupid.equals(groupid)) {
|
||||
throw new IdMismatchException("Das Event gehört zu einer anderen Gruppe");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void updateCache(GroupCache cache, Group group);
|
||||
|
||||
protected abstract void applyEvent(Group group) throws EventException;
|
||||
|
||||
@JsonIgnore
|
||||
public abstract String format();
|
||||
|
||||
@JsonIgnore
|
||||
public abstract String type();
|
||||
}
|
||||
|
15
src/main/java/mops/gruppen2/domain/event/EventType.java
Normal file
15
src/main/java/mops/gruppen2/domain/event/EventType.java
Normal file
@ -0,0 +1,15 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
public enum EventType {
|
||||
ADDMEMBER,
|
||||
CREATEGROUP,
|
||||
DESTROYGROUP,
|
||||
KICKMEMBER,
|
||||
SETDESCRIPTION,
|
||||
SETLINK,
|
||||
SETLIMIT,
|
||||
SETPARENT,
|
||||
SETTITLE,
|
||||
SETTYPE,
|
||||
UPDATEROLE
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.LastAdminException;
|
||||
import mops.gruppen2.domain.exception.UserNotFoundException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entfernt ein einzelnes Mitglied einer Gruppe.
|
||||
*/
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class KickMemberEvent extends Event {
|
||||
|
||||
public KickMemberEvent(UUID groupId, String exec, String target) {
|
||||
super(groupId, exec, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {
|
||||
cache.usersRemove(target, group);
|
||||
}
|
||||
|
||||
@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 format() {
|
||||
return "Mitglied entfernt: " + target + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.KICKMEMBER.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
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 mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Ändert nur die Gruppenbeschreibung.
|
||||
*/
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class SetDescriptionEvent extends Event {
|
||||
|
||||
@JsonProperty("desc")
|
||||
Description description;
|
||||
|
||||
public SetDescriptionEvent(UUID groupId, String exec, @Valid Description description) {
|
||||
super(groupId, exec, null);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {}
|
||||
|
||||
@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 format() {
|
||||
return "Beschreibung gesetzt: " + description + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.SETDESCRIPTION.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
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 mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class SetInviteLinkEvent extends Event {
|
||||
|
||||
@JsonProperty("link")
|
||||
Link link;
|
||||
|
||||
public SetInviteLinkEvent(UUID groupId, String exec, @Valid Link link) {
|
||||
super(groupId, exec, null);
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {
|
||||
cache.linksRemove(group.getLink());
|
||||
cache.linksPut(link.getValue(), group);
|
||||
}
|
||||
|
||||
@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 format() {
|
||||
return "Einladungslink gesetzt: " + link + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.SETLINK.toString();
|
||||
}
|
||||
}
|
48
src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java
Normal file
48
src/main/java/mops/gruppen2/domain/event/SetLimitEvent.java
Normal file
@ -0,0 +1,48 @@
|
||||
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 mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class SetLimitEvent extends Event {
|
||||
|
||||
@JsonProperty("limit")
|
||||
Limit limit;
|
||||
|
||||
public SetLimitEvent(UUID groupId, String exec, @Valid Limit limit) {
|
||||
super(groupId, exec, null);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {}
|
||||
|
||||
@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 format() {
|
||||
return "Benutzerlimit gesetzt: " + limit + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.SETLIMIT.toString();
|
||||
}
|
||||
}
|
48
src/main/java/mops/gruppen2/domain/event/SetParentEvent.java
Normal file
48
src/main/java/mops/gruppen2/domain/event/SetParentEvent.java
Normal file
@ -0,0 +1,48 @@
|
||||
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.Parent;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class SetParentEvent extends Event {
|
||||
|
||||
@JsonProperty("parent")
|
||||
Parent parent;
|
||||
|
||||
public SetParentEvent(UUID groupId, String exec, @Valid Parent parent) {
|
||||
super(groupId, exec, null);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws NoAccessException, BadArgumentException {
|
||||
group.setParent(exec, parent);
|
||||
|
||||
log.trace("\t\t\t\t\tNeues Parent: {}", group.getParent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format() {
|
||||
return "Veranstaltungszugehörigkeit gesetzt: " + parent.getValue() + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.SETPARENT.toString();
|
||||
}
|
||||
}
|
51
src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java
Normal file
51
src/main/java/mops/gruppen2/domain/event/SetTitleEvent.java
Normal file
@ -0,0 +1,51 @@
|
||||
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 mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Ändert nur den Gruppentitel.
|
||||
*/
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class SetTitleEvent extends Event {
|
||||
|
||||
@JsonProperty("title")
|
||||
Title title;
|
||||
|
||||
public SetTitleEvent(UUID groupId, String exec, @Valid Title title) {
|
||||
super(groupId, exec, null);
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {}
|
||||
|
||||
@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 format() {
|
||||
return "Titel gesetzt: " + title + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.SETTITLE.toString();
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java
Normal file
57
src/main/java/mops/gruppen2/domain/event/SetTypeEvent.java
Normal file
@ -0,0 +1,57 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.experimental.NonFinal;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.UUID;
|
||||
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class SetTypeEvent extends Event {
|
||||
|
||||
@JsonProperty("type")
|
||||
Type type;
|
||||
|
||||
//TODO: blöder hack, das soll eigentlich anders gehen
|
||||
// Problem ist, dass die Gruppe vor dem Cache verändert wird, also kann der cache den alten Typ
|
||||
// nicht mehr aus der Gruppe holen
|
||||
@NonFinal
|
||||
Type oldType;
|
||||
|
||||
public SetTypeEvent(UUID groupId, String exec, @Valid Type type) {
|
||||
super(groupId, exec, null);
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCache(GroupCache cache, Group group) {
|
||||
cache.typesRemove(oldType, group);
|
||||
cache.typesPut(type, group);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws EventException {
|
||||
oldType = group.getType();
|
||||
group.setType(exec, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format() {
|
||||
return "Gruppentype gesetzt: " + type + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.SETTYPE.toString();
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.exception.BadParameterException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Ändert nur die Gruppenbeschreibung.
|
||||
*/
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
public class UpdateGroupDescriptionEvent extends Event {
|
||||
|
||||
private String newGroupDescription;
|
||||
|
||||
public UpdateGroupDescriptionEvent(UUID groupId, String userId, String newGroupDescription) {
|
||||
super(groupId, userId);
|
||||
this.newGroupDescription = newGroupDescription.trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) {
|
||||
if (newGroupDescription.isEmpty()) {
|
||||
throw new BadParameterException("Die Beschreibung ist leer.");
|
||||
}
|
||||
|
||||
group.setDescription(newGroupDescription);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.exception.BadParameterException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Ändert nur den Gruppentitel.
|
||||
*/
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
public class UpdateGroupTitleEvent extends Event {
|
||||
|
||||
private String newGroupTitle;
|
||||
|
||||
public UpdateGroupTitleEvent(UUID groupId, String userId, String newGroupTitle) {
|
||||
super(groupId, userId);
|
||||
this.newGroupTitle = newGroupTitle.trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) {
|
||||
if (newGroupTitle.isEmpty()) {
|
||||
throw new BadParameterException("Der Titel ist leer.");
|
||||
}
|
||||
|
||||
group.setTitle(newGroupTitle);
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +1,51 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.Role;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Value;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.LastAdminException;
|
||||
import mops.gruppen2.domain.exception.UserNotFoundException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Role;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Aktualisiert die Gruppenrolle eines Teilnehmers.
|
||||
*/
|
||||
@Getter
|
||||
@NoArgsConstructor // For Jackson
|
||||
@Log4j2
|
||||
@Value
|
||||
@AllArgsConstructor
|
||||
public class UpdateRoleEvent extends Event {
|
||||
|
||||
private Role newRole;
|
||||
@JsonProperty("role")
|
||||
Role role;
|
||||
|
||||
public UpdateRoleEvent(UUID groupId, String userId, Role newRole) {
|
||||
super(groupId, userId);
|
||||
this.newRole = newRole;
|
||||
public UpdateRoleEvent(UUID groupId, String exec, String target, Role role) {
|
||||
super(groupId, exec, target);
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws UserNotFoundException {
|
||||
if (group.getRoles().containsKey(userId)) {
|
||||
group.getRoles().put(userId, newRole);
|
||||
return;
|
||||
}
|
||||
protected void updateCache(GroupCache cache, Group group) {}
|
||||
|
||||
throw new UserNotFoundException(getClass().toString());
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws UserNotFoundException, LastAdminException {
|
||||
group.memberPutRole(target, role);
|
||||
|
||||
log.trace("\t\t\t\t\tNeue Admin: {}", group.getAdmins());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format() {
|
||||
return "Mitgliedsrolle gesetzt: " + target + ": " + role + ".";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return EventType.UPDATEROLE.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
package mops.gruppen2.domain.event;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.Group;
|
||||
import mops.gruppen2.domain.exception.BadParameterException;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
public class UpdateUserMaxEvent extends Event {
|
||||
|
||||
private Long userMaximum;
|
||||
|
||||
public UpdateUserMaxEvent(UUID groupId, String userId, Long userMaximum) {
|
||||
super(groupId, userId);
|
||||
this.userMaximum = userMaximum;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyEvent(Group group) throws EventException {
|
||||
if (userMaximum <= 0 || userMaximum < group.getMembers().size()) {
|
||||
throw new BadParameterException("Usermaximum zu klein.");
|
||||
}
|
||||
|
||||
group.setUserMaximum(userMaximum);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package mops.gruppen2.domain.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class BadParameterException extends EventException {
|
||||
|
||||
public BadParameterException(String info) {
|
||||
super(HttpStatus.BAD_REQUEST, "Fehlerhafter Parameter angegeben!", info);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package mops.gruppen2.domain.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class BadPayloadException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = -3978242017847155629L;
|
||||
|
||||
public BadPayloadException(String info) {
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "Payload konnte nicht übersetzt werden.", info);
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,10 @@ import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
public class EventException extends ResponseStatusException {
|
||||
|
||||
private static final long serialVersionUID = 6784052016028094340L;
|
||||
|
||||
public EventException(HttpStatus status, String msg, String info) {
|
||||
super(status, msg + " (" + info + ")");
|
||||
super(status, info.isBlank() ? "" : msg + " (" + info + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class GroupFullException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = -4011141160467668713L;
|
||||
|
||||
public GroupFullException(String info) {
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "Die Gruppe hat die maximale Midgliederanzahl bereits erreicht!", info);
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "Gruppe hat maximale Teilnehmeranzahl bereits erreicht.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
package mops.gruppen2.domain.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class GroupIdMismatchException extends EventException {
|
||||
|
||||
public GroupIdMismatchException(String info) {
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "Falsche Gruppe für Event.", info);
|
||||
}
|
||||
}
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class GroupNotFoundException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = -4738218416842951106L;
|
||||
|
||||
public GroupNotFoundException(String info) {
|
||||
super(HttpStatus.NOT_FOUND, "Gruppe wurde nicht gefunden.", info);
|
||||
super(HttpStatus.NOT_FOUND, "Gruppe existiert nicht oder wurde gelöscht.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class InvalidInviteException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = 2643001101459427944L;
|
||||
|
||||
public InvalidInviteException(String info) {
|
||||
super(HttpStatus.NOT_FOUND, "Der Einladungslink ist ungültig.", info);
|
||||
super(HttpStatus.NOT_FOUND, "Einladungslink ist ungültig oder Gruppe wurde gelöscht.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class NoAccessException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = 1696988497122834654L;
|
||||
|
||||
public NoAccessException(String info) {
|
||||
super(HttpStatus.FORBIDDEN, "Hier hast du leider keinen Zugriff!", info);
|
||||
super(HttpStatus.FORBIDDEN, "Kein Zugriff.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
package mops.gruppen2.domain.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class NoAdminAfterActionException extends EventException {
|
||||
|
||||
public NoAdminAfterActionException(String info) {
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "Nach dieser Aktion hätte die Gruppe keinen Admin mehr", info);
|
||||
}
|
||||
}
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class NoInviteExistException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = -8092076461455840693L;
|
||||
|
||||
public NoInviteExistException(String info) {
|
||||
super(HttpStatus.NOT_FOUND, "Für diese Gruppe existiert kein Link.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class PageNotFoundException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = 2374509005158710104L;
|
||||
|
||||
public PageNotFoundException(String info) {
|
||||
super(HttpStatus.NOT_FOUND, "Die Seite wurde nicht gefunden!", info);
|
||||
super(HttpStatus.NOT_FOUND, "Seite wurde nicht gefunden.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
package mops.gruppen2.domain.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class UserAlreadyExistsException extends EventException {
|
||||
|
||||
public UserAlreadyExistsException(String info) {
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "Der User existiert bereits.", info);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package mops.gruppen2.domain.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class UserExistsException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = -8150634358760194625L;
|
||||
|
||||
public UserExistsException(String info) {
|
||||
super(HttpStatus.INTERNAL_SERVER_ERROR, "User existiert bereits.", info);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class UserNotFoundException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = 8347442921199785291L;
|
||||
|
||||
public UserNotFoundException(String info) {
|
||||
super(HttpStatus.NOT_FOUND, "Der User wurde nicht gefunden.", info);
|
||||
super(HttpStatus.NOT_FOUND, "User existiert nicht.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,10 @@ import org.springframework.http.HttpStatus;
|
||||
|
||||
public class WrongFileException extends EventException {
|
||||
|
||||
private static final long serialVersionUID = -166192514348555116L;
|
||||
|
||||
public WrongFileException(String info) {
|
||||
super(HttpStatus.BAD_REQUEST, "Die entsprechende Datei ist keine valide CSV-Datei!", info);
|
||||
super(HttpStatus.BAD_REQUEST, "Datei ist keine valide CSV-Datei.", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
357
src/main/java/mops/gruppen2/domain/model/group/Group.java
Normal file
357
src/main/java/mops/gruppen2/domain/model/group/Group.java
Normal file
@ -0,0 +1,357 @@
|
||||
package mops.gruppen2.domain.model.group;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
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.UserExistsException;
|
||||
import mops.gruppen2.domain.exception.UserNotFoundException;
|
||||
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 mops.gruppen2.domain.service.helper.CommonHelper;
|
||||
import mops.gruppen2.domain.service.helper.SortHelper;
|
||||
import mops.gruppen2.domain.service.helper.ValidationHelper;
|
||||
|
||||
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
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class Group {
|
||||
|
||||
// Metainformationen
|
||||
@EqualsAndHashCode.Include
|
||||
private UUID groupid;
|
||||
|
||||
@Getter
|
||||
private Type type = Type.PRIVATE;
|
||||
|
||||
private Parent parent = Parent.EMPTY();
|
||||
|
||||
private Limit limit = Limit.DEFAULT(); // Add initial user
|
||||
|
||||
private Link link = Link.RANDOM();
|
||||
|
||||
private GroupMeta meta = GroupMeta.EMPTY();
|
||||
|
||||
//TODO: UI set + use for options
|
||||
private final GroupOptions options = GroupOptions.DEFAULT();
|
||||
|
||||
// Inhalt
|
||||
private Title title = Title.EMPTY();
|
||||
|
||||
private Description description = Description.EMPTY();
|
||||
|
||||
//TODO: Asciidoc description
|
||||
private Body body;
|
||||
|
||||
// Integrationen
|
||||
|
||||
// Teilnehmer
|
||||
private final 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 UserExistsException, 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 boolean memberHasRole(String target, Role role) {
|
||||
ValidationHelper.throwIfNoMember(this, target);
|
||||
|
||||
return memberships.get(target).getRole() == role;
|
||||
}
|
||||
|
||||
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.getValue();
|
||||
}
|
||||
|
||||
public long getLimit() {
|
||||
return limit.getValue();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.toString();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description.getValue();
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link.getValue();
|
||||
}
|
||||
|
||||
public String creator() {
|
||||
return meta.getCreator();
|
||||
}
|
||||
|
||||
public long version() {
|
||||
return meta.getVersion();
|
||||
}
|
||||
|
||||
public LocalDateTime creationDate() {
|
||||
return meta.getCreationDate();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return memberships.size();
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
return size() >= limit.getValue();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return groupid != null && !CommonHelper.uuidIsEmpty(groupid);
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return type == Type.PUBLIC;
|
||||
}
|
||||
|
||||
public boolean isPrivate() {
|
||||
return type == Type.PRIVATE;
|
||||
}
|
||||
|
||||
public boolean isLecture() {
|
||||
return type == Type.LECTURE;
|
||||
}
|
||||
|
||||
public boolean hasParent() {
|
||||
return !parent.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasMaterial() {
|
||||
return options.isHasMaterialIntegration();
|
||||
}
|
||||
|
||||
public boolean hasForums() {
|
||||
return options.isHasForumsIntegration();
|
||||
}
|
||||
|
||||
public boolean hasCalendar() {
|
||||
return options.isHasTermineIntegration();
|
||||
}
|
||||
|
||||
public boolean hasModules() {
|
||||
return options.isHasModulesIntegration();
|
||||
}
|
||||
|
||||
public boolean hasPortfolios() {
|
||||
return options.isHasPortfolioIntegration();
|
||||
}
|
||||
|
||||
|
||||
// ######################################## Setters ##########################################
|
||||
|
||||
|
||||
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, BadArgumentException {
|
||||
ValidationHelper.throwIfNoAdmin(this, exec);
|
||||
if (parent.getValue().equals(groupid)) {
|
||||
throw new BadArgumentException("Die Gruppe kann nicht zu sich selbst gehören!");
|
||||
}
|
||||
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setLink(String exec, @Valid Link link) throws NoAccessException {
|
||||
ValidationHelper.throwIfNoAdmin(this, exec);
|
||||
if (link.getValue().equals(groupid.toString())) {
|
||||
throw new BadArgumentException("Link kann nicht der GruppenID entsprechen.");
|
||||
}
|
||||
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public void updateVersion(long version) throws IdMismatchException {
|
||||
meta = meta.setVersion(version);
|
||||
}
|
||||
|
||||
public void setCreator(String target) throws BadArgumentException {
|
||||
meta = meta.setCreator(target);
|
||||
}
|
||||
|
||||
public void setCreationDate(LocalDateTime date) throws BadArgumentException {
|
||||
meta = meta.setCreationDate(date);
|
||||
}
|
||||
|
||||
|
||||
// ######################################### Util ############################################
|
||||
|
||||
|
||||
public void destroy(String userid) throws NoAccessException {
|
||||
if (!isEmpty()) {
|
||||
ValidationHelper.throwIfNoAdmin(this, userid);
|
||||
}
|
||||
|
||||
groupid = null;
|
||||
// Wenn man alles null setzt hat der cache mehr arbeit, weil dieser erst nach der löschung
|
||||
// geupdated wird und sich link und mitgliedschaften selber heraussuchen muss
|
||||
/*type = null;
|
||||
parent = null;
|
||||
limit = null;
|
||||
link = null;
|
||||
meta = null;
|
||||
options = null;
|
||||
title = null;
|
||||
description = null;
|
||||
body = null;
|
||||
memberships = null;*/
|
||||
}
|
||||
|
||||
public String format() {
|
||||
return title + " - " + description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "group("
|
||||
+ (groupid == null ? "groupid: null" : groupid.toString())
|
||||
+ ", "
|
||||
+ (parent == null ? "parent: null" : parent.toString())
|
||||
+ ", "
|
||||
+ (meta == null ? "meta: null" : meta.toString())
|
||||
+ ")";
|
||||
}
|
||||
|
||||
public static Group EMPTY() {
|
||||
return new Group();
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return meta.getVersion();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
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
|
||||
class GroupMeta {
|
||||
|
||||
long version;
|
||||
String creator;
|
||||
LocalDateTime creationDate;
|
||||
|
||||
GroupMeta setVersion(long version) throws IdMismatchException {
|
||||
if (this.version >= version) {
|
||||
throw new IdMismatchException("Die Gruppe ist bereits auf einem neueren Stand.");
|
||||
}
|
||||
if (this.version + 1 != version) {
|
||||
throw new IdMismatchException("Es fehlen vorherige Events.");
|
||||
}
|
||||
|
||||
return new GroupMeta(version, creator, creationDate);
|
||||
}
|
||||
|
||||
GroupMeta setCreator(String userid) throws BadArgumentException {
|
||||
if (creator != null) {
|
||||
throw new BadArgumentException("Gruppe hat schon einen Ersteller.");
|
||||
}
|
||||
|
||||
return new GroupMeta(version, userid, creationDate);
|
||||
}
|
||||
|
||||
GroupMeta setCreationDate(LocalDateTime date) throws BadArgumentException {
|
||||
if (creationDate != null) {
|
||||
throw new BadArgumentException("Gruppe hat schon ein Erstellungsdatum.");
|
||||
}
|
||||
|
||||
return new GroupMeta(version, creator, date);
|
||||
}
|
||||
|
||||
static GroupMeta EMPTY() {
|
||||
return new GroupMeta(0, null, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
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;
|
||||
boolean hasForumsIntegration;
|
||||
boolean hasModulesIntegration;
|
||||
|
||||
static GroupOptions DEFAULT() {
|
||||
return new GroupOptions(true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
10
src/main/java/mops/gruppen2/domain/model/group/Role.java
Normal file
10
src/main/java/mops/gruppen2/domain/model/group/Role.java
Normal file
@ -0,0 +1,10 @@
|
||||
package mops.gruppen2.domain.model.group;
|
||||
|
||||
public enum Role {
|
||||
ADMIN,
|
||||
REGULAR;
|
||||
|
||||
public Role toggle() {
|
||||
return this == ADMIN ? REGULAR : ADMIN;
|
||||
}
|
||||
}
|
7
src/main/java/mops/gruppen2/domain/model/group/Type.java
Normal file
7
src/main/java/mops/gruppen2/domain/model/group/Type.java
Normal file
@ -0,0 +1,7 @@
|
||||
package mops.gruppen2.domain.model.group;
|
||||
|
||||
public enum Type {
|
||||
PUBLIC,
|
||||
PRIVATE,
|
||||
LECTURE
|
||||
}
|
64
src/main/java/mops/gruppen2/domain/model/group/User.java
Normal file
64
src/main/java/mops/gruppen2/domain/model/group/User.java
Normal file
@ -0,0 +1,64 @@
|
||||
package mops.gruppen2.domain.model.group;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
|
||||
@Log4j2
|
||||
@Value
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@AllArgsConstructor
|
||||
public class User {
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
@Getter(AccessLevel.NONE)
|
||||
@JsonProperty("id")
|
||||
String userid;
|
||||
|
||||
@JsonProperty("givenname")
|
||||
String givenname;
|
||||
|
||||
@JsonProperty("familyname")
|
||||
String familyname;
|
||||
|
||||
@JsonProperty("email")
|
||||
String email;
|
||||
|
||||
public User(KeycloakAuthenticationToken token) {
|
||||
KeycloakPrincipal principal = (KeycloakPrincipal) token.getPrincipal();
|
||||
userid = principal.getName();
|
||||
givenname = principal.getKeycloakSecurityContext().getIdToken().getGivenName();
|
||||
familyname = principal.getKeycloakSecurityContext().getIdToken().getFamilyName();
|
||||
email = principal.getKeycloakSecurityContext().getIdToken().getEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* User identifizieren sich über die Id, mehr wird also manchmal nicht benötigt.
|
||||
*
|
||||
* @param userid Die User Id
|
||||
*/
|
||||
public User(String userid) {
|
||||
this.userid = userid;
|
||||
givenname = "";
|
||||
familyname = "";
|
||||
email = "";
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return userid;
|
||||
}
|
||||
|
||||
public String format() {
|
||||
return givenname + " " + familyname;
|
||||
}
|
||||
|
||||
public boolean isMember(Group group) {
|
||||
return group.getMembers().contains(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package mops.gruppen2.domain.model.group.wrapper;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
//TODO: do it
|
||||
@Value
|
||||
public class Body {
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mops.gruppen2.domain.model.group.wrapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Value;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Value
|
||||
public class Description {
|
||||
|
||||
@NotNull
|
||||
@Size(min = 4, max = 512)
|
||||
@JsonProperty("value")
|
||||
String value;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public static Description EMPTY() {
|
||||
return new Description("EMPTY");
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mops.gruppen2.domain.model.group.wrapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Value;
|
||||
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Value
|
||||
public class Limit {
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
@Max(999_999)
|
||||
@JsonProperty("value")
|
||||
long value;
|
||||
|
||||
public static Limit DEFAULT() {
|
||||
return new Limit(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mops.gruppen2.domain.model.group.wrapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Value;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.UUID;
|
||||
|
||||
@Value
|
||||
public class Link {
|
||||
|
||||
@NotNull
|
||||
@JsonProperty("value")
|
||||
UUID value;
|
||||
|
||||
public Link(String value) {
|
||||
this.value = UUID.fromString(value);
|
||||
}
|
||||
|
||||
public static Link RANDOM() {
|
||||
return new Link(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package mops.gruppen2.domain.model.group.wrapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.ToString;
|
||||
import lombok.Value;
|
||||
import mops.gruppen2.domain.service.helper.CommonHelper;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.util.UUID;
|
||||
|
||||
@Value
|
||||
@ToString
|
||||
public class Parent {
|
||||
|
||||
@NotNull
|
||||
@JsonProperty("id")
|
||||
UUID value;
|
||||
|
||||
@ConstructorProperties("id")
|
||||
public Parent(@NotBlank @Size(min = 36, max = 36) String parentid) {
|
||||
value = UUID.fromString(parentid);
|
||||
}
|
||||
|
||||
public static Parent EMPTY() {
|
||||
return new Parent("00000000-0000-0000-0000-000000000000");
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isEmpty() {
|
||||
return CommonHelper.uuidIsEmpty(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mops.gruppen2.domain.model.group.wrapper;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Value;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Value
|
||||
public class Title {
|
||||
|
||||
@NotNull
|
||||
@Size(min = 4, max = 128)
|
||||
@JsonProperty("value")
|
||||
String value;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public static Title EMPTY() {
|
||||
return new Title("EMPTY");
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package mops.gruppen2.domain.service;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.event.Event;
|
||||
import mops.gruppen2.domain.exception.BadPayloadException;
|
||||
import mops.gruppen2.domain.service.helper.CommonHelper;
|
||||
import mops.gruppen2.domain.service.helper.FileHelper;
|
||||
import mops.gruppen2.persistance.EventRepository;
|
||||
import mops.gruppen2.persistance.dto.EventDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Log4j2
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class EventStoreService {
|
||||
|
||||
private final EventRepository eventStore;
|
||||
|
||||
|
||||
//########################################### SAVE ###########################################
|
||||
|
||||
|
||||
/**
|
||||
* Erzeugt ein DTO aus einem Event und speicher es.
|
||||
*
|
||||
* @param event Event, welches gespeichert wird
|
||||
*/
|
||||
public void saveEvent(Event event) {
|
||||
eventStore.save(getDTOFromEvent(event));
|
||||
}
|
||||
|
||||
public void saveAll(Event... events) {
|
||||
for (Event event : events) {
|
||||
eventStore.save(getDTOFromEvent(event));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//########################################### DTOs ###########################################
|
||||
|
||||
|
||||
/**
|
||||
* Erzeugt aus einem Event Objekt ein EventDTO Objekt.
|
||||
*
|
||||
* @param event Event, welches in DTO übersetzt wird
|
||||
*
|
||||
* @return EventDTO (Neues DTO)
|
||||
*/
|
||||
private static EventDTO getDTOFromEvent(Event event) {
|
||||
try {
|
||||
String payload = FileHelper.serializeEventJson(event);
|
||||
return new EventDTO(null,
|
||||
event.getGroupid().toString(),
|
||||
event.getVersion(),
|
||||
event.getExec(),
|
||||
event.getTarget(),
|
||||
Timestamp.valueOf(event.getDate()),
|
||||
payload);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Event ({}) konnte nicht serialisiert werden!", event, e);
|
||||
throw new BadPayloadException(EventStoreService.class.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt aus einer Liste von eventDTOs eine Liste von Events.
|
||||
*
|
||||
* @param eventDTOS Liste von DTOs
|
||||
*
|
||||
* @return Liste von Events
|
||||
*/
|
||||
private static List<Event> getEventsFromDTOs(List<EventDTO> eventDTOS) {
|
||||
return eventDTOS.stream()
|
||||
.map(EventStoreService::getEventFromDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static Event getEventFromDTO(EventDTO dto) {
|
||||
try {
|
||||
return FileHelper.deserializeEventJson(dto.getEvent_payload());
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Payload {} konnte nicht deserialisiert werden!", dto.getEvent_payload(), e);
|
||||
throw new BadPayloadException(EventStoreService.class.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #################################### SIMPLE QUERIES #######################################
|
||||
|
||||
|
||||
public List<Event> findAllEvents() {
|
||||
return getEventsFromDTOs(eventStore.findAllEvents());
|
||||
}
|
||||
|
||||
public List<Event> findGroupEvents(UUID groupId) {
|
||||
return getEventsFromDTOs(eventStore.findGroupEvents(groupId.toString()));
|
||||
}
|
||||
|
||||
public List<Event> findGroupEvents(List<UUID> ids) {
|
||||
return ids.stream()
|
||||
.map(id -> eventStore.findGroupEvents(id.toString()))
|
||||
.map(EventStoreService::getEventsFromDTOs)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<String> findGroupPayloads(UUID groupId) {
|
||||
return eventStore.findGroupPayloads(groupId.toString());
|
||||
}
|
||||
|
||||
public List<EventDTO> findGroupDTOs(UUID groupid) {
|
||||
return eventStore.findGroupEvents(groupid.toString());
|
||||
}
|
||||
|
||||
public List<UUID> findChangedGroups(long eventid) {
|
||||
return CommonHelper.stringsToUUID(eventStore.findChangedGroupIds(eventid));
|
||||
}
|
||||
|
||||
public long findMaxEventId() {
|
||||
return eventStore.findMaxEventId();
|
||||
}
|
||||
}
|
280
src/main/java/mops/gruppen2/domain/service/GroupService.java
Normal file
280
src/main/java/mops/gruppen2/domain/service/GroupService.java
Normal file
@ -0,0 +1,280 @@
|
||||
package mops.gruppen2.domain.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.event.AddMemberEvent;
|
||||
import mops.gruppen2.domain.event.CreateGroupEvent;
|
||||
import mops.gruppen2.domain.event.DestroyGroupEvent;
|
||||
import mops.gruppen2.domain.event.Event;
|
||||
import mops.gruppen2.domain.event.KickMemberEvent;
|
||||
import mops.gruppen2.domain.event.SetDescriptionEvent;
|
||||
import mops.gruppen2.domain.event.SetInviteLinkEvent;
|
||||
import mops.gruppen2.domain.event.SetLimitEvent;
|
||||
import mops.gruppen2.domain.event.SetParentEvent;
|
||||
import mops.gruppen2.domain.event.SetTitleEvent;
|
||||
import mops.gruppen2.domain.event.SetTypeEvent;
|
||||
import mops.gruppen2.domain.event.UpdateRoleEvent;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Role;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Description;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Limit;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Link;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Parent;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Title;
|
||||
import mops.gruppen2.domain.service.helper.ValidationHelper;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Behandelt Aufgaben, welche sich auf eine Gruppe beziehen.
|
||||
* Es werden übergebene Gruppen bearbeitet und dementsprechend Events erzeugt und gespeichert.
|
||||
*/
|
||||
@Log4j2
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GroupService {
|
||||
|
||||
private final GroupCache groupCache;
|
||||
private final EventStoreService eventStoreService;
|
||||
|
||||
// ################################# GRUPPE ERSTELLEN ########################################
|
||||
|
||||
|
||||
public Group createGroup(String exec) {
|
||||
return createGroup(UUID.randomUUID(), exec, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public void initGroupMembers(Group group,
|
||||
String exec,
|
||||
String target,
|
||||
User user,
|
||||
Limit limit) {
|
||||
|
||||
addMember(group, exec, target, user);
|
||||
updateRole(group, exec, target, Role.ADMIN);
|
||||
setLimit(group, exec, limit);
|
||||
}
|
||||
|
||||
public void initGroupMeta(Group group,
|
||||
String exec,
|
||||
Type type,
|
||||
Parent parent) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// ################################### GRUPPEN ÄNDERN ########################################
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param exec Ausführender User
|
||||
*/
|
||||
public void addUsersToGroup(Group group, String exec, List<User> newUsers) {
|
||||
List<User> users = newUsers.stream().distinct().collect(Collectors.toUnmodifiableList());
|
||||
|
||||
setLimit(group, exec, getAdjustedUserLimit(users, group));
|
||||
|
||||
users.forEach(newUser -> addUserSilent(group, exec, newUser.getId(), newUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt ein passendes Teilnehmermaximum.
|
||||
* Reicht das alte Maximum, wird dieses zurückgegeben.
|
||||
* Ansonsten wird ein erhöhtes Maximum zurückgegeben.
|
||||
*
|
||||
* @param newUsers Neue Teilnehmer
|
||||
* @param group Bestehende Gruppe, welche verändert wird
|
||||
*
|
||||
* @return Das neue Teilnehmermaximum
|
||||
*/
|
||||
private static Limit getAdjustedUserLimit(List<User> newUsers, Group group) {
|
||||
return new Limit(Math.max((long) group.size() + newUsers.size(), group.getLimit()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wechselt die Rolle eines Teilnehmers von Admin zu Member oder andersherum.
|
||||
* Überprüft, ob der User Mitglied ist und ob er der letzte Admin ist.
|
||||
*
|
||||
* @param target Teilnehmer, welcher geändert wird
|
||||
* @param group Gruppe, in welcher sih der Teilnehmer befindet
|
||||
*
|
||||
* @throws EventException Falls der User nicht gefunden wird
|
||||
*/
|
||||
public void toggleMemberRole(Group group, String exec, String target) {
|
||||
ValidationHelper.throwIfNoMember(group, target);
|
||||
|
||||
updateRole(group, exec, target, group.getRole(target).toggle());
|
||||
}
|
||||
|
||||
|
||||
// ################################# SINGLE EVENTS ###########################################
|
||||
// Spezifische Events werden erzeugt, validiert, auf die Gruppe angewandt und gespeichert
|
||||
|
||||
|
||||
/**
|
||||
* Erzeugt eine Gruppe, speichert diese und gibt diese zurück.
|
||||
*/
|
||||
private Group createGroup(UUID groupid, String exec, LocalDateTime date) {
|
||||
Event event = new CreateGroupEvent(groupid,
|
||||
exec,
|
||||
date);
|
||||
Group group = Group.EMPTY();
|
||||
applyAndSave(group, event);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dasselbe wie addUser(), aber exceptions werden abgefangen und nicht geworfen.
|
||||
*/
|
||||
private void addUserSilent(Group group, String exec, String target, User user) {
|
||||
try {
|
||||
addMember(group, exec, target, user);
|
||||
} catch (Exception e) {
|
||||
log.debug("Doppelter User {} wurde nicht zu Gruppe {} hinzugefügt!", user, group);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt, speichert ein AddUserEvent und wendet es auf eine Gruppe an.
|
||||
* Prüft, ob der Nutzer schon Mitglied ist und ob Gruppe voll ist.
|
||||
*/
|
||||
public void addMember(Group group, String exec, String target, User user) {
|
||||
applyAndSave(group, new AddMemberEvent(group.getId(), exec, target, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt, speichert ein DeleteUserEvent und wendet es auf eine Gruppe an.
|
||||
* Prüft, ob der Nutzer Mitglied ist und ob er der letzte Admin ist.
|
||||
*/
|
||||
public void kickMember(Group group, String exec, String target) {
|
||||
applyAndSave(group, new KickMemberEvent(group.getId(), exec, target));
|
||||
|
||||
if (group.isEmpty()) {
|
||||
deleteGroup(group, exec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt, speichert ein DeleteGroupEvent und wendet es auf eine Gruppe an.
|
||||
* Prüft, ob der Nutzer Admin ist.
|
||||
*/
|
||||
public void deleteGroup(Group group, String exec) {
|
||||
if (!group.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new DestroyGroupEvent(group.getId(), exec));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 setTitle(Group group, String exec, @Valid Title title) {
|
||||
if (group.getTitle().equals(title.getValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new SetTitleEvent(group.getId(), exec, title));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 setDescription(Group group, String exec, @Valid Description description) {
|
||||
if (group.getDescription().equals(description.getValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new SetDescriptionEvent(group.getId(), exec, description));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Group group, String exec, String target, Role role) {
|
||||
if (group.memberHasRole(target, role)) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new UpdateRoleEvent(group.getId(), exec, target, role));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 setLimit(Group group, String exec, @Valid Limit userLimit) {
|
||||
if (userLimit.getValue() == group.getLimit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new SetLimitEvent(group.getId(), exec, userLimit));
|
||||
}
|
||||
|
||||
public void setParent(Group group, String exec, Parent parent) {
|
||||
if (parent.getValue() == group.getParent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new SetParentEvent(group.getId(), exec, parent));
|
||||
}
|
||||
|
||||
//TODO: UI Link regenerieren button
|
||||
public void setLink(Group group, String exec, @Valid Link link) {
|
||||
if (group.getLink().equals(link.getValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new SetInviteLinkEvent(group.getId(), exec, link));
|
||||
}
|
||||
|
||||
private void setType(Group group, String exec, Type type) {
|
||||
if (group.getType() == type) {
|
||||
return;
|
||||
}
|
||||
|
||||
applyAndSave(group, new SetTypeEvent(group.getId(), exec, type));
|
||||
}
|
||||
|
||||
private void applyAndSave(Group group, Event event) throws EventException {
|
||||
event.init(group.version() + 1);
|
||||
event.apply(group, groupCache);
|
||||
|
||||
eventStoreService.saveEvent(event);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package mops.gruppen2.domain.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Log4j2
|
||||
public class SearchService {
|
||||
|
||||
private final GroupCache groupCache;
|
||||
|
||||
/**
|
||||
* Filtert alle öffentliche Gruppen nach dem Suchbegriff und gibt diese als sortierte Liste zurück.
|
||||
* Groß- und Kleinschreibung wird nicht beachtet.
|
||||
* Der Suchbegriff wird im Gruppentitel und in der Beschreibung gesucht.
|
||||
*
|
||||
* @param search Der Suchstring
|
||||
*
|
||||
* @return Liste von projizierten Gruppen
|
||||
*
|
||||
* @throws EventException Projektionsfehler
|
||||
*/
|
||||
public List<Group> searchString(String search, String principal) {
|
||||
List<Group> groups = new ArrayList<>();
|
||||
groups.addAll(groupCache.publics());
|
||||
groups.addAll(groupCache.lectures());
|
||||
groups = removeUserGroups(groups, principal);
|
||||
|
||||
if (search.isEmpty()) {
|
||||
return groups;
|
||||
}
|
||||
|
||||
log.debug("Es wurde gesucht nach: {}", search);
|
||||
return groups.stream()
|
||||
.filter(group -> group.format().toLowerCase().contains(search.toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Group> searchType(Type type, String principal) {
|
||||
log.debug("Es wurde gesucht nach: {}", type);
|
||||
|
||||
if (type == Type.LECTURE) {
|
||||
return removeUserGroups(groupCache.lectures(), principal);
|
||||
}
|
||||
if (type == Type.PUBLIC) {
|
||||
return removeUserGroups(groupCache.publics(), principal);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<Group> removeUserGroups(List<Group> groups, String principal) {
|
||||
return groups.stream()
|
||||
.filter(group -> !group.isMember(principal))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package mops.gruppen2.domain.service.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.infrastructure.api.GroupRequestWrapper;
|
||||
import mops.gruppen2.infrastructure.api.GroupWrapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class APIHelper {
|
||||
|
||||
public static GroupRequestWrapper wrap(long status, List<Group> groupList) {
|
||||
return new GroupRequestWrapper(status, wrap(groupList));
|
||||
}
|
||||
|
||||
public static List<GroupWrapper> wrap(List<Group> groups) {
|
||||
return groups.stream()
|
||||
.map(GroupWrapper::new)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package mops.gruppen2.domain.service.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class CommonHelper {
|
||||
|
||||
public static boolean uuidIsEmpty(UUID uuid) {
|
||||
return "00000000-0000-0000-0000-000000000000".equals(uuid.toString());
|
||||
}
|
||||
|
||||
public static List<UUID> stringsToUUID(List<String> changedGroupIds) {
|
||||
return changedGroupIds.stream()
|
||||
.map(UUID::fromString)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
package mops.gruppen2.domain.service.helper;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
|
||||
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.event.Event;
|
||||
import mops.gruppen2.domain.exception.EventException;
|
||||
import mops.gruppen2.domain.exception.GroupNotFoundException;
|
||||
import mops.gruppen2.domain.exception.WrongFileException;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
import mops.gruppen2.persistance.dto.EventDTO;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class FileHelper {
|
||||
|
||||
// ######################################## CSV #############################################
|
||||
|
||||
|
||||
public static List<User> readCsvFile(MultipartFile file) throws EventException {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
List<User> userList = readCsv(file.getInputStream());
|
||||
return userList.stream()
|
||||
.distinct()
|
||||
.collect(Collectors.toList()); //filter duplicates from list
|
||||
} catch (IOException e) {
|
||||
log.error("File konnte nicht gelesen werden!", e);
|
||||
throw new WrongFileException(file.getOriginalFilename());
|
||||
}
|
||||
}
|
||||
|
||||
private static List<User> readCsv(InputStream stream) throws IOException {
|
||||
CsvMapper mapper = new CsvMapper();
|
||||
|
||||
CsvSchema schema = mapper.schemaFor(User.class).withHeader().withColumnReordering(true);
|
||||
ObjectReader reader = mapper.readerFor(User.class).with(schema);
|
||||
|
||||
return reader.<User>readValues(stream).readAll();
|
||||
}
|
||||
|
||||
public static String writeCsvUserList(List<User> members) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("id,givenname,familyname,email\n");
|
||||
|
||||
members.forEach(user -> builder.append(user.getId())
|
||||
.append(",")
|
||||
.append(user.getGivenname())
|
||||
.append(",")
|
||||
.append(user.getFamilyname())
|
||||
.append(",")
|
||||
.append(user.getEmail())
|
||||
.append("\n"));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
// ########################################## JSON ###########################################
|
||||
|
||||
|
||||
/**
|
||||
* Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload.
|
||||
*
|
||||
* @param event Java-Event-Repräsentation
|
||||
*
|
||||
* @return JSON-Event-Payload als String
|
||||
*
|
||||
* @throws JsonProcessingException Bei JSON Fehler
|
||||
*/
|
||||
|
||||
public static String serializeEventJson(Event event) throws JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
|
||||
String payload = mapper.writeValueAsString(event);
|
||||
log.trace(payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Übersetzt eine JSON-Event-Payload zu einer Java-Event-Repräsentation.
|
||||
*
|
||||
* @param json JSON-Event-Payload als String
|
||||
*
|
||||
* @return Java-Event-Repräsentation
|
||||
*
|
||||
* @throws JsonProcessingException Bei JSON Fehler
|
||||
*/
|
||||
public static Event deserializeEventJson(String json) throws JsonProcessingException {
|
||||
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
|
||||
Event event = mapper.readValue(json, Event.class);
|
||||
log.trace(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
// ############################################### TXT #######################################
|
||||
|
||||
|
||||
public static String payloadsToPlain(List<String> payloads) {
|
||||
return payloads.stream()
|
||||
.map(payload -> payload + "\n")
|
||||
.reduce((String payloadA, String payloadB) -> payloadA + payloadB)
|
||||
.orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden."));
|
||||
}
|
||||
|
||||
public static String eventDTOsToSql(List<EventDTO> dtos) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("INSERT INTO event(group_id, group_version, exec_id, target_id, event_date, event_payload)\nVALUES\n");
|
||||
|
||||
dtos.forEach(dto -> builder.append("('")
|
||||
.append(dto.getGroup_id())
|
||||
.append("','")
|
||||
.append(dto.getGroup_version())
|
||||
.append("','")
|
||||
.append(dto.getExec_id())
|
||||
.append("','")
|
||||
.append(dto.getTarget_id())
|
||||
.append("','")
|
||||
.append(dto.getEvent_date())
|
||||
.append("','")
|
||||
.append(dto.getEvent_payload())
|
||||
.append("'),\n"));
|
||||
|
||||
builder.replace(builder.length() - 2, builder.length(), ";");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
// ############################################### SQL #######################################
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package mops.gruppen2.domain.service.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.event.Event;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Liefert verschiedene Projektionen auf Gruppen.
|
||||
* Benötigt ausschließlich den EventStoreService.
|
||||
*/
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class ProjectionHelper {
|
||||
|
||||
public static List<Group> project(List<Event> events) {
|
||||
Map<UUID, Group> groups = new HashMap<>();
|
||||
|
||||
if (events.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
log.trace(groups);
|
||||
log.trace(events);
|
||||
|
||||
events.forEach(event -> event.apply(getOrCreateGroup(groups, event.getGroupid())));
|
||||
|
||||
return new ArrayList<>(groups.values());
|
||||
}
|
||||
|
||||
public static void project(Map<UUID, Group> groups, List<Event> events, GroupCache cache) {
|
||||
if (events.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.trace(groups);
|
||||
log.trace(events);
|
||||
|
||||
events.forEach(event -> event.apply(getOrCreateGroup(groups, event.getGroupid()), cache));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Gruppe mit der richtigen Id aus der übergebenen Map wieder, existiert diese nicht
|
||||
* wird die Gruppe erstellt und der Map hizugefügt.
|
||||
*
|
||||
* @param groups Map aus GruppenIds und Gruppen
|
||||
* @param groupId Die Id der Gruppe, die zurückgegeben werden soll
|
||||
*
|
||||
* @return Die gesuchte Gruppe
|
||||
*/
|
||||
private static Group getOrCreateGroup(Map<UUID, Group> groups, UUID groupId) {
|
||||
if (!groups.containsKey(groupId)) {
|
||||
groups.put(groupId, Group.EMPTY());
|
||||
}
|
||||
|
||||
return groups.get(groupId);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package mops.gruppen2.domain.service.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import mops.gruppen2.domain.model.group.Membership;
|
||||
import mops.gruppen2.domain.model.group.Role;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class SortHelper {
|
||||
|
||||
public static List<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;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package mops.gruppen2.domain.service.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.BadArgumentException;
|
||||
import mops.gruppen2.domain.exception.GroupFullException;
|
||||
import mops.gruppen2.domain.exception.LastAdminException;
|
||||
import mops.gruppen2.domain.exception.NoAccessException;
|
||||
import mops.gruppen2.domain.exception.UserExistsException;
|
||||
import mops.gruppen2.domain.exception.UserNotFoundException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
|
||||
@Log4j2
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class ValidationHelper {
|
||||
|
||||
public static boolean checkIfLastMember(Group group, String userid) {
|
||||
return group.isMember(userid) && group.size() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Überprüft, ob ein User in einer Gruppe Admin ist.
|
||||
*/
|
||||
public static boolean checkIfAdmin(Group group, String userid) {
|
||||
if (group.isMember(userid)) {
|
||||
return group.isAdmin(userid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean checkIfLastAdmin(Group group, String userid) {
|
||||
return checkIfAdmin(group, userid) && group.getAdmins().size() == 1;
|
||||
}
|
||||
|
||||
|
||||
// ######################################## THROW ############################################
|
||||
|
||||
|
||||
public static void throwIfMember(Group group, String userid) throws UserExistsException {
|
||||
if (group.isMember(userid)) {
|
||||
log.error("Benutzer {} ist schon in Gruppe {}", userid, group);
|
||||
throw new UserExistsException(userid);
|
||||
}
|
||||
}
|
||||
|
||||
public static void throwIfNoMember(Group group, String userid) throws UserNotFoundException {
|
||||
if (!group.isMember(userid)) {
|
||||
log.error("Benutzer {} ist nicht in Gruppe {}!", userid, group);
|
||||
throw new UserNotFoundException(userid);
|
||||
}
|
||||
}
|
||||
|
||||
public static void throwIfNoAdmin(Group group, String userid) throws NoAccessException {
|
||||
if (!checkIfAdmin(group, userid)) {
|
||||
log.error("User {} ist kein Admin in Gruppe {}!", userid, group);
|
||||
throw new NoAccessException(group.getId().toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schmeißt keine Exception, wenn der User der letzte User ist.
|
||||
*/
|
||||
public static void throwIfLastAdmin(Group group, String userid) throws LastAdminException {
|
||||
if (!checkIfLastMember(group, userid) && checkIfLastAdmin(group, userid)) {
|
||||
throw new LastAdminException("Du bist letzter Admin!");
|
||||
}
|
||||
}
|
||||
|
||||
public static void throwIfGroupFull(Group group) throws GroupFullException {
|
||||
if (group.isFull()) {
|
||||
log.error("Die Gruppe {} ist voll!", group);
|
||||
throw new GroupFullException(group.getId().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateCreateForm(KeycloakAuthenticationToken token, Type type) {
|
||||
if (!token.getAccount().getRoles().contains("orga") && type == Type.LECTURE) {
|
||||
throw new BadArgumentException("Nur Orga kann Veranstaltungen erstellen.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package mops.gruppen2.infrastructure;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class ApplicationInit {
|
||||
|
||||
private final GroupCache groupCache;
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void init() {
|
||||
groupCache.init();
|
||||
}
|
||||
}
|
208
src/main/java/mops/gruppen2/infrastructure/GroupCache.java
Normal file
208
src/main/java/mops/gruppen2/infrastructure/GroupCache.java
Normal file
@ -0,0 +1,208 @@
|
||||
package mops.gruppen2.infrastructure;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.domain.exception.GroupNotFoundException;
|
||||
import mops.gruppen2.domain.exception.IdMismatchException;
|
||||
import mops.gruppen2.domain.exception.UserNotFoundException;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.domain.service.EventStoreService;
|
||||
import mops.gruppen2.domain.service.helper.ProjectionHelper;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Cached alle existierenden Gruppen und einige Beziehungen.
|
||||
* Gruppen können nach Typ angefragt werden, nach ID, nach Link oder nach User.
|
||||
* Der Cache wird von den Events aktualisiert.
|
||||
* Beim Aufruf der init() Methode werden alle bisherigen Events projiziert und die Gruppen gespeichert.
|
||||
* Die Komplette Anwendung verwendet eine Instanz des Caches.
|
||||
*/
|
||||
@Log4j2
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class GroupCache {
|
||||
|
||||
private final EventStoreService eventStoreService;
|
||||
|
||||
private final Map<UUID, Group> groups = new HashMap<>();
|
||||
private final Map<String, Group> links = new HashMap<>();
|
||||
private final Map<String, List<Group>> users = new HashMap<>(); // Wird vielleicht zu groß?
|
||||
private final Map<Type, List<Group>> types = new EnumMap<>(Type.class);
|
||||
|
||||
|
||||
// ######################################## CACHE ###########################################
|
||||
|
||||
|
||||
void init() {
|
||||
ProjectionHelper.project(groups, eventStoreService.findAllEvents(), this);
|
||||
}
|
||||
|
||||
|
||||
// ########################################### GETTERS #######################################
|
||||
|
||||
|
||||
public Group group(UUID groupid) {
|
||||
if (!groups.containsKey(groupid)) {
|
||||
throw new GroupNotFoundException("Gruppe ist nicht im Cache.");
|
||||
}
|
||||
|
||||
return groups.get(groupid);
|
||||
}
|
||||
|
||||
public Group group(String link) {
|
||||
if (!links.containsKey(link)) {
|
||||
throw new GroupNotFoundException("Link ist nicht im Cache.");
|
||||
}
|
||||
|
||||
return links.get(link);
|
||||
}
|
||||
|
||||
public List<Group> groups() {
|
||||
if (groups.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return List.copyOf(groups.values());
|
||||
}
|
||||
|
||||
public List<Group> userGroups(String userid) {
|
||||
if (!users.containsKey(userid)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(users.get(userid));
|
||||
}
|
||||
|
||||
public List<Group> userLectures(String userid) {
|
||||
return userGroups(userid).stream()
|
||||
.filter(Group::isLecture)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
public List<Group> userPublics(String userid) {
|
||||
return userGroups(userid).stream()
|
||||
.filter(Group::isPublic)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
public List<Group> userPrivates(String userid) {
|
||||
return userGroups(userid).stream()
|
||||
.filter(Group::isPrivate)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
public List<Group> publics() {
|
||||
if (!types.containsKey(Type.PUBLIC)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(types.get(Type.PUBLIC));
|
||||
}
|
||||
|
||||
public List<Group> privates() {
|
||||
if (!types.containsKey(Type.PRIVATE)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(types.get(Type.PRIVATE));
|
||||
}
|
||||
|
||||
public List<Group> lectures() {
|
||||
if (!types.containsKey(Type.LECTURE)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(types.get(Type.LECTURE));
|
||||
}
|
||||
|
||||
|
||||
// ######################################## SETTERS ##########################################
|
||||
|
||||
|
||||
public void usersPut(String userid, Group group) {
|
||||
if (!group.isMember(userid)) {
|
||||
throw new UserNotFoundException("User ist kein Mitglied, Gruppe nicht gecached.");
|
||||
}
|
||||
if (!users.containsKey(userid)) {
|
||||
users.put(userid, new ArrayList<>());
|
||||
log.debug("Ein User wurde dem Cache hinzugefügt.");
|
||||
}
|
||||
|
||||
users.get(userid).add(group);
|
||||
}
|
||||
|
||||
public void usersRemove(String target, Group group) {
|
||||
if (!users.containsKey(target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
users.get(target).remove(group);
|
||||
}
|
||||
|
||||
public void groupsPut(UUID groupid, Group group) {
|
||||
if (group.getId() != groupid) {
|
||||
throw new IdMismatchException("ID passt nicht zu Gruppe, Gruppe nicht gecached.");
|
||||
}
|
||||
|
||||
groups.put(groupid, group);
|
||||
}
|
||||
|
||||
public void groupsRemove(UUID groupid, Group group) {
|
||||
if (!groups.containsKey(groupid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
groups.remove(groupid);
|
||||
links.remove(group.getLink());
|
||||
group.getMembers().forEach(user -> users.get(user.getId()).removeIf(usergroup -> !usergroup.exists()));
|
||||
types.get(group.getType()).removeIf(typegroup -> !typegroup.exists());
|
||||
}
|
||||
|
||||
public void linksPut(String link, Group group) {
|
||||
if (!link.equals(group.getLink())) {
|
||||
throw new IdMismatchException("Link passt nicht zu Gruppe, Gruppe nicht gecached.");
|
||||
}
|
||||
|
||||
links.put(link, group);
|
||||
}
|
||||
|
||||
public void linksRemove(String link) {
|
||||
if (!links.containsKey(link)) {
|
||||
return;
|
||||
}
|
||||
|
||||
links.remove(link);
|
||||
}
|
||||
|
||||
public void typesPut(Type type, Group group) {
|
||||
if (!types.containsKey(type)) {
|
||||
types.put(type, new ArrayList<>());
|
||||
log.debug("Ein Typ wurde dem Cache hinzugefügt.");
|
||||
}
|
||||
if (group.getType() != type) {
|
||||
throw new IdMismatchException("Typ passt nicht zu Gruppe, Gruppe nicht gecached.");
|
||||
}
|
||||
|
||||
types.get(type).add(group);
|
||||
}
|
||||
|
||||
public void typesRemove(Type type, Group group) {
|
||||
if (!types.containsKey(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
types.get(type).remove(group);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package mops.gruppen2.infrastructure;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import mops.gruppen2.domain.Account;
|
||||
import mops.gruppen2.domain.model.group.Role;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@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("principal", new User(token));
|
||||
}
|
||||
|
||||
// Add enums
|
||||
model.addAttribute("REGULAR", Role.REGULAR);
|
||||
model.addAttribute("ADMIN", Role.ADMIN);
|
||||
model.addAttribute("PUBLIC", Type.PUBLIC);
|
||||
model.addAttribute("PRIVATE", Type.PRIVATE);
|
||||
model.addAttribute("LECTURE", Type.LECTURE);
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,17 @@
|
||||
package mops.gruppen2.domain.api;
|
||||
package mops.gruppen2.infrastructure.api;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import mops.gruppen2.domain.Group;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Kombiniert den Status und die Gruppenliste zur ausgabe über die API.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class GroupRequestWrapper {
|
||||
|
||||
private final Long status;
|
||||
private final List<Group> groupList;
|
||||
private final long version;
|
||||
private final List<GroupWrapper> groups;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package mops.gruppen2.infrastructure.api;
|
||||
|
||||
import lombok.Value;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Value
|
||||
public class GroupWrapper {
|
||||
|
||||
UUID groupid;
|
||||
Type type;
|
||||
UUID parent;
|
||||
String title;
|
||||
String description;
|
||||
List<User> admins;
|
||||
List<User> regulars;
|
||||
|
||||
public GroupWrapper(Group group) {
|
||||
groupid = group.getId();
|
||||
type = group.getType();
|
||||
parent = group.getParent();
|
||||
title = group.getTitle();
|
||||
description = group.getDescription();
|
||||
admins = group.getAdmins();
|
||||
regulars = group.getRegulars();
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package mops.gruppen2.infrastructure.controller;
|
||||
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.service.EventStoreService;
|
||||
import mops.gruppen2.domain.service.helper.APIHelper;
|
||||
import mops.gruppen2.domain.service.helper.ProjectionHelper;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
import mops.gruppen2.infrastructure.api.GroupRequestWrapper;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Api zum Datenabgleich.
|
||||
*/
|
||||
@Log4j2
|
||||
@TraceMethodCalls
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/gruppen2/api")
|
||||
public class APIController {
|
||||
|
||||
private final GroupCache cache;
|
||||
private final EventStoreService eventStoreService;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
//TODO: sollte den cache benutzen, am besten wäre eine groupversion, welche der eventid
|
||||
//TODO: entspricht, dann kann man leicht alle geänderten gruppen finden
|
||||
@GetMapping("/update/{id}")
|
||||
@Secured("ROLE_api_user")
|
||||
@ApiOperation("Gibt veränderte Gruppen zurück")
|
||||
public GroupRequestWrapper getApiUpdate(@ApiParam("Letzte gespeicherte EventId des Anfragestellers")
|
||||
@PathVariable("id") long eventId) {
|
||||
|
||||
return APIHelper.wrap(eventStoreService.findMaxEventId(),
|
||||
ProjectionHelper.project(eventStoreService.findGroupEvents(eventStoreService.findChangedGroups(eventId))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Gruppen-IDs von Gruppen, in welchen der übergebene Nutzer teilnimmt, zurück.
|
||||
*/
|
||||
@GetMapping("/usergroups/{id}")
|
||||
@Secured("ROLE_api_user")
|
||||
@ApiOperation("Gibt Gruppen zurück, in welchen ein Nutzer teilnimmt")
|
||||
public List<String> getApiUserGroups(@ApiParam("Nutzer-Id")
|
||||
@PathVariable("id") String userId) {
|
||||
|
||||
return cache.userGroups(userId).stream()
|
||||
.map(Group::getId)
|
||||
.map(UUID::toString)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getApiGroup(@ApiParam("Gruppen-Id der gefordeten Gruppe")
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
return cache.group(UUID.fromString(groupId));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package mops.gruppen2.infrastructure.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.Type;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Description;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Limit;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Parent;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Title;
|
||||
import mops.gruppen2.domain.service.GroupService;
|
||||
import mops.gruppen2.domain.service.helper.FileHelper;
|
||||
import mops.gruppen2.domain.service.helper.ValidationHelper;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
@Log4j2
|
||||
@TraceMethodCalls
|
||||
@RequiredArgsConstructor
|
||||
@Controller
|
||||
@RequestMapping("/gruppen2")
|
||||
public class GroupCreationController {
|
||||
|
||||
private final GroupCache groupCache;
|
||||
private final GroupService groupService;
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping("/create")
|
||||
public String getCreate(Model model) {
|
||||
|
||||
model.addAttribute("lectures", groupCache.lectures());
|
||||
|
||||
return "create";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/create")
|
||||
public String postCreateOrga(KeycloakAuthenticationToken token,
|
||||
@RequestParam("type") Type type,
|
||||
@RequestParam("parent") @Valid Parent parent,
|
||||
@RequestParam("title") @Valid Title title,
|
||||
@RequestParam("description") @Valid Description description,
|
||||
@RequestParam("limit") @Valid Limit limit,
|
||||
@RequestParam(value = "file", required = false) MultipartFile file) {
|
||||
|
||||
// Zusätzlicher check: studentin kann keine lecture erstellen
|
||||
ValidationHelper.validateCreateForm(token, type);
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupService.createGroup(principal);
|
||||
groupService.initGroupMembers(group, principal, principal, new User(token), limit);
|
||||
groupService.initGroupMeta(group, principal, type, parent);
|
||||
groupService.initGroupText(group, principal, title, description);
|
||||
|
||||
// ROLE_studentin kann kein CSV importieren
|
||||
if (token.getAccount().getRoles().contains("orga")) {
|
||||
groupService.addUsersToGroup(group, principal, FileHelper.readCsvFile(file));
|
||||
}
|
||||
|
||||
return "redirect:/gruppen2/details/" + group.getId();
|
||||
}
|
||||
}
|
@ -0,0 +1,296 @@
|
||||
package mops.gruppen2.infrastructure.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.aspect.annotation.TraceMethodCalls;
|
||||
import mops.gruppen2.domain.model.group.Group;
|
||||
import mops.gruppen2.domain.model.group.User;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Description;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Limit;
|
||||
import mops.gruppen2.domain.model.group.wrapper.Title;
|
||||
import mops.gruppen2.domain.service.EventStoreService;
|
||||
import mops.gruppen2.domain.service.GroupService;
|
||||
import mops.gruppen2.domain.service.helper.FileHelper;
|
||||
import mops.gruppen2.domain.service.helper.ValidationHelper;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
@Log4j2
|
||||
@TraceMethodCalls
|
||||
@RequiredArgsConstructor
|
||||
@Controller
|
||||
@RequestMapping("/gruppen2")
|
||||
public class GroupDetailsController {
|
||||
|
||||
private final GroupCache groupCache;
|
||||
private final GroupService groupService;
|
||||
private final EventStoreService eventStoreService;
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping("/details/{id}")
|
||||
public String getDetailsPage(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
// Parent Badge
|
||||
Group parent = Group.EMPTY();
|
||||
if (group.hasParent()) {
|
||||
parent = groupCache.group(group.getParent());
|
||||
}
|
||||
|
||||
model.addAttribute("group", group);
|
||||
model.addAttribute("parent", parent);
|
||||
|
||||
// Detailseite für nicht-Mitglieder
|
||||
if (!group.isMember(principal)) {
|
||||
return "preview";
|
||||
}
|
||||
|
||||
return "details";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/join")
|
||||
public String postDetailsJoin(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
if (group.isMember(principal)) {
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
|
||||
groupService.addMember(group, principal, principal, new User(token));
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/leave")
|
||||
public String postDetailsLeave(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
groupService.kickMember(group, principal, principal);
|
||||
|
||||
return "redirect:/gruppen2";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping("details/{id}/history")
|
||||
public String getDetailsHistory(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
model.addAttribute("events",
|
||||
eventStoreService.findGroupEvents(UUID.fromString(groupId)));
|
||||
|
||||
return "log";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping(value = "details/{id}/export/history/plain", produces = "text/plain;charset=UTF-8")
|
||||
public void getDetailsExportHistoryPlain(HttpServletResponse response,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String filename = "eventlog-" + groupId + ".txt";
|
||||
|
||||
response.setContentType("text/txt;charset=UTF-8");
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + filename + "\"");
|
||||
|
||||
try {
|
||||
response.getWriter()
|
||||
.write(FileHelper.payloadsToPlain(
|
||||
eventStoreService.findGroupPayloads(UUID.fromString(groupId))));
|
||||
} catch (IOException e) {
|
||||
log.error("Payloads konnten nicht geschrieben werden.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping(value = "details/{id}/export/history/sql", produces = "application/sql;charset=UTF-8")
|
||||
public void getDetailsExportHistorySql(HttpServletResponse response,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String filename = "data.sql";
|
||||
|
||||
response.setContentType("application/sql;charset=UTF-8");
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + filename + "\"");
|
||||
|
||||
try {
|
||||
response.getWriter()
|
||||
.write(FileHelper.eventDTOsToSql(
|
||||
eventStoreService.findGroupDTOs(UUID.fromString(groupId))));
|
||||
} catch (IOException e) {
|
||||
log.error("Payloads konnten nicht geschrieben werden.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping(value = "details/{id}/export/members", produces = "text/csv;charset=UTF-8")
|
||||
public void getDetailsExportMembers(HttpServletResponse response,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String filename = "teilnehmer-" + groupId + ".csv";
|
||||
|
||||
response.setContentType("text/csv;charset=UTF-8");
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + filename + "\"");
|
||||
|
||||
try {
|
||||
response.getWriter()
|
||||
.print(FileHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers()));
|
||||
} catch (IOException e) {
|
||||
log.error("Teilnehmerliste konnte nicht geschrieben werden.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@GetMapping("/details/{id}/edit")
|
||||
public String getDetailsEdit(KeycloakAuthenticationToken token,
|
||||
Model model,
|
||||
HttpServletRequest request,
|
||||
@PathVariable("id") String groupId) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
// Invite Link
|
||||
String actualURL = request.getRequestURL().toString();
|
||||
String serverURL = actualURL.substring(0, actualURL.indexOf("gruppen2/"));
|
||||
String link = serverURL + "gruppen2/join/" + group.getLink();
|
||||
|
||||
ValidationHelper.throwIfNoAdmin(group, principal);
|
||||
|
||||
model.addAttribute("group", group);
|
||||
model.addAttribute("link", link);
|
||||
|
||||
return "edit";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/edit/meta")
|
||||
public String postDetailsEditMeta(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId,
|
||||
@Valid Title title,
|
||||
@Valid Description description) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
groupService.setTitle(group, principal, title);
|
||||
groupService.setDescription(group, principal, description);
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId + "/edit";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/edit/userlimit")
|
||||
public String postDetailsEditUserLimit(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId,
|
||||
@Valid Limit limit) {
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
groupService.setLimit(group, principal, limit);
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId + "/edit";
|
||||
}
|
||||
|
||||
@RolesAllowed("ROLE_orga")
|
||||
@PostMapping("/details/{id}/edit/csv")
|
||||
public String postDetailsEditCsv(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId,
|
||||
@RequestParam(value = "file", required = false) MultipartFile file) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
groupService.addUsersToGroup(group, principal, FileHelper.readCsvFile(file));
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId + "/edit";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/edit/role/{userid}")
|
||||
public String postDetailsEditRole(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId,
|
||||
@PathVariable("userid") String target) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
ValidationHelper.throwIfNoAdmin(group, principal);
|
||||
if (target.equals(principal)) {
|
||||
ValidationHelper.throwIfLastAdmin(group, principal);
|
||||
}
|
||||
|
||||
groupService.toggleMemberRole(group, principal, target);
|
||||
|
||||
// Falls sich der User selbst die Rechte genommen hat
|
||||
if (!ValidationHelper.checkIfAdmin(group, principal)) {
|
||||
return "redirect:/gruppen2/details/" + groupId;
|
||||
}
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId + "/edit";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/edit/delete/{userid}")
|
||||
public String postDetailsEditDelete(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupId,
|
||||
@PathVariable("userid") String target) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupId));
|
||||
|
||||
ValidationHelper.throwIfNoAdmin(group, principal);
|
||||
|
||||
// Der eingeloggte User kann sich nicht selbst entfernen (er kann aber verlassen)
|
||||
if (!principal.equals(target)) {
|
||||
groupService.kickMember(group, principal, target);
|
||||
}
|
||||
|
||||
return "redirect:/gruppen2/details/" + groupId + "/edit";
|
||||
}
|
||||
|
||||
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
|
||||
@PostMapping("/details/{id}/edit/destroy")
|
||||
public String postDetailsEditDestroy(KeycloakAuthenticationToken token,
|
||||
@PathVariable("id") String groupid) {
|
||||
|
||||
String principal = token.getName();
|
||||
Group group = groupCache.group(UUID.fromString(groupid));
|
||||
|
||||
groupService.deleteGroup(group, principal);
|
||||
|
||||
return "redirect:/gruppen2";
|
||||
}
|
||||
|
||||
//TODO: Method + view for /details/{id}/member/{id}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package mops.gruppen2.controller;
|
||||
package mops.gruppen2.infrastructure.controller;
|
||||
|
||||
import mops.gruppen2.domain.Account;
|
||||
import mops.gruppen2.domain.User;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import mops.gruppen2.aspect.annotation.TraceMethodCall;
|
||||
import mops.gruppen2.domain.exception.PageNotFoundException;
|
||||
import mops.gruppen2.service.KeyCloakService;
|
||||
import mops.gruppen2.service.UserService;
|
||||
import mops.gruppen2.infrastructure.GroupCache;
|
||||
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
@ -14,31 +14,29 @@ import javax.annotation.security.RolesAllowed;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
@Log4j2
|
||||
@RequiredArgsConstructor
|
||||
@Controller
|
||||
public class GruppenfindungController {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public GruppenfindungController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
private final GroupCache groupCache;
|
||||
|
||||
// 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) {
|
||||
|
||||
Account account = KeyCloakService.createAccountFromPrincipal(token);
|
||||
User user = new User(account);
|
||||
|
||||
model.addAttribute("account", account);
|
||||
model.addAttribute("gruppen", userService.getUserGroups(user));
|
||||
model.addAttribute("user", user);
|
||||
model.addAttribute("lectures", groupCache.userLectures(token.getName()));
|
||||
model.addAttribute("publics", groupCache.userPublics(token.getName()));
|
||||
model.addAttribute("privates", groupCache.userPrivates(token.getName()));
|
||||
|
||||
return "index";
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user