Compare commits

..

37 Commits

Author SHA1 Message Date
ded7b2a370 Updated pom to maven v4 2026-01-31 22:52:55 -05:00
3c5791bd84 Update dependencies 2026-01-07 12:13:47 -05:00
949d9805e5 Update dependencies 2025-08-10 11:38:03 -04:00
30fbe5d970 Update logging 2025-08-09 23:30:38 -04:00
77adf13d65 Update dependencies and plugins 2025-05-24 12:52:53 -04:00
df07a5d15f Update service functions maintain create auditing fields 2025-04-06 22:25:40 -04:00
b618d57143 Add validation to utils 2025-04-06 21:59:06 -04:00
60aa0bcb7e Fixed unknown properties 2025-03-22 23:53:39 -04:00
01fca599ac Confirm and password emails sending 2025-03-22 23:47:17 -04:00
b6fb636ddd Fix adhoc Raid Instance saving 2025-03-16 15:07:05 -04:00
6c0ffccd60 Handle file stream not closing 2025-03-16 12:07:26 -04:00
de23a8bfa6 Update file saving code 2025-03-16 11:32:18 -04:00
2439c9b3cb Update build parameters 2025-03-16 09:54:12 -04:00
73a4f9d603 Password reset working 2025-03-15 21:27:18 -04:00
1b31144f44 Raid Instances showing up on calendar 2025-03-15 18:37:47 -04:00
2778740cef Tutorial working 2025-03-15 18:23:24 -04:00
a86f0302de Buttons hidden by permissions 2025-03-15 16:51:10 -04:00
8eb3c424c3 Raid Instance Creator working 2025-03-15 12:20:09 -04:00
f2f05f5adf Raid Instance tab working 2025-03-11 22:46:35 -04:00
15036d7e0f Person page working 2025-03-10 22:55:26 -04:00
09986bb81d Raid Request button working 2025-03-10 20:30:44 -04:00
c58df66e75 Requests tab working 2025-03-10 19:19:53 -04:00
5dc0ab1e10 User tab working 2025-03-09 19:49:45 -04:00
63486457fe Raid Layout page working 2025-03-09 12:15:34 -04:00
01c27d0c5a Class Groups tab working 2025-03-08 18:45:51 -05:00
868daeb517 Person tab working 2025-03-08 13:26:59 -05:00
3e0996c432 Raid Group calendar working 2025-03-07 21:54:30 -05:00
18f048bc3b Raid groups page working 2025-03-06 23:54:34 -05:00
fa3738f88e Add icon directory to gitignore 2025-03-06 22:34:35 -05:00
5777898834 Make sure all icon directories work 2025-03-06 22:33:20 -05:00
0bcc57b614 Game Classes tab working 2025-03-06 22:32:04 -05:00
78453ebfa1 Game calendar working 2025-03-06 19:49:23 -05:00
70d3b22cdc Created indices on search and filter fields 2025-03-05 20:25:42 -05:00
aff81e27eb Admin page raid group tab working 2025-03-05 20:12:32 -05:00
f528cbaa2b Games tab on admin page working 2025-03-04 21:14:22 -05:00
dd4480cf4e Finished account page users tab 2025-03-02 20:58:46 -05:00
02c615ee0c Authorization working 2025-02-22 16:54:37 -05:00
175 changed files with 9449 additions and 0 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
.settings/ .settings/
.classpath .classpath
.project .project
temp/
#Ignore the compiled files #Ignore the compiled files
target/ target/

2
.mvn/maven.config Normal file
View File

@@ -0,0 +1,2 @@
-Dstyle.color=always
-T1C

14
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"configurations": [
{
"type": "java",
"name": "Spring Boot-RaidBuilderAPI<raid-builder-api>",
"request": "launch",
"cwd": "${workspaceFolder}",
"mainClass": "com.mattrixwv.raidbuilder.RaidBuilderAPI",
"projectName": "raid-builder-api",
"args": "--spring.config.additional-location=local/application.properties --logging.config=local/log4j2-spring.xml",
"envFile": "${workspaceFolder}/.env"
}
]
}

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"cSpell.words": [
"raidbuilder",
"springframework"
],
"sonarlint.connectedMode.project": {
"connectionId": "mattrixwvSonarqube",
"projectKey": "RaidBuilderAPI"
}
}

View File

@@ -0,0 +1,3 @@
# Raid Builder API
[![Quality Gate Status](https://sonarqube.mattrixwv.com/api/project_badges/measure?project=RaidBuilderAPI&metric=alert_status&token=sqb_ab4c1e39be4304f908cc73a4c94c1b40577a4e9f)](https://sonarqube.mattrixwv.com/dashboard?id=RaidBuilderAPI)

View File

@@ -0,0 +1,25 @@
java.lang.reflect.Method
org.apache.cataline
org.springframework.aop
org.springframework.security
org.springframework.transaction
org.springframework.web
sun.reflect
net.sf.cglib
ByCGLIB
//Thsea re ones I'm removing because I'm not seeing anythign important in them
BySpringCGLIB //Spring impl of CGLIB?
org.springframework.cglib
org.springframework.orm //Spring automagic
org.springframework.dao //Spring automagic
org.springframework.session //Spring automagic
org.springframework.repository //Spring automagic
org.springframework.data //Spring automagic
org.springframework.scheduling //Spring automagic scheduling
org.springframework.beans //Spring automagic beans
org.springframework.boot //Spring Boot automagic
jdk.internal.reflect //Java automagic
sun.proxy //Java automagic
io.netty.util.concurrent //Server framework threading
com.google.common.util.concurrent //Threading
java.util.concurrent //Threading

4
db/1.0.0/1. schema.sql Normal file
View File

@@ -0,0 +1,4 @@
CREATE USER raid_builder WITH PASSWORD 'raid_builder';
CREATE SCHEMA raid_builder;
GRANT ALL ON SCHEMA raid_builder TO raid_builder;

View File

@@ -0,0 +1,17 @@
CREATE TABLE raid_builder.game_class(
game_class_id uuid PRIMARY KEY,
game_id uuid REFERENCES raid_builder.game(game_id),
game_class_name text NOT NULL,
game_class_icon text,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_game_class_game_id ON raid_builder.game_class(game_id);
CREATE INDEX idx_game_class_game_class_name ON raid_builder.game_class(game_class_name);
GRANT ALL ON TABLE raid_builder.game_class TO raid_builder;

View File

@@ -0,0 +1,18 @@
CREATE TABLE raid_builder.raid_group_calendar_event(
raid_group_calendar_event_id uuid PRIMARY KEY,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id) NOT NULL,
event_name text NOT NULL,
event_description text,
event_start_date timestamptz NOT NULL,
event_end_date timestamptz NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_group_calendar_event_raid_group_id ON raid_builder.raid_group_calendar_event(raid_group_id);
GRANT ALL ON TABLE raid_builder.raid_group_calendar_event TO raid_builder;

View File

@@ -0,0 +1,16 @@
CREATE TABLE raid_builder.person(
person_id uuid PRIMARY KEY,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id) NOT NULL,
person_name text NOT NULL,
discord_id text,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_person_raid_group_id ON raid_builder.person(raid_group_id);
GRANT ALL ON TABLE raid_builder.person TO raid_builder;

View File

@@ -0,0 +1,21 @@
CREATE TABLE raid_builder.person_character(
person_character_id uuid PRIMARY KEY,
person_id uuid REFERENCES raid_builder.person(person_id) NOT NULL,
game_class_id uuid REFERENCES raid_builder.game_class(game_class_id) NOT NULL,
character_name text NOT NULL,
character_rating int,
character_comments text,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_person_character_person_id ON raid_builder.person_character(person_id);
CREATE INDEX idx_person_character_game_class_id ON raid_builder.person_character(game_class_id);
CREATE INDEX idx_person_character_character_name ON raid_builder.person_character(character_name);
CREATE INDEX idx_person_character_character_rading ON raid_builder.person_character(character_rating);
GRANT ALL ON TABLE raid_builder.person_character TO raid_builder;

View File

@@ -0,0 +1,34 @@
CREATE TABLE raid_builder.class_group(
class_group_id uuid PRIMARY KEY,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id) NOT NULL,
class_group_name text NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_class_group_raid_group_id ON raid_builder.class_group(raid_group_id);
GRANT ALL ON TABLE raid_builder.class_group TO raid_builder;
CREATE TABLE raid_builder.class_group_game_class_xref(
class_group_game_class_xref_id uuid PRIMARY KEY,
class_group_id uuid REFERENCES raid_builder.class_group(class_group_id) NOT NULL,
game_class_id uuid REFERENCES raid_builder.game_class(game_class_id) NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_class_group_game_class_xref_class_group_id ON raid_builder.class_group_game_class_xref(class_group_id);
CREATE INDEX idx_class_group_game_class_xref_game_class_id ON raid_builder.class_group_game_class_xref(game_class_id);
GRANT ALL ON TABLE raid_builder.class_group_game_class_xref TO raid_builder;

View File

@@ -0,0 +1,37 @@
CREATE TABLE raid_builder.raid_layout(
raid_layout_id uuid PRIMARY KEY,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id) NOT NULL,
raid_layout_name text NOT NULL,
raid_size int NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_layout_raid_group_id ON raid_builder.raid_layout(raid_group_id);
CREATE INDEX idx_raid_layout_raid_layout_name ON raid_builder.raid_layout(raid_layout_name);
GRANT ALL ON TABLE raid_builder.raid_layout TO raid_builder;
CREATE TABLE raid_builder.raid_layout_class_group_xref(
raid_layout_class_group_xref_id uuid PRIMARY KEY,
raid_layout_id uuid REFERENCES raid_builder.raid_layout(raid_layout_id) NOT NULL,
class_group_id uuid REFERENCES raid_builder.class_group(class_group_id) NOT NULL,
layout_location int NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_layout_class_group_xref_raid_layout_id ON raid_builder.raid_layout_class_group_xref(raid_layout_id);
CREATE INDEX idx_raid_layout_class_group_xref_class_group_id ON raid_builder.raid_layout_class_group_xref(class_group_id);
GRANT ALL ON TABLE raid_builder.raid_layout_class_group_xref TO raid_builder;

View File

@@ -0,0 +1,17 @@
CREATE TABLE raid_builder.raid_group_request(
raid_group_request_id uuid PRIMARY KEY,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id) NOT NULL,
account_id uuid REFERENCES raid_builder.account(account_id) NOT NULL,
request_message text,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_group_request_raid_group_id ON raid_builder.raid_group_request(raid_group_id);
CREATE INDEX idx_raid_group_request_account_id ON raid_builder.raid_group_request(account_id);
GRANT ALL ON TABLE raid_builder.raid_group_request TO raid_builder;

View File

@@ -0,0 +1,23 @@
CREATE TABLE raid_builder.raid_instance (
raid_instance_id uuid PRIMARY KEY,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id),
raid_layout_id uuid REFERENCES raid_builder.raid_layout(raid_layout_id),
raid_instance_name text,
raid_size int,
number_runs int NOT NULL,
raid_start_date timestamptz NOT NULL,
raid_end_date timestamptz NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_instance_raid_group_id ON raid_builder.raid_instance(raid_group_id);
CREATE INDEX idx_raid_instance_raid_layout_id ON raid_builder.raid_instance(raid_layout_id);
CREATE INDEX idx_raid_instance_raid_instance_name ON raid_builder.raid_instance(raid_instance_name);
CREATE INDEX idx_raid_instance_raid_start_date ON raid_builder.raid_instance(raid_start_date);
GRANT ALL ON TABLE raid_builder.raid_instance TO raid_builder;

View File

@@ -0,0 +1,18 @@
CREATE TABLE raid_builder.raid_instance_person_character_xref(
raid_instance_person_character_xref_id uuid PRIMARY KEY,
raid_instance_id uuid REFERENCES raid_builder.raid_instance(raid_instance_id) NOT NULL,
person_character_id uuid REFERENCES raid_builder.person_character(person_character_id),
group_number int NOT NULL,
position_number int NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_instance_person_character_xref_raid_instance_id ON raid_builder.raid_instance_person_character_xref(raid_instance_id);
CREATE INDEX idx_raid_instance_person_character_xref_person_character_id ON raid_builder.raid_instance_person_character_xref(person_character_id);
GRANT ALL ON TABLE raid_builder.raid_instance_person_character_xref TO raid_builder;

View File

@@ -0,0 +1,18 @@
CREATE TYPE raid_builder.account_status AS ENUM ( 'ACTIVE', 'LOCKED', 'INACTIVE', 'DELETED', 'UNCONFIRMED');
CREATE TABLE IF NOT EXISTS raid_builder.account(
account_id uuid PRIMARY KEY,
username text UNIQUE NOT NULL,
password text NOT NULL,
login_date timestamptz,
email text UNIQUE NOT NULL,
force_reset boolean NOT NULL,
refresh_token uuid UNIQUE,
refresh_token_expiration timestamptz,
account_status raid_builder.account_status NOT NULL
);
CREATE INDEX idx_account_username ON raid_builder.account(username);
GRANT ALL ON TABLE raid_builder.account TO raid_builder;

View File

@@ -0,0 +1,12 @@
CREATE TYPE raid_builder.account_permission_type AS ENUM ( 'ADMIN', 'USER' );
CREATE TABLE IF NOT EXISTS raid_builder.account_permission(
account_permission_id uuid PRIMARY KEY,
account_id uuid REFERENCES raid_builder.account(account_id) NOT NULL,
account_permission_type raid_builder.account_permission_type NOT NULL
);
CREATE INDEX idx_account_permission_account_id ON raid_builder.account_permission(account_id);
GRANT ALL ON TABLE raid_builder.account_permission TO raid_builder;

View File

@@ -0,0 +1,16 @@
CREATE TYPE raid_builder.tutorial_status AS ENUM ( 'COMPLETED', 'NOT_COMPLETED' );
CREATE TABLE IF NOT EXISTS raid_builder.account_tutorial_status (
account_tutorial_status_id uuid PRIMARY KEY,
account_id uuid REFERENCES raid_builder.account(account_id),
games_tutorial_status raid_builder.tutorial_status NOT NULL,
game_tutorial_status raid_builder.tutorial_status NOT NULL,
raid_groups_tutorial_status raid_builder.tutorial_status NOT NULL,
raid_group_tutorial_status raid_builder.tutorial_status NOT NULL,
instance_tutorial_status raid_builder.tutorial_status NOT NULL
);
CREATE INDEX idx_account_tutorial_status_account_id ON raid_builder.account_tutorial_status (account_id);
GRANT ALL ON TABLE raid_builder.account_tutorial_status TO raid_builder;

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS raid_builder.game(
game_id uuid PRIMARY KEY,
game_name text UNIQUE NOT NULL,
game_icon text,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_game_game_name ON raid_builder.game(game_name);
GRANT ALL ON TABLE raid_builder.game TO raid_builder;

View File

@@ -0,0 +1,20 @@
CREATE TYPE raid_builder.game_permission_type AS ENUM ( 'ADMIN' );
CREATE TABLE IF NOT EXISTS raid_builder.game_permission(
game_permission_id uuid PRIMARY KEY,
account_id uuid REFERENCES raid_builder.account(account_id) NOT NULL,
game_id uuid REFERENCES raid_builder.game(game_id) NOT NULL,
game_permission_type raid_builder.game_permission_type NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_game_permission_account_id ON raid_builder.game_permission(account_id);
CREATE INDEX idx_game_permission_game_id ON raid_builder.game_permission(game_id);
GRANT ALL ON TABLE raid_builder.game_permission TO raid_builder;

View File

@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS raid_builder.raid_group(
raid_group_id uuid PRIMARY KEY,
game_id uuid REFERENCES raid_builder.game(game_id) NOT NULL,
raid_group_name text NOT NULL,
raid_group_icon text,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_group_game_id ON raid_builder.raid_group(game_id);
CREATE INDEX idx_raid_group_raid_group_name ON raid_builder.raid_group(raid_group_name);
GRANT ALL ON TABLE raid_builder.raid_group TO raid_builder;

View File

@@ -0,0 +1,20 @@
CREATE TYPE raid_builder.raid_group_permission_type AS ENUM ( 'ADMIN', 'LEADER', 'RAIDER' );
CREATE TABLE IF NOT EXISTS raid_builder.raid_group_permission(
raid_group_permission_id uuid PRIMARY KEY,
account_id uuid REFERENCES raid_builder.account(account_id) NOT NULL,
raid_group_id uuid REFERENCES raid_builder.raid_group(raid_group_id) NOT NULL,
permission raid_builder.raid_group_permission_type NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_raid_group_permission_account_id ON raid_builder.raid_group_permission(account_id);
CREATE INDEX idx_raid_group_permission_raid_group_id ON raid_builder.raid_group_permission(raid_group_id);
GRANT ALL ON TABLE raid_builder.raid_group_permission TO raid_builder;

View File

@@ -0,0 +1,18 @@
CREATE TABLE raid_builder.game_calendar_event(
game_calendar_event_id uuid PRIMARY KEY,
game_id uuid REFERENCES raid_builder.game(game_id) NOT NULL,
event_name text NOT NULL,
event_description text,
event_start_date timestamptz NOT NULL,
event_end_date timestamptz NOT NULL,
--Auditing
modified_by uuid,
modified_date timestamptz,
created_by uuid NOT NULL,
created_date timestamptz NOT NULL
);
CREATE INDEX idx_game_calendar_event_game_id ON raid_builder.game_calendar_event(game_id);
GRANT ALL ON TABLE raid_builder.game_calendar_event TO raid_builder;

16
dependencySuppression.xml Normal file
View File

@@ -0,0 +1,16 @@
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress>
<notes><![CDATA[
Spring Boot devs say this is not a problem
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.springframework/spring\-web@.*$</packageUrl>
<cve>CVE-2016-1000027</cve>
</suppress>
<suppress>
<notes><![CDATA[
False positive, CVE only affects plugins and devtools are not included in production builds
]]></notes>
<packageUrl regex="true">^pkg:maven/org\.springframework\.boot/spring-boot-devtools@.*$</packageUrl>
<cve>CVE-2022-31691</cve>
</suppress>
</suppressions>

380
pom.xml Normal file
View File

@@ -0,0 +1,380 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mattrixwv.raidbuilder</groupId>
<artifactId>raid-builder-api</artifactId>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<name>Raid Builder API</name>
<url>https://api.raidbuilder.mattrixwv.com</url>
<developers>
<developer>
<name>Matthew Ellison</name>
<email>m_ellison@ymail.com</email>
<url>https://git.mattrixwv.com/matthew</url>
</developer>
</developers>
<scm>
<connection>scm:git:git://git.mattrixwv.com/HomeLab/RaidBuilderAPI.git</connection>
<developerConnection>scm:git:ssh://git.mattrixwv.com/HomeLab/RaidBuilderAPI.git</developerConnection>
<url>https://git.mattrixwv.com/HomeLab/RaidBuilderAPI</url>
</scm>
<properties>
<!--Compile-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<java.version>25</java.version>
<!--Sonarqube-->
<sonar.java.source>25</sonar.java.source>
<sonar.dependencyCheck.jsonReportPath>target/dependency-check-report.json</sonar.dependencyCheck.jsonReportPath>
<sonar.dependencyCheck.htmlReportPath>target/dependency-check-report.html</sonar.dependencyCheck.htmlReportPath>
<argLine></argLine>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.2</version>
</parent>
<dependencies>
<!--! Spring Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--! Database -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.9</version>
</dependency>
<dependency>
<groupId>io.hypersistence</groupId>
<artifactId>hypersistence-utils-hibernate-63</artifactId>
<version>3.15.1</version>
</dependency>
<!--! Jackson -->
<dependency>
<groupId>tools.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>3.0.0-rc2</version>
</dependency>
<dependency>
<groupId>tools.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate6</artifactId>
<version>3.0.4</version>
</dependency>
<!--! Boilerplate Generator -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.42</version>
<scope>provided</scope>
</dependency>
<!--! Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-layout-template-json</artifactId>
<version>2.25.3</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>4.0.0</version>
</dependency>
<!--! Email -->
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.21.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--Ensure maven is the correct version-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-maven</id>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<!--Sonarqube-->
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<executions>
<execution>
<phase>none</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--Ensure maven is the correct version-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<rules>
<requireMavenVersion>
<version>3.8.6</version>
</requireMavenVersion>
</rules>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.4.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<configuration>
<compilerArgs>
<arg>-Xlint:all</arg>
<arg>-proc:full</arg>
<arg>-Xlint:-serial</arg>
<arg>-Xlint:-processing</arg>
</compilerArgs>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
<configuration>
<skipTests>${skip.unit.tests}</skipTests>
<excludes>
<exclude>**/*IntegrationTest.java</exclude>
</excludes>
<classpathDependencyExcludes>
<classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j2-impl</classpathDependencyExclude>
</classpathDependencyExcludes>
<argLine>@{argLine} -Xshare:off -javaagent:${org.mockito:mockito-core:jar}</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.4</version>
<configuration>
<skipITs>${skip.integration.tests}</skipITs>
<includes>
<include>**/*IntegrationTest.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.21.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.21.0</version>
<configuration>
<rulesUri>file://${session.executionRootDirectory}/version-rules.xml</rulesUri>
</configuration>
</plugin>
<!--Sonarqube-->
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>5.5.0.6356</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.14</version>
<configuration>
<excludes>
<exclude>**/RaidBuilderAPI*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<phase>none</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>none</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>12.2.0</version>
<configuration>
<formats>
<format>json</format>
<format>html</format>
</formats>
<nvdApiServerId>nvd</nvdApiServerId>
<failBuildOnCVSS>7</failBuildOnCVSS>
<ossIndexServerId>ossindex</ossIndexServerId>
<suppressionFiles>
<suppressionFile>${project.basedir}/dependencySuppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -0,0 +1,17 @@
package com.mattrixwv.raidbuilder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import com.mattrixwv.raidbuilder.config.RsaKeyProperties;
@SpringBootApplication
@EnableConfigurationProperties(RsaKeyProperties.class)
public class RaidBuilderAPI{
public static void main(String[] args){
SpringApplication.run(RaidBuilderAPI.class, args);
}
}

View File

@@ -0,0 +1,16 @@
package com.mattrixwv.raidbuilder.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccountAuthorization{
public AccountPermissionType[] permissions();
}

View File

@@ -0,0 +1,16 @@
package com.mattrixwv.raidbuilder.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GameAuthorization{
public GamePermissionType[] permissions();
}

View File

@@ -0,0 +1,16 @@
package com.mattrixwv.raidbuilder.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RaidGroupAuthorization{
public RaidGroupPermissionType[] permissions();
}

View File

@@ -0,0 +1,94 @@
package com.mattrixwv.raidbuilder.aspect;
import java.util.List;
import org.aspectj.lang.JoinPoint;
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.Configuration;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.AccountPermission;
import com.mattrixwv.raidbuilder.exception.MissingAuthorizationException;
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Configuration
@RequiredArgsConstructor
public class AccountAuthorizationAspect{
private final AccountService accountService;
private final AccountPermissionService accountPermissionService;
@Pointcut("@annotation(com.mattrixwv.raidbuilder.annotation.AccountAuthorization)")
public void accountAuthorizationAnnotation(){
//Intentionally blank
}
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void mappedFunction(){
//Intentionally blank
}
@Before("accountAuthorizationAnnotation()")
public void authorizeAccount(JoinPoint joinPoint){
log.debug("Authorizing account");
//Get the annotation
AccountAuthorization accountAuthorization = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(AccountAuthorization.class);
log.debug("Required authorizations = {}", accountAuthorization);
//Return if there are no required permissions
if(accountAuthorization.permissions().length == 0){
log.debug("No required permissions");
return;
}
//Get the account
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = ((Jwt)auth.getPrincipal()).getClaimAsString("sub");
Account account = accountService.getByUsername(username);
if(account.getAccountStatus() != AccountStatus.ACTIVE){
throw new AuthorizationDeniedException("Account is not active", () -> false);
}
List<AccountPermission> accountPermissions = accountPermissionService.getByAccountId(account.getAccountId());
//Return if the account has a matching permissions
for(AccountPermission permission : accountPermissions){
for(AccountPermissionType permissionType : accountAuthorization.permissions()){
if(permission.getAccountPermissionType() == permissionType){
log.debug("User is authorized");
return;
}
}
}
log.debug("User is not authorized");
//If the user doesn't have a matching permission, throw an authorization exception
throw new AuthorizationDeniedException("User is not authorized to perform this action", () -> false);
}
@Before("mappedFunction() && !accountAuthorizationAnnotation()")
public void missingAuthorization(){
throw new MissingAuthorizationException();
}
}

View File

@@ -0,0 +1,96 @@
package com.mattrixwv.raidbuilder.aspect;
import java.util.List;
import org.aspectj.lang.JoinPoint;
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.Configuration;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import com.mattrixwv.raidbuilder.annotation.GameAuthorization;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.AccountPermission;
import com.mattrixwv.raidbuilder.entity.GamePermission;
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.service.GamePermissionService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Configuration
@RequiredArgsConstructor
public class GameAuthorizationAspect{
private final AccountService accountService;
private final AccountPermissionService accountPermissionService;
private final GamePermissionService gamePermissionService;
@Pointcut("@annotation(com.mattrixwv.raidbuilder.annotation.GameAuthorizationAspect)")
public void gameAuthorizationAnnotation(){
//Intentionally blank
}
@Before("gameAuthorizationAnnotation()")
public void authorizeGame(JoinPoint joinPoint){
log.debug("Authorizing game");
//Get the annotation
GameAuthorization gameAuthorization = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(GameAuthorization.class);
//Return if there are no required permissions
if(gameAuthorization.permissions().length == 0){
log.debug("No required permissions");
return;
}
//Get the account
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = ((Jwt)auth.getPrincipal()).getClaimAsString("sub");
Account account = accountService.getByUsername(username);
if(account.getAccountStatus() != AccountStatus.ACTIVE){
throw new AuthorizationDeniedException("Account is not active", () -> false);
}
//Return if the user is a site admin
List<AccountPermission> accountPermissions = accountPermissionService.getByAccountId(account.getAccountId());
for(AccountPermission permission : accountPermissions){
if(permission.getAccountPermissionType() == AccountPermissionType.ADMIN){
log.debug("User is a site admin");
return;
}
}
//TODO: Make sure this matches the game
//Return if the account has a matching permission
List<GamePermission> gamePermissions = gamePermissionService.getByAccountId(account.getAccountId());
for(GamePermission permission : gamePermissions){
for(GamePermissionType permissionType : gameAuthorization.permissions()){
if(permission.getGamePermissionType() == permissionType){
log.debug("User is authorized");
return;
}
}
}
log.debug("User is not authorized");
//If the user doesn't have a matching permission, throw an authorization exception
throw new AuthorizationDeniedException("User is not authorized to perform this action", () -> false);
}
}

View File

@@ -0,0 +1,106 @@
package com.mattrixwv.raidbuilder.aspect;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.UUID;
import org.aspectj.lang.JoinPoint;
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.Configuration;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.AccountPermission;
import com.mattrixwv.raidbuilder.entity.RaidGroupPermission;
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.service.RaidGroupPermissionService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Configuration
@RequiredArgsConstructor
public class RaidGroupAuthorizationAspect{
private final AccountService accountService;
private final AccountPermissionService accountPermissionService;
private final RaidGroupPermissionService raidGroupPermissionService;
@Pointcut("@annotation(com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization)")
public void raidGroupAuthorization(){
//Intentionally blank
}
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void mappedFunction(){
//Intentionally blank
}
@Before("raidGroupAuthorization() && mappedFunction()")
public void authorizeRaidGroup(JoinPoint joinPoint){
log.debug("Authorizing Raid Group");
//Get the annotation
RaidGroupAuthorization raidGroupAuthorization = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(RaidGroupAuthorization.class);
log.debug("Required authorizations = {}", raidGroupAuthorization);
//Return if there are no required permissions
if(raidGroupAuthorization.permissions().length == 0){
log.debug("No required permissions");
return;
}
//Get the account
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = ((Jwt)auth.getPrincipal()).getClaimAsString("sub");
Account account = accountService.getByUsername(username);
//Return if the user is a site admin
List<AccountPermission> accountPermissions = accountPermissionService.getByAccountId(account.getAccountId());
for(AccountPermission permission : accountPermissions){
if(permission.getAccountPermissionType() == AccountPermissionType.ADMIN){
log.debug("User is a site admin");
return;
}
}
UUID raidGroupId = null;
Parameter[] params = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameters();
for(int cnt = 0;cnt < params.length;++cnt){
if(params[cnt].getName().equals("raidGroupId")){
raidGroupId = (UUID)joinPoint.getArgs()[cnt];
break;
}
}
//Return if the account has a matching permission
List<RaidGroupPermission> raidGroupPermissions = raidGroupPermissionService.getByAccountId(account.getAccountId());
log.debug("account permissions {}", raidGroupPermissions.stream().map(rgp -> rgp.getPermission()));
for(RaidGroupPermission permission : raidGroupPermissions){
for(RaidGroupPermissionType permissionType : raidGroupAuthorization.permissions()){
if((permission.getRaidGroupId().equals(raidGroupId)) && (permission.getPermission() == permissionType)){
log.debug("User is authorized");
return;
}
}
}
//If the user doesn't have a matching permission throw an authorization exception
throw new AuthorizationDeniedException("User is not a qualified member of the raid group", () -> false);
}
}

View File

@@ -0,0 +1,12 @@
package com.mattrixwv.raidbuilder.config;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "rsa")
public record RsaKeyProperties(RSAPublicKey publicKey, RSAPrivateKey privateKey){
}

View File

@@ -0,0 +1,74 @@
package com.mattrixwv.raidbuilder.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.web.SecurityFilterChain;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig{
private final RsaKeyProperties rsaKeys;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> {
auth.requestMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers("/icons/**").permitAll()
.requestMatchers("/auth/refresh", "/auth/test").permitAll() //Permit refresh tokens
.requestMatchers(HttpMethod.POST, "/auth/signup", "/auth/confirm/*").permitAll() //Permit signup operations
.requestMatchers(HttpMethod.POST, "/auth/forgot", "/auth/forgot/*").permitAll() //Permit forgot password operations
.anyRequest().authenticated();
})
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.httpBasic(Customizer.withDefaults())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
public JwtEncoder jwtEncoder(){
JWK jwk = new RSAKey.Builder(rsaKeys.publicKey()).privateKey(rsaKeys.privateKey()).build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
@Bean
public JwtDecoder jwtDecoder(){
return NimbusJwtDecoder.withPublicKey(rsaKeys.publicKey()).build();
}
}

View File

@@ -0,0 +1,74 @@
package com.mattrixwv.raidbuilder.config;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.AccountPermission;
import com.mattrixwv.raidbuilder.entity.GamePermission;
import com.mattrixwv.raidbuilder.entity.RaidGroupPermission;
import com.mattrixwv.raidbuilder.entity.RaidGroupRequest;
import com.mattrixwv.raidbuilder.service.AccountPermissionService;
import com.mattrixwv.raidbuilder.service.GamePermissionService;
import com.mattrixwv.raidbuilder.service.RaidGroupPermissionService;
import com.mattrixwv.raidbuilder.service.RaidGroupRequestService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
@Slf4j
@Configuration
@RequiredArgsConstructor
public class TokenService{
private final ObjectMapper mapper;
private final JwtEncoder encoder;
private final AccountPermissionService accountPermissionService;
private final GamePermissionService gamePermissionService;
private final RaidGroupPermissionService raidGroupPermissionService;
private final RaidGroupRequestService raidGroupRequestService;
//Fields
@Value("${jwt.accessTokenDuration}")
private Duration accessTokenDuration;
public String generateAccessToken(Account account){
log.debug("Generating access token for account {}", account.getAccountId());
List<AccountPermission> accountPermissions = accountPermissionService.getByAccountId(account.getAccountId());
String scope = accountPermissions.stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(" "));
List<GamePermission> gamePermissions = gamePermissionService.getByAccountId(account.getAccountId());
List<RaidGroupPermission> raidGroupPermissions = raidGroupPermissionService.getByAccountId(account.getAccountId());
List<RaidGroupRequest> raidGroupRequests = raidGroupRequestService.getByAccountId(account.getAccountId());
Instant now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("self")
.issuedAt(now)
.expiresAt(now.plus(accessTokenDuration))
.subject(account.getUsername())
.claim("scope", scope)
.claim("accountId", account.getAccountId().toString())
.claim("accountPermissions", mapper.valueToTree(accountPermissions).toString())
.claim("raidGroupPermissions", mapper.valueToTree(raidGroupPermissions).toString())
.claim("gamePermissions", mapper.valueToTree(gamePermissions).toString())
.claim("raidGroupRequests", mapper.valueToTree(raidGroupRequests).toString())
//Game Permissions
//Raid Group Permissions
.build();
return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
}

View File

@@ -0,0 +1,56 @@
package com.mattrixwv.raidbuilder.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.datatype.hibernate6.Hibernate6Module;
import tools.jackson.datatype.hibernate6.Hibernate6Module.Feature;
import tools.jackson.datatype.jsr310.JavaTimeModule;
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Value("${allowedOrigins}")
private String allowedOrigins;
@Override
public void addCorsMappings(CorsRegistry registry){
log.debug("Adding CORS mappings: {}", allowedOrigins);
registry.addMapping("/**")
.allowedOriginPatterns(allowedOrigins)
.allowedMethods("GET", "PUT", "DELETE", "OPTIONS", "POST")
.allowCredentials(true);
}
@Bean
public ObjectMapper objectMapper(){
log.debug("Starting mapping configuration");
//Make sure Jackson doesn't attempt lazy loading
Hibernate6Module hibernate6Module = new Hibernate6Module();
hibernate6Module.configure(Feature.FORCE_LAZY_LOADING, false);
hibernate6Module.configure(Feature.USE_TRANSIENT_ANNOTATION, false);
hibernate6Module.configure(Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER, true);
hibernate6Module.configure(Feature.WRITE_MISSING_ENTITIES_AS_NULL, false);
ObjectMapper mapper = JsonMapper.builder()
.addModule(hibernate6Module)
.addModule(new JavaTimeModule())
.findAndAddModules()
.build();
log.debug("Completed mapping configuration");
return mapper;
}
}

View File

@@ -0,0 +1,78 @@
package com.mattrixwv.raidbuilder.config;
import java.io.IOException;
import java.util.StringJoiner;
import java.util.UUID;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class WebFilter extends OncePerRequestFilter{
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException{
if(!request.getMethod().equalsIgnoreCase("OPTIONS")){
setupMDC(request);
}
//Continue to the controller
filterChain.doFilter(request, response);
//Clear the MDC for the next request
MDC.clear();
}
private void setupMDC(HttpServletRequest request){
//Get the requestId
String requestId = request.getHeader("X-Request-Id");
if(requestId != null){
MDC.put("requestId", requestId);
}
else{
requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
}
//Get IP address
String forwardedFor = request.getHeader("X-Forwarded-For");
if(forwardedFor != null){
MDC.put("ip", forwardedFor.split(",")[0]);
}
//Get all of the parameters in the request and print them in the log
StringJoiner parameters = new StringJoiner(", ");
request.getParameterMap().entrySet().forEach(entry -> {
if(!entry.getKey().equals("_")){
String key = entry.getKey();
String value = "";
if(entry.getValue().length > 1){
StringJoiner joiner = new StringJoiner(", ", "[", "]");
for(String str : entry.getValue()){
joiner.add(str);
}
value = joiner.toString();
}
else{
value = entry.getValue()[0];
}
parameters.add(key + "->" + value);
}
});
if(parameters.length() > 0){
log.info("Request parameters: {}", parameters);
}
//Get the path
MDC.put("url", request.getRequestURI());
}
}

View File

@@ -0,0 +1,431 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.RaidGroupPermission;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.service.RaidGroupPermissionService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/account")
@RequiredArgsConstructor
public class AccountController{
private final ObjectMapper mapper;
private final AccountService accountService;
private final RaidGroupPermissionService rgpService;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public List<Account> getAccounts(@RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting accounts page {} of size {} with search term {}", page, pageSize, searchTerm);
List<Account> accounts;
if((searchTerm == null) || (searchTerm.isEmpty())){
accounts = accountService.getAccounts(page, pageSize);
}
else{
accounts = accountService.getAccounts(page, pageSize, searchTerm);
}
return accounts;
}
@GetMapping("/raidGroup/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public List<Account> getAccountsByRaidGroup(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting accounts by raid group {}", raidGroupId);
List<Account> accounts;
if(searchTerm == null){
accounts = accountService.getAccountsByRaidGroupId(raidGroupId, page, pageSize);
}
else{
accounts = accountService.getAccountsByRaidGroupId(raidGroupId, page, pageSize, searchTerm);
}
return accounts;
}
@GetMapping("/{accountId}/raidGroup/{raidGroupId}/permission")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode getAccountPermission(@PathVariable("accountId") UUID accountId, @PathVariable("raidGroupId") UUID raidGroupId){
log.info("Getting account permission for account {} and raid group {}", accountId, raidGroupId);
RaidGroupPermission permission = rgpService.getByAccountIdAndRaidGroupId(accountId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
returnNode.put("permission", permission.getPermission().name());
return returnNode;
}
@PutMapping("/{accountId}/raidGroup/{raidGroupId}/permission")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode updateAccountPermission(@PathVariable("accountId") UUID accountId, @PathVariable("raidGroupId") UUID raidGroupId, @RequestBody ObjectNode permissionNode){
log.info("Updating account permission for account {} and raid group {}", accountId, raidGroupId);
RaidGroupPermission existingPermission = rgpService.getByAccountIdAndRaidGroupId(accountId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
if(existingPermission == null){
returnNode.put("status", "error");
ArrayNode arrayNode = mapper.createArrayNode();
arrayNode.add("Account does not have permission to raid group");
returnNode.set("errors", arrayNode);
}
else{
RaidGroupPermissionType permission = RaidGroupPermissionType.valueOf(permissionNode.get("permission").asString());
existingPermission.setPermission(permission);
rgpService.createRaidGroupPermission(existingPermission);
returnNode.put("status", "success");
}
return returnNode;
}
@DeleteMapping("/{accountId}/raidGroup/{raidGroupId}/permission")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode deleteAccountPermission(@PathVariable("accountId") UUID accountId, @PathVariable("raidGroupId") UUID raidGroupId){
log.info("Deleting account permission for account {} and raid group {}", accountId, raidGroupId);
rgpService.deleteByAccountIdAndRaidGroupId(accountId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
return returnNode;
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode createAccount(@RequestBody Account account){
log.info("Creating account {}", account.getUsername());
ObjectNode returnNode = mapper.createObjectNode();
//TODO: Move this to util
List<String> errors = verifyNewAccount(account);
if(errors.isEmpty()){
account = accountService.createAccount(account);
returnNode.put("status", "success");
returnNode.put("accountId", account.getAccountId().toString());
log.info("Created account {}", account.getAccountId());
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Username already exists");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
return returnNode;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode getAccountsCount(@RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting accounts count");
Long accountsCount;
if((searchTerm == null) || (searchTerm.isBlank())){
accountsCount = accountService.getAccountsCount();
}
else{
accountsCount = accountService.getAccountsCount(searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", accountsCount);
countNode.put("status", "success");
return countNode;
}
@GetMapping("/raidGroup/{raidGroupId}/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode getAccountsCountByRaidGroup(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting accounts count by raid group {}", raidGroupId);
Long accountsCount;
if(searchTerm == null){
accountsCount = accountService.getAccountsByRaidGroupIdCount(raidGroupId);
}
else{
accountsCount = accountService.getAccountsByRaidGroupIdCount(raidGroupId, searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", accountsCount);
countNode.put("status", "success");
return countNode;
}
@PutMapping("/{accountId}/forcePasswordReset")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode forcePasswordReset(@PathVariable("accountId") UUID accountId){
log.info("Forcing password reset for account {}", accountId);
Account account = accountService.getByAccountId(accountId);
ObjectNode returnNode = mapper.createObjectNode();
if(account == null){
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Account not found");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
else{
account.setRefreshToken(null);
account.setRefreshTokenExpiration(null);
account.setForceReset(true);
accountService.updateAccount(account);
returnNode.put("status", "success");
}
return returnNode;
}
@PutMapping("/{accountId}/resetPassword")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode resetPassword(@PathVariable("accountId") UUID accountId, @RequestBody ObjectNode passwordNode){
log.info("Resetting password for account {}", accountId);
Account account = accountService.getByAccountId(accountId);
ObjectNode returnNode = mapper.createObjectNode();
if(account == null){
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Account not found");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
else{
account.setRefreshToken(null);
account.setRefreshTokenExpiration(null);
account.setForceReset(false);
accountService.updateAccount(account);
accountService.updatePassword(accountId, passwordNode.get("password").asString());
returnNode.put("status", "success");
}
return returnNode;
}
@PutMapping("/{accountId}/revokeRefreshToken")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode revokeRefreshToken(@PathVariable("accountId") UUID accountId){
log.info("Revoking refresh token for account {}", accountId);
Account account = accountService.getByAccountId(accountId);
ObjectNode returnNode = mapper.createObjectNode();
if(account == null){
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Account not found");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
else{
account.setRefreshToken(null);
account.setRefreshTokenExpiration(null);
accountService.updateAccount(account);
returnNode.put("status", "success");
}
return returnNode;
}
@PutMapping("/{accountId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode updateAccount(@PathVariable("accountId") UUID accountId, @RequestBody Account account){
log.info("Updating account {}", accountId);
//TODO: Move this to util
List<String> errors = verifyUpdatedAccount(account);
ObjectNode returnNode = mapper.createObjectNode();
if(errors.isEmpty()){
Account existingAccount = accountService.getByAccountId(accountId);
existingAccount.setUsername(account.getUsername());
existingAccount.setEmail(account.getEmail());
existingAccount.setAccountStatus(account.getAccountStatus());
accountService.updateAccount(existingAccount);
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Account not found");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
return returnNode;
}
@DeleteMapping("/{accountId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode deleteAccount(@PathVariable("accountId") UUID accountId){
log.info("Deleting account {}", accountId);
Account account = accountService.getByAccountId(accountId);
ObjectNode returnNode = mapper.createObjectNode();
if(account == null){
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Account not found");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
else{
accountService.deleteAccount(accountId);
returnNode.put("status", "success");
}
return returnNode;
}
private List<String> verifyNewAccount(Account account){
ArrayList<String> errors = new ArrayList<>();
//Check ID
if(account.getAccountId() != null){
errors.add("Invalid Account ID");
}
//Check login exists in entity
if((account.getUsername() == null) || (account.getUsername().isEmpty())){
errors.add("Invalid Login ID");
}
else{
//Check login doesn't exist in db
Account existingAccount = accountService.getByUsername(account.getUsername());
if(existingAccount != null){
errors.add("Login ID already exists");
}
}
//Check password exists in entity
if((account.getPassword() == null) || (account.getPassword().isEmpty())){
errors.add("No password found");
}
//Check email exists in entity
if((account.getEmail() == null) || (account.getEmail().isEmpty())){
errors.add("Invalid email");
}
else{
//Check email doesn't exist in db
Account existingAccount = accountService.getByEmail(account.getEmail());
if(existingAccount != null){
errors.add("Account with email already exists");
}
}
return errors;
}
private List<String> verifyUpdatedAccount(Account account){
ArrayList<String> errors = new ArrayList<>();
//Check ID
if(account.getAccountId() == null){
errors.add("Invalid Account ID");
}
//Verify account exists
Account existingAccount = accountService.getByAccountId(account.getAccountId());
if(existingAccount == null){
errors.add("Account not found");
}
//Check login exists in entity
if((account.getUsername() == null) || (account.getUsername().isEmpty())){
errors.add("Invalid Login ID");
}
else{
//Check login doesn't exist in db, other than the object itself
Account existingAccountByLogin = accountService.getByUsername(account.getUsername());
if((existingAccountByLogin != null) && (!existingAccountByLogin.getAccountId().equals(account.getAccountId()))){
errors.add("Login ID already exists");
}
}
//Check email exists in entity
if((account.getEmail() == null) || (account.getEmail().isEmpty())){
errors.add("Invalid email");
}
else{
//Check email doesn't exist in db, other than the object itself
Account existingAccountByEmail = accountService.getByEmail(account.getEmail());
if((existingAccountByEmail != null) && (!existingAccountByEmail.getAccountId().equals(account.getAccountId()))){
errors.add("Account with email already exists");
}
}
return errors;
}
}

View File

@@ -0,0 +1,63 @@
package com.mattrixwv.raidbuilder.controller;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.AccountTutorialStatus;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.service.AccountTutorialStatusService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/account/tutorial")
@RequiredArgsConstructor
public class AccountTutorialController{
private final ObjectMapper mapper;
private final AccountService accountService;
private final AccountTutorialStatusService tutorialService;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public AccountTutorialStatus getTutorialStatus(Authentication authentication){
log.info("Getting tutorial status for account {}", authentication.getName());
Account account = accountService.getByUsername(authentication.getName());
return tutorialService.getByAccountId(account.getAccountId());
}
//TODO: Break this up per tutorial?
@PutMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode updateTutorialStatus(@RequestBody AccountTutorialStatus tutorialStatus, Authentication authentication){
log.info("Updating tutorial status for account {}", authentication.getName());
Account account = accountService.getByUsername(authentication.getName());
tutorialStatus.setAccountId(account.getAccountId());
tutorialService.updateAccountTutorialStatus(tutorialStatus);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
return returnNode;
}
}

View File

@@ -0,0 +1,352 @@
package com.mattrixwv.raidbuilder.controller;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.config.TokenService;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
import com.mattrixwv.raidbuilder.util.EmailUtil;
import jakarta.mail.internet.InternetAddress;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthenticationController{
private final ObjectMapper mapper;
private final PasswordEncoder passwordEncoder;
private final TokenService tokenService;
private final AccountService accountService;
//Email
private final EmailUtil emailUtil;
@GetMapping("/token")
@AccountAuthorization(permissions = {})
public ObjectNode token(Authentication authentication, HttpServletResponse response){
log.info("Token requested for user {}", authentication.getName());
Account account = accountService.getByUsername(authentication.getName());
String token = tokenService.generateAccessToken(account);
log.debug("Token granted {}", token);
ObjectNode tokenNode = mapper.valueToTree(account);
tokenNode.put("token", token);
Cookie cookie = new Cookie("refreshToken", account.getRefreshToken().toString());
cookie.setHttpOnly(true);
cookie.setPath("/");
response.addCookie(cookie);
return tokenNode;
}
@GetMapping("/refresh")
@AccountAuthorization(permissions = {})
public ObjectNode refresh(HttpServletRequest request) throws InterruptedException{
log.info("Refreshing token");
UUID refreshToken = null;
if(request.getCookies() != null){
for(Cookie cookie : request.getCookies()){
if(cookie.getName().equals("refreshToken")){
refreshToken = UUID.fromString(cookie.getValue());
log.debug("refreshToken = {}", refreshToken);
}
}
}
if(refreshToken == null){
throw new AuthorizationDeniedException("Refresh token is missing", () -> false);
}
Account account = accountService.getByRefreshToken(refreshToken);
if(account == null){
throw new AuthorizationDeniedException("Refresh token is invalid", () -> false);
}
//Update login date
account.setLoginDate(ZonedDateTime.now());
account = accountService.updateAccount(account);
String token = tokenService.generateAccessToken(account);
log.debug("new token: {}", token);
ObjectNode tokenNode = mapper.valueToTree(account);
tokenNode.put("token", token);
return tokenNode;
}
@PostMapping("/signup")
@AccountAuthorization(permissions = {})
public ObjectNode signup(@RequestBody Account account){
log.info("Creating account {}", account.getUsername());
ObjectNode returnNode = mapper.createObjectNode();
//Verify account object
List<String> errors = verifyNewAccount(account);
if(errors.isEmpty()){
//Create the account
account = accountService.createAccount(account);
returnNode.put("accountId", account.getAccountId().toString());
returnNode.put("status", "success");
log.info("Successfully created account: {}", account.getAccountId());
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errors.forEach(errorsNode::add);
returnNode.set("errors", errorsNode);
log.info("Error creating account: {}", errors);
}
return returnNode;
}
@PostMapping("/confirm/{confirmToken}")
@AccountAuthorization(permissions = {})
public ObjectNode confirm(@PathVariable("confirmToken") UUID confirmToken){
log.info("Confirming account with token {}", confirmToken);
ObjectNode returnNode = mapper.createObjectNode();
//Verify token
Account account = accountService.getByRefreshToken(confirmToken);
log.debug("Found account: {}", account);
if((account != null) && (account.getAccountStatus() == AccountStatus.UNCONFIRMED) && (account.getRefreshTokenExpiration().isAfter(ZonedDateTime.now()))){
accountService.confirmAccount(account);
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Account is not unconfirmed");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
@PostMapping("/forgot")
@AccountAuthorization(permissions = {})
public ObjectNode forgot(@RequestParam("username") String username){
log.info("Setting up user that forgot their password: {}", username);
ObjectNode returnNode = mapper.createObjectNode();
//Verify the account exists
Account account = accountService.getByUsername(username);
if(account != null){
//Setup token
UUID token = UUID.randomUUID();
account.setRefreshToken(token);
account.setRefreshTokenExpiration(ZonedDateTime.now().plusHours(1));
account.setAccountStatus(AccountStatus.LOCKED);
account = accountService.updateAccount(account);
try{
emailUtil.sendPasswordResetEmail(new InternetAddress(account.getEmail()), token);
}
catch(Exception error){
log.error("Error sending email", error);
throw new RuntimeException("Error sending reset email", error);
}
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Could not find account with login " + username);
returnNode.set("errors", errorsNode);
}
return returnNode;
}
@PostMapping("/forgot/{forgotToken}")
@AccountAuthorization(permissions = {})
public ObjectNode setNewPasswordForgot(@PathVariable("forgotToken") UUID forgotToken, @RequestBody ObjectNode passwordNode){
log.info("Confirming user reset password (forget) with token {}", forgotToken);
ObjectNode returnNode = mapper.createObjectNode();
//Verify the account exists
Account existingAccount = accountService.getByRefreshToken(forgotToken);
if(existingAccount != null){
String newPassword = passwordNode.get("password").asString();
if(newPassword.trim().length() <= 0){
throw new IllegalArgumentException("Invalid password");
}
existingAccount = accountService.updatePassword(existingAccount.getAccountId(), newPassword);
existingAccount.setRefreshToken(null);
existingAccount.setRefreshTokenExpiration(null);
existingAccount.setAccountStatus(AccountStatus.ACTIVE);
existingAccount = accountService.updateAccount(existingAccount);
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Invalid token");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
@PostMapping("/resetPassword")
@AccountAuthorization(permissions = {})
public ObjectNode resetPassword(Authentication authentication, @RequestBody ObjectNode requestNode){
log.info("Resetting password for {}", authentication.getName());
if((requestNode == null) || (!requestNode.has("newPassword"))){
throw new IllegalArgumentException("Invalid request");
}
String currentPassword = requestNode.get("currentPassword").asString();
String newPassword = requestNode.get("newPassword").asString();
if(newPassword.trim().length() <= 0){
throw new IllegalArgumentException("Invalid password");
}
Account account = accountService.getByUsername(authentication.getName());
if(!passwordEncoder.matches(currentPassword, account.getPassword())){
throw new IllegalArgumentException("Current password did not match");
}
account.setForceReset(false);
accountService.updatePassword(account.getAccountId(), newPassword);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
return returnNode;
}
@GetMapping("/logout")
@AccountAuthorization(permissions = {})
public ObjectNode logout(Authentication authentication, HttpServletResponse response){
log.info("Logging out account {}", authentication.getName());
Account account = accountService.getByUsername(authentication.getName());
if(account != null){
account.setRefreshToken(null);
account.setRefreshTokenExpiration(null);
account = accountService.updateAccount(account);
}
Cookie cookie = new Cookie("refreshToken", null);
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
return returnNode;
}
@PutMapping("/{accountId}/revokeRefreshToken")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode revokeRefreshToken(@PathVariable("accountId") UUID accountId){
log.info("Revoking refresh token for account {}", accountId);
Account account = accountService.getByAccountId(accountId);
account.setRefreshToken(null);
account.setRefreshTokenExpiration(null);
account = accountService.updateAccount(account);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
returnNode.put("accountId", account.getAccountId().toString());
return returnNode;
}
private List<String> verifyNewAccount(Account account){
ArrayList<String> errors = new ArrayList<>();
//Check ID
if(account.getAccountId() != null){
errors.add("Invalid Account ID");
}
//Check login exists in entity
if((account.getUsername() == null) || (account.getUsername().isEmpty())){
errors.add("Invalid Login ID");
}
else{
//Check login doesn't exist in db
Account existingAccount = accountService.getByUsername(account.getUsername());
if(existingAccount != null){
errors.add("Login ID already exists");
}
}
//Check password exists in entity
if((account.getPassword() == null) || (account.getPassword().isEmpty())){
errors.add("No password found");
}
//Check email exists in entity
if((account.getEmail() == null) || (account.getEmail().isEmpty())){
errors.add("Invalid email");
}
else{
//Check email doesn't exist in db
Account existingAccount = accountService.getByEmail(account.getEmail());
if(existingAccount != null){
errors.add("Account with email already exists");
}
}
return errors;
}
}

View File

@@ -0,0 +1,240 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.GameAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.GameCalendarEvent;
import com.mattrixwv.raidbuilder.entity.RaidGroupCalendarEvent;
import com.mattrixwv.raidbuilder.entity.RaidInstanceCalendarEvent;
import com.mattrixwv.raidbuilder.service.GameCalendarEventService;
import com.mattrixwv.raidbuilder.service.RaidGroupCalendarEventService;
import com.mattrixwv.raidbuilder.service.RaidInstanceService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.CalendarEventValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/calendar")
@RequiredArgsConstructor
public class CalendarController{
private final ObjectMapper mapper;
private final GameCalendarEventService gceService;
private final RaidGroupCalendarEventService rgceService;
private final RaidInstanceService raidInstanceService;
//Utilities
private final CalendarEventValidationUtil cevUtil;
//! Game
@GetMapping("/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public List<GameCalendarEvent> getGameCalendarEvents(@PathVariable("gameId") UUID gameId){
log.info("Getting calendar events for game {}", gameId);
return gceService.getByGameId(gameId);
}
@PostMapping("/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode createGameCalendarEvent(@PathVariable("gameId") UUID gameId, @RequestBody GameCalendarEvent gameCalendarEvent){
log.info("Creating calendar event for game {}", gameId);
gameCalendarEvent.setGameId(gameId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = cevUtil.validateExistingGameCalendarEvent(gameCalendarEvent);
if(errors.isEmpty()){
gameCalendarEvent = gceService.createGameCalendarEvent(gameCalendarEvent);
returnNode.put("status", "success");
returnNode.put("gameCalendarEventId", gameCalendarEvent.getGameCalendarEventId().toString());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@PutMapping("/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode updateGameCalendarEvent(@PathVariable("gameId") UUID gameId, @RequestBody GameCalendarEvent gameCalendarEvent){
log.info("Updating calendar event for game {}", gameId);
gameCalendarEvent.setGameId(gameId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = cevUtil.validateExistingGameCalendarEvent(gameCalendarEvent);
if(errors.isEmpty()){
gameCalendarEvent = gceService.updateGameCalendarEvent(gameCalendarEvent);
returnNode.put("status", "success");
returnNode.put("gameCalendarEventId", gameCalendarEvent.getGameCalendarEventId().toString());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@DeleteMapping("/game/{gameId}/{calendarEventId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode deleteGameCalendarEvent(@PathVariable("gameId") UUID gameId, @PathVariable("calendarEventId") UUID calendarEventId){
log.info("Deleting calendar event for game {}", gameId);
ObjectNode returnNode = mapper.createObjectNode();
GameCalendarEvent gce = gceService.getById(calendarEventId);
if(gce != null){
gceService.deleteGameCalendarEvent(calendarEventId);
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Calendar event ID is invalid");
returnNode.put("status", "error");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
//! Raid Group
@GetMapping("/raidGroup/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<RaidGroupCalendarEvent> getGameCalendarEventsByRaidGroup(@PathVariable("raidGroupId") UUID raidGroupId){
log.info("Getting calendar events for raid group {}", raidGroupId);
return rgceService.getByRaidGroupId(raidGroupId);
}
@PostMapping("/raidGroup/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createRaidGroupCalendarEvent(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody RaidGroupCalendarEvent raidGroupCalendarEvent){
log.info("Creating calendar event for raid group {}", raidGroupId);
raidGroupCalendarEvent.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = cevUtil.validateNewRaidGroupCalendarEvent(raidGroupCalendarEvent);
if(errors.isEmpty()){
rgceService.createRaidGroupCalendarEvent(raidGroupCalendarEvent);
returnNode.put("status", "success");
returnNode.put("raidGroupCalendarEventId", raidGroupCalendarEvent.getRaidGroupCalendarEventId().toString());
log.info("Created Raid Group Calendar Event {}", raidGroupCalendarEvent.getRaidGroupCalendarEventId());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@PutMapping("/raidGroup/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode updateRaidGroupCalendarEvent(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody RaidGroupCalendarEvent raidGroupCalendarEvent){
log.info("Updating calendar event for raid group {}", raidGroupId);
raidGroupCalendarEvent.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = cevUtil.validateExistingRaidGroupCalendarEvent(raidGroupCalendarEvent);
if(errors.isEmpty()){
rgceService.updateRaidGroupCalendarEvent(raidGroupCalendarEvent);
returnNode.put("status", "success");
returnNode.put("raidGroupCalendarEventId", raidGroupCalendarEvent.getRaidGroupCalendarEventId().toString());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@DeleteMapping("/raidGroup/{raidGroupId}/{calendarEventId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode deleteRaidGroupCalendarEvent(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("calendarEventId") UUID calendarEventId){
log.info("Deleting calendar event for raid group {}", raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
GameCalendarEvent gce = gceService.getById(calendarEventId);
if(gce != null){
gceService.deleteGameCalendarEvent(calendarEventId);
returnNode.put("status", "success");
returnNode.put("raidGroupCalendarEventId", gce.getGameCalendarEventId().toString());
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Calendar event ID is invalid");
returnNode.put("status", "error");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
//! Raid Instance
@GetMapping("/raidGroup/{raidGroupId}/raidInstance")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<RaidInstanceCalendarEvent> getGameCalendarEventsByRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId){
log.info("Getting calendar events for raid group {}", raidGroupId);
return raidInstanceService.findAllByRaidGroupId(raidGroupId).stream().map(instance -> new RaidInstanceCalendarEvent(instance)).toList();
}
}

View File

@@ -0,0 +1,205 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.ClassGroup;
import com.mattrixwv.raidbuilder.entity.ClassGroupGameClassXref;
import com.mattrixwv.raidbuilder.service.ClassGroupService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.ClassGroupValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/classGroup")
@RequiredArgsConstructor
public class ClassGroupController{
private final ObjectMapper mapper;
private final ClassGroupService classGroupService;
//Utilities
private final ClassGroupValidationUtil cgvUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<ClassGroup> getClassGroupByRaidGroupId(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting class groups for raid group {} page {} of size {} with search {}", raidGroupId, page, pageSize, searchTerm);
List<ClassGroup> classGroups;
if(searchTerm == null){
classGroups = classGroupService.getClassGroupsByRaidGroupId(raidGroupId, page, pageSize);
}
else{
classGroups = classGroupService.getClassGroupsByRaidGroupIdAndSearchTerm(raidGroupId, page, pageSize, searchTerm);
}
return classGroups;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public ObjectNode getClassGroupCountByRaidGroupId(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting class group count for raid group {} with search {}", raidGroupId, searchTerm);
Long count;
if(searchTerm == null){
count = classGroupService.getClassGroupCountByRaidGroupId(raidGroupId);
}
else{
count = classGroupService.getClassGroupCountByRaidGroupId(raidGroupId, searchTerm);
}
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("count", count);
returnNode.put("status", "success");
return returnNode;
}
@GetMapping("/{classGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public ClassGroup getClassGroup(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("classGroupId") UUID classGroupId){
log.info("Getting class group {} for raid group {}", classGroupId, raidGroupId);
return classGroupService.getClassGroup(classGroupId, raidGroupId);
}
@GetMapping("/raidLayout/{raidLayoutId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<ClassGroup> getClassGroupsByRaidLayoutId(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidLayoutId") UUID raidLayoutId){
log.info("Getting class groups for raid layout {} for raid group {}", raidLayoutId, raidGroupId);
return classGroupService.getClassGroupsByRaidLayoutId(raidLayoutId);
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createClassGroup(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody ObjectNode bodyNode){
log.info("Creating class group for raid group {}", raidGroupId);
ClassGroup classGroup = mapper.treeToValue(bodyNode.get("classGroup"), ClassGroup.class);
UUID[] gameClassIds = mapper.treeToValue(bodyNode.get("gameClassIds"), UUID[].class);
List<ClassGroupGameClassXref> xrefs = new ArrayList<>(gameClassIds.length);
for(UUID gameClassId : gameClassIds){
ClassGroupGameClassXref xref = new ClassGroupGameClassXref();
xref.setGameClassId(gameClassId);
xrefs.add(xref);
}
List<String> errors = cgvUtil.validateNewClassGroup(classGroup);
ObjectNode returnNode = mapper.createObjectNode();
if(errors.isEmpty()){
classGroup = classGroupService.createClassGroup(classGroup, xrefs);
returnNode.put("status", "success");
returnNode.put("classGroupId", classGroup.getClassGroupId().toString());
log.info("Created Class Group {}", classGroup.getClassGroupId());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@PutMapping("/{classGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode updateClassGroup(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("classGroupId") UUID classGroupId, @RequestBody ObjectNode bodyNode){
log.info("Updating class group {} for raid group {}", classGroupId, raidGroupId);
ClassGroup classGroup = mapper.treeToValue(bodyNode.get("classGroup"), ClassGroup.class);
UUID[] gameClassIds = mapper.treeToValue(bodyNode.get("gameClassIds"), UUID[].class);
List<ClassGroupGameClassXref> xrefs = new ArrayList<>(gameClassIds.length);
for(UUID gameClassId : gameClassIds){
ClassGroupGameClassXref xref = new ClassGroupGameClassXref();
xref.setClassGroupId(classGroup.getClassGroupId());
xref.setGameClassId(gameClassId);
xrefs.add(xref);
}
List<String> errors = cgvUtil.validateExistingClassGroup(classGroup);
ObjectNode returnNode = mapper.createObjectNode();
if(errors.isEmpty()){
classGroupService.updateClassGroup(classGroup, xrefs);
returnNode.put("classGroupId", classGroup.getClassGroupId().toString());
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@DeleteMapping("/{classGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode deleteClassGroup(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("classGroupId") UUID classGroupId){
log.info("Deleting class group {} for raid group {}", classGroupId, raidGroupId);
ClassGroup classGroup = classGroupService.getClassGroup(classGroupId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
if(classGroup != null){
classGroupService.deleteClassGroup(classGroupId, raidGroupId);
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errorNode.add("Class group ID is invalid");
returnNode.set("errors", errorNode);
}
return returnNode;
}
}

View File

@@ -0,0 +1,75 @@
package com.mattrixwv.raidbuilder.controller;
import org.springframework.http.HttpStatus;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.mattrixwv.raidbuilder.exception.MissingAuthorizationException;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestControllerAdvice
public class ExceptionController{
private static final ObjectMapper mapper = new ObjectMapper();
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ObjectNode genericExceptionHandler(Exception error){
log.error(error.getMessage(), error);
ObjectNode returnJson = mapper.createObjectNode();
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add(error.getMessage());
returnJson.set("errors", errorsNode);
returnJson.put("status", "error");
return returnJson;
}
@ExceptionHandler(AuthorizationDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public ObjectNode authorizationExceptionHandler(AuthorizationDeniedException error){
log.info(error.getMessage());
ObjectNode returnJson = mapper.createObjectNode();
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add(error.getMessage());
returnJson.set("errors", errorsNode);
returnJson.put("status", "error");
return returnJson;
}
@ExceptionHandler(MissingAuthorizationException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public ObjectNode missingAuthorizationExceptionHandler(MissingAuthorizationException error){
log.info(error.getMessage());
ObjectNode returnJson = mapper.createObjectNode();
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Server is misconfigured");
returnJson.set("errors", errorsNode);
returnJson.put("status", "error");
return returnJson;
}
}

View File

@@ -0,0 +1,188 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.GameAuthorization;
import com.mattrixwv.raidbuilder.entity.GameClass;
import com.mattrixwv.raidbuilder.service.GameClassService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType;
import com.mattrixwv.raidbuilder.util.validation.GameClassValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/gameClass")
@RequiredArgsConstructor
public class GameClassController{
private final ObjectMapper mapper;
private final GameClassService gameClassService;
//Utilities
private final GameClassValidationUtil gcvUtil;
@GetMapping("/{gameClassId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public GameClass getGameClass(@PathVariable("gameClassId") UUID gameClassId){
log.info("Getting game class {}", gameClassId);
return gameClassService.getById(gameClassId);
}
@GetMapping("/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public List<GameClass> getByGameId(@PathVariable("gameId") UUID gameId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting game classes for game {} with page {} of size {} with search term {}", gameId, page, pageSize, searchTerm);
List<GameClass> gameClasses;
if((searchTerm == null) || (searchTerm.isBlank())){
gameClasses = gameClassService.getByGameId(gameId, page, pageSize);
}
else{
gameClasses = gameClassService.getByGameId(gameId, page, pageSize, searchTerm);
}
return gameClasses;
}
@GetMapping("/game/{gameId}/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode getByGameCount(@PathVariable("gameId") UUID gameId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting game classes count for game {} with search term {}", gameId, searchTerm);
Long gameClassesCount;
if((searchTerm == null) || (searchTerm.isBlank())){
gameClassesCount = gameClassService.countByGameId(gameId);
}
else{
gameClassesCount = gameClassService.countByGameId(gameId, searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", gameClassesCount);
countNode.put("status", "success");
return countNode;
}
@GetMapping("/classGroup/{classGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public List<GameClass> getByClassGroupId(@PathVariable("classGroupId") UUID classGroupId){
log.info("Getting game classes for class group {}", classGroupId);
return gameClassService.getByClassGroupId(classGroupId);
}
@PostMapping("/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode createGameClass(@PathVariable("gameId") UUID gameId, @RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("gameClassName") String gameClassName){
log.info("Creating game class {}", gameClassName);
ObjectNode returnNode = mapper.createObjectNode();
GameClass gameClass = new GameClass();
gameClass.setGameId(gameId);
gameClass.setGameClassName(gameClassName);
List<String> errors = gcvUtil.validateNewGameClass(gameClass);
if(errors.isEmpty()){
gameClassService.createGameClass(gameClass, file);
returnNode.put("status", "success");
returnNode.put("gameClassId", gameClass.getGameClassId().toString());
log.info("Successfully created game class: {}", gameClass.getGameClassId());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@PutMapping("/{gameClassId}/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode updateGameClass(@PathVariable("gameClassId") UUID gameClassId, @PathVariable("gameId") UUID gameId, @RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("gameClassName") String gameClassName, @RequestParam(value = "gameClassIcon", required = false) String gameClassIcon){
log.info("Updating game class {}", gameClassName);
ObjectNode returnNode = mapper.createObjectNode();
GameClass gameClass = new GameClass();
gameClass.setGameClassId(gameClassId);
gameClass.setGameId(gameId);
gameClass.setGameClassName(gameClassName);
gameClass.setGameClassIcon(gameClassIcon);
List<String> errors = gcvUtil.validateExistingGameClass(gameClass);
if(errors.isEmpty()){
gameClassService.updateGameClass(gameClass, file);
returnNode.put("gameClassId", gameClass.getGameClassId().toString());
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@DeleteMapping("/{gameClassId}/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode deleteGameClass(@PathVariable("gameClassId") UUID gameClassId, @PathVariable("gameId") UUID gameId){
log.info("Deleting game class {}", gameClassId);
GameClass gameClass = gameClassService.getById(gameClassId);
ObjectNode returnNode = mapper.createObjectNode();
if(gameClass != null){
gameClassService.deleteById(gameClassId);
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errorNode.add("Game class ID is invalid");
returnNode.set("errors", errorNode);
}
return returnNode;
}
}

View File

@@ -0,0 +1,177 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.GameAuthorization;
import com.mattrixwv.raidbuilder.entity.Game;
import com.mattrixwv.raidbuilder.service.GameService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType;
import com.mattrixwv.raidbuilder.util.validation.GameValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/game")
@RequiredArgsConstructor
public class GameController{
private final ObjectMapper mapper;
private final GameService gameService;
//Utilities
private final GameValidationUtil gvUtil;
@GetMapping("/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public Game getGame(@PathVariable("gameId") UUID gameId){
log.info("Getting game {}", gameId);
return gameService.getGameById(gameId);
}
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public List<Game> getGames(@RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting games page {} of size {} with search term {}", page, pageSize, searchTerm);
List<Game> games;
if((searchTerm == null) || (searchTerm.isBlank())){
games = gameService.getGames(page, pageSize);
}
else{
games = gameService.getGames(page, pageSize, searchTerm);
}
return games;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode getGamesCount(@RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting games count with search term {}", searchTerm);
Long gamesCount;
if((searchTerm == null) || (searchTerm.isBlank())){
gamesCount = gameService.getGamesCount();
}
else{
gamesCount = gameService.getGamesCount(searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", gamesCount);
countNode.put("status", "success");
return countNode;
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode createGame(@RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("gameName") String gameName){
log.info("Creating game {}", gameName);
ObjectNode returnNode = mapper.createObjectNode();
Game game = new Game();
game.setGameName(gameName);
List<String> errors = gvUtil.validateNewGame(game);
if(errors.isEmpty()){
game = gameService.createGame(game, file);
returnNode.put("gameId", game.getGameId().toString());
returnNode.put("status", "success");
log.info("Successfully created game: {}", game.getGameId());
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@PutMapping("/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode updateGame(@PathVariable("gameId") UUID gameId, @RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("gameName") String gameName, @RequestParam(name = "gameIcon", required = false) String gameIcon){
log.info("Updating game {}", gameName);
ObjectNode returnNode = mapper.createObjectNode();
Game game = new Game();
game.setGameId(gameId);
game.setGameName(gameName);
game.setGameIcon(gameIcon);
List<String> errors = gvUtil.validateExistingGame(game);
if(errors.isEmpty()){
game = gameService.updateGame(game, file);
returnNode.put("gameId", game.getGameId().toString());
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errors.forEach(errorNode::add);
returnNode.set("errors", errorNode);
}
return returnNode;
}
@DeleteMapping("/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@GameAuthorization(permissions = {GamePermissionType.ADMIN})
public ObjectNode deleteGame(@PathVariable("gameId") UUID gameId){
log.info("Deleting game {}", gameId);
ObjectNode returnNode = mapper.createObjectNode();
Game game = gameService.getGameById(gameId);
if(game != null){
gameService.deleteById(gameId);
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
ArrayNode errorNode = mapper.createArrayNode();
errorNode.add("Game ID is invalid");
returnNode.set("errors", errorNode);
}
return returnNode;
}
}

View File

@@ -0,0 +1,87 @@
package com.mattrixwv.raidbuilder.controller;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
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 com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.entity.GameClass;
import com.mattrixwv.raidbuilder.service.GameClassService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("/icons")
@RequiredArgsConstructor
public class IconController{
private final GameClassService gameClassService;
//Properties
@Value("${uploadFileDirectory}")
private String uploadFileDirectory;
@GetMapping("/game/{gameIconName}")
@AccountAuthorization(permissions = {})
public ResponseEntity<byte[]> getGameIcons(@PathVariable("gameIconName") String gameIconName) throws IOException{
log.info("Getting game icon {}", gameIconName);
byte[] resource = Files.readAllBytes(Path.of(uploadFileDirectory + "/gameIcons/" + gameIconName));
return ResponseEntity.ok().body(resource);
}
@GetMapping("/raidGroup/{raidGroupIconName}")
@AccountAuthorization(permissions = {})
public ResponseEntity<byte[]> getRaidGroupIcons(@PathVariable("raidGroupIconName") String raidGroupIconName) throws IOException{
log.info("Getting raid group icon {}", raidGroupIconName);
byte[] resource = Files.readAllBytes(Path.of(uploadFileDirectory + "/raidGroupIcons/" + raidGroupIconName));
return ResponseEntity.ok().body(resource);
}
@GetMapping("/gameClass/{gameClassIconName}")
@AccountAuthorization(permissions = {})
public ResponseEntity<byte[]> getGameClassIcons(@PathVariable("gameClassIconName") String gameClassIconName) throws IOException{
log.info("Getting game class icon {}", gameClassIconName);
byte[] resource = Files.readAllBytes(Path.of(uploadFileDirectory + "/gameClassIcons/" + gameClassIconName));
return ResponseEntity.ok().body(resource);
}
@GetMapping("/gameClass/id/{gameClassId}")
@AccountAuthorization(permissions = {})
public ResponseEntity<byte[]> getGameClassIcons(@PathVariable("gameClassId") UUID gameClassId) throws IOException{
log.info("Getting game class icon {}", gameClassId);
GameClass gameClass = gameClassService.getById(gameClassId);
byte[] resource = null;
if((gameClass != null) && (gameClass.getGameClassIcon() != null)){
resource = Files.readAllBytes(Path.of(uploadFileDirectory + "/gameClassIcons/" + gameClass.getGameClassIcon()));
}
return ResponseEntity.ok().body(resource);
}
}

View File

@@ -0,0 +1,174 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.PersonCharacter;
import com.mattrixwv.raidbuilder.service.PersonCharacterService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.PersonCharacterValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/person/{personId}/character")
@RequiredArgsConstructor
public class PersonCharacterController{
private final ObjectMapper mapper;
private final PersonCharacterService personCharacterService;
//Utilities
private final PersonCharacterValidationUtil pcvUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<PersonCharacter> getPersonCharactersForPerson(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId){
log.info("Getting person characters for person {}", personId);
return personCharacterService.getByPersonId(personId);
}
@GetMapping("/page")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public List<PersonCharacter> getCharactersForPersonPaged(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting person characters for person {} with page {} of size {} with search term {}", personId, page, pageSize, searchTerm);
List<PersonCharacter> personCharacters;
if(searchTerm == null){
personCharacters = personCharacterService.getByPersonId(personId, page, pageSize);
}
else{
personCharacters = personCharacterService.getByPersonId(personId, page, pageSize, searchTerm);
}
return personCharacters;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode getCharactersCountForPerson(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting person characters count for person {} with search term {}", personId, searchTerm);
Long count;
if(searchTerm == null){
count = personCharacterService.getCountByPersonId(personId);
}
else{
count = personCharacterService.getCountByPersonId(personId, searchTerm);
}
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
returnNode.put("count", count);
return returnNode;
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createPersonCharacter(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId, @RequestBody PersonCharacter personCharacter){
log.info("Creating person character {} for person {}", personCharacter.getCharacterName(), personId);
personCharacter.setPersonId(personId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = pcvUtil.validateNewPersonCharacter(personCharacter, raidGroupId);
if(errors.isEmpty()){
personCharacter = personCharacterService.createPersonCharacter(personCharacter);
returnNode.put("status", "success");
returnNode.put("personCharacterId", personCharacter.getPersonCharacterId().toString());
log.info("Created person character {} for person {}", personCharacter.getCharacterName(), personId);
}
else{
returnNode.put("status", "error");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@PutMapping("/{characterId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode updatePersonCharacter(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId, @PathVariable("characterId") UUID characterId, @RequestBody PersonCharacter personCharacter){
log.info("Updating person character {} for person {}", personCharacter.getCharacterName(), personId);
personCharacter.setPersonCharacterId(characterId);
personCharacter.setPersonId(personId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = pcvUtil.validateExistingPersonCharacter(personCharacter, raidGroupId);
if(errors.isEmpty()){
personCharacter = personCharacterService.updatePersonCharacter(personCharacter);
returnNode.put("status", "success");
returnNode.put("personCharacterId", personCharacter.getPersonCharacterId().toString());
}
else{
returnNode.put("status", "error");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@DeleteMapping("/{characterId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode deletePersonCharacter(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId, @PathVariable("characterId") UUID characterId){
log.info("Deleting person character {} for person {}", characterId, personId);
ObjectNode returnNode = mapper.createObjectNode();
PersonCharacter personCharacter = personCharacterService.getById(characterId);
if(personCharacter != null){
personCharacterService.deletePersonCharacter(characterId);
returnNode.put("status", "success");
}
else{
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Person character not found");
returnNode.put("status", "error");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
}

View File

@@ -0,0 +1,177 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.Person;
import com.mattrixwv.raidbuilder.service.PersonService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.PersonValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/person")
@RequiredArgsConstructor
public class PersonController{
private final ObjectMapper mapper;
private final PersonService personService;
//Utils
private final PersonValidationUtil pvUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<Person> getPeople(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting people for raid group: {} page {} of size {} with search term {}", raidGroupId, page, pageSize, searchTerm);
List<Person> people;
if(searchTerm == null){
people = personService.getPeopleByRaidGroup(raidGroupId, page, pageSize);
}
else{
people = personService.getPeopleByRaidGroup(raidGroupId, page, pageSize, searchTerm);
}
return people;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public ObjectNode getPeopleCount(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting people count for raid group {}", raidGroupId);
long peopleCount;
if(searchTerm == null){
peopleCount = personService.getPeopleCountByRaidGroup(raidGroupId);
}
else{
peopleCount = personService.getPeopleCountByRaidGroup(raidGroupId, searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", peopleCount);
countNode.put("status", "success");
return countNode;
}
@GetMapping("/{personId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public Person getPerson(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId){
log.info("Getting person for raid group {}", raidGroupId);
Person person = personService.getPerson(personId, raidGroupId);
return person;
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createPerson(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody Person person){
log.info("Creating person for raid group {}", raidGroupId);
person.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = pvUtil.validateNewPerson(person);
if(errors.isEmpty()){
person = personService.createPerson(person);
returnNode.put("status", "success");
returnNode.put("personId", person.getPersonId().toString());
log.info("Created person {} for raid group {}", person.getPersonName(), raidGroupId);
}
else{
returnNode.put("status", "error");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@PutMapping("/{personId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode updatePerson(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId, @RequestBody Person person){
log.info("Updating person for raid group {}", raidGroupId);
person.setPersonId(personId);
person.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = pvUtil.validateExistingPerson(person);
if(errors.isEmpty()){
person = personService.updatePerson(person);
returnNode.put("status", "success");
}
else{
returnNode.put("status", "error");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@DeleteMapping("/{personId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode deletePerson(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("personId") UUID personId){
log.info("Deleting person for raid group {}", raidGroupId);
Person person = personService.getPerson(personId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
if(person == null){
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Person not found");
returnNode.set("errors", errorsNode);
returnNode.put("status", "error");
}
else{
personService.deletePerson(personId);
returnNode.put("status", "success");
}
return returnNode;
}
}

View File

@@ -0,0 +1,280 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.PersonCharacter;
import com.mattrixwv.raidbuilder.entity.RaidGroup;
import com.mattrixwv.raidbuilder.service.PersonCharacterService;
import com.mattrixwv.raidbuilder.service.RaidGroupService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.RaidGroupValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup")
@RequiredArgsConstructor
public class RaidGroupController{
private final ObjectMapper mapper;
private final RaidGroupService raidGroupService;
private final PersonCharacterService personCharacterService;
//Utilities
private final RaidGroupValidationUtil rgvUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public List<RaidGroup> getRaidGroups(@RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting raid groups page {} of size {} with search term {}", page, pageSize, searchTerm);
List<RaidGroup> raidGroups;
if((searchTerm == null) || (searchTerm.isBlank())){
raidGroups = raidGroupService.getRaidGroups(page, pageSize);
}
else{
raidGroups = raidGroupService.getRaidGroups(page, pageSize, searchTerm);
}
return raidGroups;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN})
public ObjectNode getRaidGroupsCount(@RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting raid groups count with search term {}", searchTerm);
Long raidGroupsCount;
if((searchTerm == null) || (searchTerm.isBlank())){
raidGroupsCount = raidGroupService.getRaidGroupsCount();
}
else{
raidGroupsCount = raidGroupService.getRaidGroupsCount(searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", raidGroupsCount);
countNode.put("status", "success");
return countNode;
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode createRaidGroup(@RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("raidGroupName") String raidGroupName, @RequestParam("gameId") UUID gameId){
log.info("Creating raid group {}", raidGroupName);
ObjectNode returnNode = mapper.createObjectNode();
RaidGroup raidGroup = new RaidGroup();
raidGroup.setGameId(gameId);
raidGroup.setRaidGroupName(raidGroupName);
List<String> errors = rgvUtil.validateNewRaidGroup(raidGroup);
if(errors.isEmpty()){
raidGroupService.createRaidGroup(raidGroup, file);
returnNode.put("raidGroupId", raidGroup.getRaidGroupId().toString());
returnNode.put("status", "success");
log.info("Created raid group {}", raidGroup.getRaidGroupName());
}
else{
returnNode.put("status", "fail");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@GetMapping("/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public RaidGroup getRaidGroup(@PathVariable("raidGroupId") UUID raidGroupId){
log.info("Getting raid group {}", raidGroupId);
RaidGroup raidGroup = raidGroupService.getByRaidGroupId(raidGroupId);
return raidGroup;
}
@PutMapping("/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode updateRaidGroup(@RequestParam(value = "iconFile", required = false) MultipartFile file, @PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("raidGroupName") String raidGroupName, @RequestParam("gameId") UUID gameId, @RequestParam(value = "raidGroupIcon", required = false) String raidGroupIcon){
log.info("Updating raid group {}", raidGroupName);
ObjectNode returnNode = mapper.createObjectNode();
RaidGroup raidGroup = new RaidGroup();
raidGroup.setRaidGroupId(raidGroupId);
raidGroup.setGameId(gameId);
raidGroup.setRaidGroupName(raidGroupName);
raidGroup.setRaidGroupIcon(raidGroupIcon);
List<String> errors = rgvUtil.validateExistingRaidGroup(raidGroup);
if(errors.isEmpty()){
raidGroupService.updateRaidGroup(raidGroup, file);
returnNode.put("raidGroupId", raidGroup.getRaidGroupId().toString());
returnNode.put("status", "success");
}
else{
returnNode.put("status", "fail");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@DeleteMapping("/{raidGroupId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode deleteRaidGroup(@PathVariable("raidGroupId") UUID raidGroupId){
log.info("Deleting raid group {}", raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
RaidGroup existingRaidGroup = raidGroupService.getByRaidGroupId(raidGroupId);
if(existingRaidGroup != null){
raidGroupService.deleteById(raidGroupId);
returnNode.put("status", "success");
}
else{
returnNode.put("status", "fail");
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Raid group does not exist");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
//!Game
@GetMapping("/game/{gameId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public List<RaidGroup> getRaidGroupsByGame(@PathVariable("gameId") UUID gameId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTermString){
log.info("Getting raid groups for game {} page {} of size {} with search term {}", gameId, page, pageSize, searchTermString);
List<RaidGroup> raidGroups;
if((searchTermString == null) || (searchTermString.isBlank())){
raidGroups = raidGroupService.getRaidGroupsByGame(gameId, page, pageSize);
}
else{
raidGroups = raidGroupService.getRaidGroupsByGame(gameId, page, pageSize, searchTermString);
}
return raidGroups;
}
@GetMapping("/game/{gameId}/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode getRaidGroupsCountByGame(@PathVariable("gameId") UUID gameId, @RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting raid groups count for game {} with search term {}", gameId, searchTerm);
Long raidGroupsCount;
if((searchTerm == null) || (searchTerm.isBlank())){
raidGroupsCount = raidGroupService.getRaidGroupsCountByGame(gameId);
}
else{
raidGroupsCount = raidGroupService.getRaidGroupsCountByGame(gameId, searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", raidGroupsCount);
countNode.put("status", "success");
return countNode;
}
//!Account
@GetMapping("/account/{accountId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public List<RaidGroup> getRaidGroupsByAccount(@PathVariable("accountId") UUID accountId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTermString){
log.info("Getting raid groups for account {} page {} of size {} with search term {}", accountId, page, pageSize, searchTermString);
List<RaidGroup> raidGroups;
if((searchTermString == null) || (searchTermString.isBlank())){
raidGroups = raidGroupService.getRaidGroupsByAccount(accountId, page, pageSize);
}
else{
raidGroups = raidGroupService.getRaidGroupsByAccount(accountId, page, pageSize, searchTermString);
}
return raidGroups;
}
@GetMapping("/account/{accountId}/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode getRaidGroupsCountByAccount(@PathVariable("accountId") UUID accountId, @RequestParam(value = "searchTerm", required = false) String searchTerm){
log.info("Getting raid groups count for account {} with search term {}", accountId, searchTerm);
Long raidGroupsCount;
if((searchTerm == null) || (searchTerm.isBlank())){
raidGroupsCount = raidGroupService.getRaidGroupsCountByAccount(accountId);
}
else{
raidGroupsCount = raidGroupService.getRaidGroupsCountByAccount(accountId, searchTerm);
}
ObjectNode countNode = mapper.createObjectNode();
countNode.put("count", raidGroupsCount);
countNode.put("status", "success");
return countNode;
}
//!Person Character
@GetMapping("/{raidGroupId}/person/character")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<PersonCharacter> getPersonCharactersByRaidGroup(@PathVariable("raidGroupId") UUID raidGroupId){
log.info("Getting person characters for raid group {}", raidGroupId);
List<PersonCharacter> personCharacters = personCharacterService.getPersonCharactersByRaidGroupId(raidGroupId);
return personCharacters;
}
}

View File

@@ -0,0 +1,179 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.Account;
import com.mattrixwv.raidbuilder.entity.RaidGroupRequest;
import com.mattrixwv.raidbuilder.service.AccountService;
import com.mattrixwv.raidbuilder.service.RaidGroupRequestService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.RaidGroupRequestValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/raidGroupRequest")
@RequiredArgsConstructor
public class RaidGroupRequestController{
private final ObjectMapper mapper;
private final AccountService accountService;
private final RaidGroupRequestService rgrService;
//Utilities
private final RaidGroupRequestValidationUtil rgrvUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public List<RaidGroupRequest> getRaidGroupRequests(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting raid group requests for raid group id: {}", raidGroupId);
List<RaidGroupRequest> raidGroupRequests = searchTerm == null ?
rgrService.getByRaidGroupId(raidGroupId, page, pageSize) :
rgrService.getByRaidGroupId(raidGroupId, page, pageSize, searchTerm);
return raidGroupRequests;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode getRaidGroupRequestCount(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting Raid Group Request Count for Raid Group {} with search {}", raidGroupId, searchTerm);
long count = searchTerm == null ?
rgrService.getCountByRaidGroupId(raidGroupId) :
rgrService.getCountByRaidGroupId(raidGroupId, searchTerm);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("count", count);
returnNode.put("status", "success");
return returnNode;
}
@PutMapping("/{raidGroupRequestId}/resolve")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN})
public ObjectNode resolveRaidGroupRequest(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidGroupRequestId") UUID raidGroupRequestId, @RequestBody ObjectNode bodyNode){
log.info("Resolving raid group request for raid group id: {}", raidGroupId);
String resolution = bodyNode.get("resolution").asString();
if(resolution.equals("denied")){
rgrService.deleteRaidGroupRequest(raidGroupRequestId);
}
else{
RaidGroupPermissionType permissionType = RaidGroupPermissionType.valueOf(resolution);
rgrService.resolveRaidGroupRequest(raidGroupRequestId, permissionType);
}
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
return returnNode;
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode createRaidGroupRequest(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody RaidGroupRequest raidGroupRequest, Authentication authentication){
log.info("Creating raid group request for raid group id: {}", raidGroupId);
Account account = accountService.getByUsername(authentication.getName());
raidGroupRequest.setAccountId(account.getAccountId());
raidGroupRequest.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = rgrvUtil.validateNewRaidGroupRequest(raidGroupRequest);
if(errors.isEmpty()){
raidGroupRequest = rgrService.createRaidGroupRequest(raidGroupRequest);
returnNode.put("status", "success");
returnNode.put("raidGroupRequestId", raidGroupRequest.getRaidGroupRequestId().toString());
log.info("Created raid group request for raid group id: {}", raidGroupId);
}
else{
returnNode.put("status", "fail");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@PutMapping("/{raidGroupRequestId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode updateRaidGroupRequest(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidGroupRequestId") UUID raidGroupRequestId, @RequestBody RaidGroupRequest raidGroupRequest){
log.info("Updating raid group request for raid group id: {}", raidGroupId);
raidGroupRequest.setRaidGroupRequestId(raidGroupRequestId);
raidGroupRequest.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = rgrvUtil.validateExistingRaidGroupRequest(raidGroupRequest);
if(errors.isEmpty()){
raidGroupRequest = rgrService.updateRaidGroupRequest(raidGroupRequest);
returnNode.put("status", "success");
returnNode.put("raidGroupRequestId", raidGroupRequest.getRaidGroupRequestId().toString());
}
else{
returnNode.put("status", "fail");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@DeleteMapping("/{raidGroupRequestId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
public ObjectNode deleteRaidGroupRequest(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidGroupRequestId") UUID raidGroupRequestId){
log.info("Deleting raid group request for raid group id: {}", raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
RaidGroupRequest existingRaidGroupRequest = rgrService.getById(raidGroupRequestId);
if(existingRaidGroupRequest != null){
rgrService.deleteRaidGroupRequest(raidGroupRequestId);
returnNode.put("status", "success");
}
else{
returnNode.put("status", "fail");
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Raid Group Request does not exist");
returnNode.set("errors", errorsNode);
}
return returnNode;
}
}

View File

@@ -0,0 +1,175 @@
package com.mattrixwv.raidbuilder.controller;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.RaidInstance;
import com.mattrixwv.raidbuilder.service.RaidInstanceService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.RaidInstanceValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/raidInstance")
@RequiredArgsConstructor
public class RaidInstanceController{
private final ObjectMapper mapper;
private final RaidInstanceService raidInstanceService;
//Utilities
private final RaidInstanceValidationUtil rivUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<RaidInstance> getRaidInstances(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting future raid instance for raid group {} on page {} of size {} with search term {}", raidGroupId, page, pageSize, searchTerm);
List<RaidInstance> raidInstances;
if(searchTerm == null || searchTerm.isEmpty()){
raidInstances = raidInstanceService.findAllByRaidGroupIdAndEndDate(raidGroupId, ZonedDateTime.now().minusYears(1), page, pageSize);
}
else{
raidInstances = raidInstanceService.findAllByRaidGroupIdAndEndDate(raidGroupId, ZonedDateTime.now().minusYears(1), page, pageSize, searchTerm);
}
log.debug("Found {} raid instances", raidInstances.size());
return raidInstances;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public ObjectNode getRaidInstanceCount(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting raid instance count for raid group {} with search term {}", raidGroupId, searchTerm);
long count;
if(searchTerm == null || searchTerm.isEmpty()){
count = raidInstanceService.countByRaidGroupIdAndEndDate(raidGroupId, ZonedDateTime.now());
}
else{
count = raidInstanceService.countByRaidGroupIdAndEndDate(raidGroupId, ZonedDateTime.now(), searchTerm);
}
log.debug("Found count {}", count);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("count", count);
returnNode.put("status", "success");
return returnNode;
}
@GetMapping("/{raidInstanceId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public RaidInstance getRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidInstanceId") UUID raidInstanceId){
log.info("Getting raid instance {} for raid group {}", raidInstanceId, raidGroupId);
return raidInstanceService.findById(raidInstanceId);
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody RaidInstance raidInstance){
log.info("Creating raid instance for raid group {}", raidGroupId);
raidInstance.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = rivUtil.validateNewRaidInstance(raidInstance);
if(errors.isEmpty()){
RaidInstance createdRaidInstance = raidInstanceService.createRaidInstance(raidInstance);
returnNode.put("status", "success");
returnNode.put("raidInstanceId", createdRaidInstance.getRaidInstanceId().toString());
log.info("Created raid instance {}", createdRaidInstance);
}
else{
returnNode.put("status", "error");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@PutMapping("/{raidInstanceId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode updateRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidInstanceId") UUID raidInstanceId, @RequestBody RaidInstance raidInstance){
log.info("Updating raid instance {} for raid group {}", raidInstanceId, raidGroupId);
raidInstance.setRaidInstanceId(raidInstanceId);
raidInstance.setRaidGroupId(raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = rivUtil.validateExistingRaidInstance(raidInstance);
if(errors.isEmpty()){
RaidInstance updatedRaidInstance = raidInstanceService.updateRaidInstance(raidInstance);
returnNode.put("status", "success");
returnNode.put("raidInstanceId", updatedRaidInstance.getRaidInstanceId().toString());
}
else{
returnNode.put("status", "error");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@DeleteMapping("/{raidInstanceId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode deleteRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidInstanceId") UUID raidInstanceId){
log.info("Deleting raid instance {} for raid group {}", raidInstanceId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
if(raidInstanceService.findById(raidInstanceId) == null){
returnNode.put("status", "fail");
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Raid instance does not exist");
returnNode.set("errors", errorsNode);
}
else{
raidInstanceService.deleteById(raidInstanceId);
returnNode.put("status", "success");
}
return returnNode;
}
}

View File

@@ -0,0 +1,66 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.RaidInstancePersonCharacterXref;
import com.mattrixwv.raidbuilder.service.RaidInstancePersonCharacterXrefService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/raidInstance/{raidInstanceId}/personCharacterXref")
@RequiredArgsConstructor
public class RaidInstancePersonCharacterXrefController{
private final ObjectMapper mapper;
private final RaidInstancePersonCharacterXrefService ripcXrefService;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<RaidInstancePersonCharacterXref> getXrefsForRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidInstanceId") UUID raidInstanceId){
log.info("Getting xrefs for raid instance {} for raid group {}", raidInstanceId, raidGroupId);
return ripcXrefService.getByRaidInstanceId(raidInstanceId);
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createXrefsForRaidInstance(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidInstanceId") UUID raidInstanceId, @RequestBody List<RaidInstancePersonCharacterXref> xrefs){
log.info("Creating xrefs for raid instance {} for raid group {}", raidInstanceId, raidGroupId);
xrefs.forEach(xref -> xref.setRaidInstanceId(raidInstanceId));
List<RaidInstancePersonCharacterXref> existingXrefs = ripcXrefService.getByRaidInstanceId(raidInstanceId);
List<RaidInstancePersonCharacterXref> xrefsToDelete = existingXrefs.stream().filter(xref -> xrefs.stream().noneMatch(xref2 -> xref2.getPersonCharacterId().equals(xref.getPersonCharacterId()))).toList();
ripcXrefService.update(xrefs, xrefsToDelete);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("status", "success");
return returnNode;
}
}

View File

@@ -0,0 +1,173 @@
package com.mattrixwv.raidbuilder.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.DeleteMapping;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mattrixwv.raidbuilder.annotation.AccountAuthorization;
import com.mattrixwv.raidbuilder.annotation.RaidGroupAuthorization;
import com.mattrixwv.raidbuilder.entity.RaidLayout;
import com.mattrixwv.raidbuilder.entity.RaidLayoutClassGroupXref;
import com.mattrixwv.raidbuilder.service.RaidLayoutService;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import com.mattrixwv.raidbuilder.util.validation.RaidLayoutValidationUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
@Slf4j
@RestController
@RequestMapping("/raidGroup/{raidGroupId}/raidLayout")
@RequiredArgsConstructor
public class RaidLayoutController{
private final ObjectMapper mapper;
private final RaidLayoutService raidLayoutService;
//Utilities
private final RaidLayoutValidationUtil rlvUtil;
@GetMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public List<RaidLayout> getRaidLayouts(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting Raid Layouts for Raid Group {} on page {} of size {} with search {}", raidGroupId, page, pageSize, searchTerm);
List<RaidLayout> raidLayouts = searchTerm == null ?
raidLayoutService.getRaidLayoutsByRaidGroupId(raidGroupId, page, pageSize) :
raidLayoutService.getRaidLayoutsByRaidGroupId(raidGroupId, page, pageSize, searchTerm);
return raidLayouts;
}
@GetMapping("/count")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public ObjectNode getRaidLayoutCount(@PathVariable("raidGroupId") UUID raidGroupId, @RequestParam(name = "searchTerm", required = false) String searchTerm){
log.info("Getting Raid Layout Count for Raid Group {} with search {}", raidGroupId, searchTerm);
long count = searchTerm == null ?
raidLayoutService.getRaidLayoutCountByRaidGroupId(raidGroupId) :
raidLayoutService.getRaidLayoutCountByRaidGroupId(raidGroupId, searchTerm);
ObjectNode returnNode = mapper.createObjectNode();
returnNode.put("count", count);
returnNode.put("status", "success");
return returnNode;
}
@GetMapping("/{raidLayoutId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER, RaidGroupPermissionType.RAIDER})
public RaidLayout getRaidLayout(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidLayoutId") UUID raidLayoutId){
log.info("Getting Raid Layout {} for Raid Group {}", raidLayoutId, raidGroupId);
return raidLayoutService.getRaidLayout(raidLayoutId, raidGroupId);
}
@PostMapping
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode createRaidLayout(@PathVariable("raidGroupId") UUID raidGroupId, @RequestBody ObjectNode bodyNode){
log.info("Creating Raid Layout for Raid Group {}", raidGroupId);
RaidLayout raidLayout = mapper.convertValue(bodyNode.get("raidLayout"), RaidLayout.class);
raidLayout.setRaidGroupId(raidGroupId);
RaidLayoutClassGroupXref[] xrefsArray = mapper.convertValue(bodyNode.get("raidLayoutClassGroupXrefs"), RaidLayoutClassGroupXref[].class);
List<RaidLayoutClassGroupXref> xrefs = List.of(xrefsArray);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = rlvUtil.validateNewRaidLayout(raidLayout);
if(errors.isEmpty()){
raidLayout = raidLayoutService.createRaidLayout(raidLayout, xrefs);
returnNode.put("raidLayoutId", raidLayout.getRaidLayoutId().toString());
returnNode.put("status", "success");
log.info("Created Raid Layout {} for Raid Group {}", raidLayout.getRaidLayoutId(), raidGroupId);
}
else{
returnNode.put("status", "fail");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@PutMapping("/{raidLayoutId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode updateRaidLayout(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidLayoutId") UUID raidLayoutId, @RequestBody ObjectNode bodyNode){
log.info("Updating Raid Layout {} for Raid Group {}", raidLayoutId, raidGroupId);
RaidLayout raidLayout = mapper.convertValue(bodyNode.get("raidLayout"), RaidLayout.class);
raidLayout.setRaidGroupId(raidGroupId);
raidLayout.setRaidLayoutId(raidLayoutId);
RaidLayoutClassGroupXref[] xrefsArray = mapper.convertValue(bodyNode.get("raidLayoutClassGroupXrefs"), RaidLayoutClassGroupXref[].class);
List<RaidLayoutClassGroupXref> xrefs = List.of(xrefsArray);
ObjectNode returnNode = mapper.createObjectNode();
List<String> errors = rlvUtil.validateExistingRaidLayout(raidLayout);
if(errors.isEmpty()){
raidLayoutService.updateRaidLayout(raidLayout, xrefs);
returnNode.put("status", "success");
returnNode.put("raidLayoutId", raidLayout.getRaidLayoutId().toString());
}
else{
returnNode.put("status", "fail");
returnNode.set("errors", mapper.valueToTree(errors));
}
return returnNode;
}
@DeleteMapping("/{raidLayoutId}")
@AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER})
@RaidGroupAuthorization(permissions = {RaidGroupPermissionType.ADMIN, RaidGroupPermissionType.LEADER})
public ObjectNode deleteRaidLayout(@PathVariable("raidGroupId") UUID raidGroupId, @PathVariable("raidLayoutId") UUID raidLayoutId){
log.info("Deleting Raid Layout {} for Raid Group {}", raidLayoutId, raidGroupId);
ObjectNode returnNode = mapper.createObjectNode();
RaidLayout raidLayout = raidLayoutService.getRaidLayout(raidLayoutId, raidGroupId);
if(raidLayout == null){
ArrayNode errorsNode = mapper.createArrayNode();
errorsNode.add("Raid Layout doesn't exist");
returnNode.put("status", "fail");
returnNode.set("errors", errorsNode);
}
else{
raidLayoutService.deleteRaidLayout(raidLayoutId, raidGroupId);
returnNode.put("status", "success");
}
return returnNode;
}
}

View File

@@ -0,0 +1,54 @@
package com.mattrixwv.raidbuilder.entity;
import java.time.ZonedDateTime;
import java.util.UUID;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "account", schema = "raid_builder")
@Data
@NoArgsConstructor
public class Account{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "account_id")
private UUID accountId;
@Column(name = "username")
private String username;
@Column(name = "password")
private String password;
@Column(name = "login_date")
private ZonedDateTime loginDate;
@Column(name = "email")
private String email;
@Column(name = "force_reset")
private boolean forceReset;
@Column(name = "refresh_token")
private UUID refreshToken;
@Column(name = "refresh_token_expiration")
private ZonedDateTime refreshTokenExpiration;
@Enumerated(EnumType.STRING)
@Column(name = "account_status")
private AccountStatus accountStatus;
@JsonIgnore
public String getPassword(){ return password; }
@JsonProperty
public void setPassword(String password){ this.password = password; }
}

View File

@@ -0,0 +1,42 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import org.springframework.security.core.GrantedAuthority;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "account_permission", schema = "raid_builder")
@Data
@NoArgsConstructor
public class AccountPermission implements GrantedAuthority{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "account_permission_id")
private UUID accountPermissionId;
@Column(name = "account_id")
private UUID accountId;
@Enumerated(EnumType.STRING)
@Column(name = "account_permission_type")
private AccountPermissionType accountPermissionType;
@Override
public String getAuthority(){
return accountPermissionType.name();
}
}

View File

@@ -0,0 +1,45 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.TutorialStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "account_tutorial_status", schema = "raid_builder")
@Data
@NoArgsConstructor
public class AccountTutorialStatus{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "account_tutorial_status_id")
private UUID accountTutorialStatusId;
@Column(name = "account_id")
private UUID accountId;
@Enumerated(EnumType.STRING)
@Column(name = "games_tutorial_status")
private TutorialStatus gamesTutorialStatus;
@Enumerated(EnumType.STRING)
@Column(name = "game_tutorial_status")
private TutorialStatus gameTutorialStatus;
@Enumerated(EnumType.STRING)
@Column(name = "raid_groups_tutorial_status")
private TutorialStatus raidGroupsTutorialStatus;
@Enumerated(EnumType.STRING)
@Column(name = "raid_group_tutorial_status")
private TutorialStatus raidGroupTutorialStatus;
@Enumerated(EnumType.STRING)
@Column(name = "instance_tutorial_status")
private TutorialStatus instanceTutorialStatus;
}

View File

@@ -0,0 +1,63 @@
package com.mattrixwv.raidbuilder.entity;
import java.time.ZonedDateTime;
import java.util.UUID;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import lombok.Data;
@Data
@MappedSuperclass
public abstract class AuditableEntity{
@JsonIgnore
@Column(name = "modified_by")
protected UUID modifiedBy;
@JsonIgnore
@Column(name = "modified_date")
protected ZonedDateTime modifiedDate;
@JsonIgnore
@Column(name = "created_by", updatable = false)
protected UUID createdBy;
@JsonIgnore
@Column(name = "created_date", updatable = false)
protected ZonedDateTime createdDate;
@PrePersist
public void prePersist() throws NoSuchFieldException, IllegalAccessException{
this.createdBy = getCurrentUserId();
this.createdDate = ZonedDateTime.now();
}
@PreUpdate
public void preUpdate(){
this.modifiedBy = getCurrentUserId();
this.modifiedDate = ZonedDateTime.now();
}
private UUID getCurrentUserId(){
UUID returnUUID;
try{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
returnUUID = UUID.fromString(((Jwt)auth.getPrincipal()).getClaimAsString("accountId"));
}
catch(Exception e){
returnUUID = new UUID(0, 0);
}
return returnUUID;
}
}

View File

@@ -0,0 +1,31 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "class_group", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class ClassGroup extends AuditableEntity{
@Id
@Column(name = "class_group_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID classGroupId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "class_group_name")
private String classGroupName;
}

View File

@@ -0,0 +1,31 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "class_group_game_class_xref", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class ClassGroupGameClassXref extends AuditableEntity{
@Id
@Column(name = "class_group_game_class_xref_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID classGroupGameClassXrefId;
@Column(name = "class_group_id")
private UUID classGroupId;
@Column(name = "game_class_id")
private UUID gameClassId;
}

View File

@@ -0,0 +1,31 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "game", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class Game extends AuditableEntity{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "game_id")
private UUID gameId;
@Column(name = "game_name")
private String gameName;
@Column(name = "game_icon")
private String gameIcon;
}

View File

@@ -0,0 +1,43 @@
package com.mattrixwv.raidbuilder.entity;
import java.time.ZonedDateTime;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "game_calendar_event", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class GameCalendarEvent extends AuditableEntity{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "game_calendar_event_id")
private UUID gameCalendarEventId;
@Column(name = "game_id")
private UUID gameId;
@Column(name = "event_name")
private String eventName;
@Column(name = "event_description")
private String eventDescription;
@Column(name = "event_start_date")
private ZonedDateTime eventStartDate;
@Column(name = "event_end_date")
private ZonedDateTime eventEndDate;
public UUID getCalendarEventId(){
return gameCalendarEventId;
}
}

View File

@@ -0,0 +1,33 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "game_class", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class GameClass extends AuditableEntity{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "game_class_id")
private UUID gameClassId;
@Column(name = "game_id")
private UUID gameId;
@Column(name = "game_class_name")
private String gameClassName;
@Column(name = "game_class_icon")
private String gameClassIcon;
}

View File

@@ -0,0 +1,38 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "game_permission", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class GamePermission extends AuditableEntity{
@Id
@Column(name = "game_permission_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID gamePermissionId;
@Column(name = "account_id")
private UUID accountId;
@Column(name = "game_id")
private UUID gameId;
@Enumerated(EnumType.STRING)
@Column(name = "game_permission_type")
private GamePermissionType gamePermissionType;
}

View File

@@ -0,0 +1,33 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "person", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class Person extends AuditableEntity{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "person_id")
private UUID personId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "person_name")
private String personName;
@Column(name = "discord_id")
private String discordId;
}

View File

@@ -0,0 +1,37 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "person_character", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class PersonCharacter extends AuditableEntity{
@Id
@Column(name = "person_character_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID personCharacterId;
@Column(name = "person_id")
private UUID personId;
@Column(name = "game_class_id")
private UUID gameClassId;
@Column(name = "character_name")
private String characterName;
@Column(name = "character_rating")
private int characterRating;
@Column(name = "character_comments")
private String characterComments;
}

View File

@@ -0,0 +1,33 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_group", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidGroup extends AuditableEntity{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "game_id")
private UUID gameId;
@Column(name = "raid_group_name")
private String raidGroupName;
@Column(name = "raid_group_icon")
private String raidGroupIcon;
}

View File

@@ -0,0 +1,43 @@
package com.mattrixwv.raidbuilder.entity;
import java.time.ZonedDateTime;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_group_calendar_event", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidGroupCalendarEvent extends AuditableEntity{
@Id
@Column(name = "raid_group_calendar_event_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID raidGroupCalendarEventId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "event_name")
private String eventName;
@Column(name = "event_description")
private String eventDescription;
@Column(name = "event_start_date")
private ZonedDateTime eventStartDate;
@Column(name = "event_end_date")
private ZonedDateTime eventEndDate;
public UUID getCalendarEventId(){
return raidGroupCalendarEventId;
}
}

View File

@@ -0,0 +1,38 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.RaidGroupPermissionType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_group_permission", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidGroupPermission extends AuditableEntity{
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "raid_group_permission_id")
private UUID raidGroupPermissionId;
@Column(name = "account_id")
private UUID accountId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Enumerated(EnumType.STRING)
@Column(name = "permission")
private RaidGroupPermissionType permission;
}

View File

@@ -0,0 +1,39 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import org.hibernate.annotations.Formula;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_group_request", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidGroupRequest extends AuditableEntity{
@Id
@Column(name = "raid_group_request_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID raidGroupRequestId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "account_id")
private UUID accountId;
@Column(name = "request_message")
private String requestMessage;
@Formula("(SELECT a.username FROM raid_builder.account a WHERE a.account_id = account_id)")
private String username;
}

View File

@@ -0,0 +1,42 @@
package com.mattrixwv.raidbuilder.entity;
import java.time.ZonedDateTime;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_instance", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidInstance extends AuditableEntity{
@Id
@Column(name = "raid_instance_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID raidInstanceId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "raid_layout_id")
private UUID raidLayoutId;
@Column(name = "raid_instance_name")
private String raidInstanceName;
@Column(name = "raid_size")
private Integer raidSize;
@Column(name = "number_runs")
private Integer numberRuns;
@Column(name = "raid_start_date")
private ZonedDateTime raidStartDate;
@Column(name = "raid_end_date")
private ZonedDateTime raidEndDate;
}

View File

@@ -0,0 +1,30 @@
package com.mattrixwv.raidbuilder.entity;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class RaidInstanceCalendarEvent{
private UUID calendarEventId;
private UUID raidInstanceId;
private String eventName;
private String eventDescription;
private ZonedDateTime eventStartDate;
private ZonedDateTime eventEndDate;
public RaidInstanceCalendarEvent(RaidInstance raidInstance){
this.calendarEventId = raidInstance.getRaidInstanceId();
this.raidInstanceId = raidInstance.getRaidInstanceId();
this.eventName = raidInstance.getRaidInstanceName();
this.eventDescription = "Raid Instance " + raidInstance.getRaidInstanceName();
this.eventStartDate = raidInstance.getRaidStartDate();
this.eventEndDate = raidInstance.getRaidEndDate();
}
}

View File

@@ -0,0 +1,35 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_instance_person_character_xref", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidInstancePersonCharacterXref extends AuditableEntity{
@Id
@Column(name = "raid_instance_person_character_xref_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID raidInstancePersonCharacterXrefId;
@Column(name = "raid_instance_id")
private UUID raidInstanceId;
@Column(name = "person_character_id")
private UUID personCharacterId;
@Column(name = "group_number")
private int groupNumber;
@Column(name = "position_number")
private int positionNumber;
}

View File

@@ -0,0 +1,33 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_layout", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidLayout extends AuditableEntity{
@Id
@Column(name = "raid_layout_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID raidLayoutId;
@Column(name = "raid_group_id")
private UUID raidGroupId;
@Column(name = "raid_layout_name")
private String raidLayoutName;
@Column(name = "raid_size")
private int raidSize;
}

View File

@@ -0,0 +1,41 @@
package com.mattrixwv.raidbuilder.entity;
import java.util.UUID;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "raid_layout_class_group_xref", schema = "raid_builder")
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public class RaidLayoutClassGroupXref extends AuditableEntity{
@Id
@Column(name = "raid_layout_class_group_xref_id")
@GeneratedValue(strategy = GenerationType.UUID)
private UUID raidLayoutClassGroupXrefId;
@Column(name = "raid_layout_id")
private UUID raidLayoutId;
@Column(name = "class_group_id")
private UUID classGroupId;
@Column(name = "layout_location")
private int layoutLocation;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "class_group_id", referencedColumnName = "class_group_id", insertable = false, updatable = false)
private ClassGroup classGroup;
}

View File

@@ -0,0 +1,20 @@
package com.mattrixwv.raidbuilder.exception;
public class MissingAuthorizationException extends RuntimeException{
public MissingAuthorizationException(){
super();
}
public MissingAuthorizationException(String message){
super(message);
}
public MissingAuthorizationException(Throwable cause){
super(cause);
}
public MissingAuthorizationException(String message, Throwable cause){
super(message, cause);
}
}

View File

@@ -0,0 +1,18 @@
package com.mattrixwv.raidbuilder.repository.account;
import java.util.List;
import java.util.UUID;
import org.springframework.data.domain.PageRequest;
import com.mattrixwv.raidbuilder.entity.Account;
public interface AccountCustomRepository{
public List<Account> findAllAccountsByRaidGroupId(UUID raidGroupId, PageRequest pageRequest);
public List<Account> findAllAccountsByRaidGroupId(UUID raidGroupId, String searchTerm, PageRequest pageRequest);
public long countByRaidGroupId(UUID raidGroupId);
public long countByRaidGroupId(UUID raidGroupId, String searchTerm);
}

View File

@@ -0,0 +1,20 @@
package com.mattrixwv.raidbuilder.repository.account;
import java.util.List;
import java.util.UUID;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.Account;
public interface AccountRepository extends AccountCustomRepository, JpaRepository<Account, UUID>{
public Account findByUsername(String username);
public Account findByRefreshToken(UUID refreshToken);
public Account findByEmail(String email);
public List<Account> findAllByUsernameContainingIgnoreCase(String searchTerm, PageRequest pageRequest);
public long countByUsernameContainingIgnoreCase(String searchTerm);
}

View File

@@ -0,0 +1,80 @@
package com.mattrixwv.raidbuilder.repository.account;
import java.util.List;
import java.util.UUID;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Order;
import org.springframework.stereotype.Repository;
import com.mattrixwv.raidbuilder.entity.Account;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class AccountRepositoryImpl implements AccountCustomRepository{
private final EntityManager entityManager;
public List<Account> findAllAccountsByRaidGroupId(UUID raidGroupId, PageRequest pageRequest){
Order order = pageRequest.getSort().toList().get(0);
return entityManager.createQuery("""
SELECT a FROM Account a
INNER JOIN RaidGroupPermission rgp ON a.accountId = rgp.accountId
WHERE rgp.raidGroupId = :raidGroupId
ORDER BY :orderBy
""", Account.class)
.setParameter("raidGroupId", raidGroupId)
.setParameter("orderBy", order.getProperty() + " " + order.getDirection())
.setFirstResult(pageRequest.getPageNumber() * pageRequest.getPageSize())
.setMaxResults(pageRequest.getPageSize())
.getResultList();
}
public List<Account> findAllAccountsByRaidGroupId(UUID raidGroupId, String searchTerm, PageRequest pageRequest){
Order order = pageRequest.getSort().toList().get(0);
return entityManager.createQuery("""
SELECT a FROM Account a
INNER JOIN RaidGroupPermission rgp ON a.accountId = rgp.accountId
WHERE rgp.raidGroupId = :raidGroupId
AND LOWER(a.username) LIKE LOWER(:searchTerm)
ORDER BY :orderBy
""", Account.class)
.setParameter("raidGroupId", raidGroupId)
.setParameter("searchTerm", "%" + searchTerm + "%")
.setParameter("orderBy", order.getProperty() + " " + order.getDirection())
.setFirstResult(pageRequest.getPageNumber() * pageRequest.getPageSize())
.setMaxResults(pageRequest.getPageSize())
.getResultList();
}
public long countByRaidGroupId(UUID raidGroupId){
return entityManager.createQuery("""
SELECT COUNT(a) FROM Account a
INNER JOIN RaidGroupPermission rgp ON a.accountId = rgp.accountId
WHERE rgp.raidGroupId = :raidGroupId
""", Long.class)
.setParameter("raidGroupId", raidGroupId)
.getSingleResult();
}
public long countByRaidGroupId(UUID raidGroupId, String searchTerm){
return entityManager.createQuery("""
SELECT COUNT(a) FROM Account a
INNER JOIN RaidGroupPermission rgp ON a.accountId = rgp.accountId
WHERE rgp.raidGroupId = :raidGroupId
AND LOWER(a.username) LIKE LOWER(:searchTerm)
""", Long.class)
.setParameter("raidGroupId", raidGroupId)
.setParameter("searchTerm", "%" + searchTerm + "%")
.getSingleResult();
}
}

View File

@@ -0,0 +1,5 @@
package com.mattrixwv.raidbuilder.repository.account_permission;
public interface AccountPermissionCustomRepository{
}

View File

@@ -0,0 +1,17 @@
package com.mattrixwv.raidbuilder.repository.account_permission;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.AccountPermission;
public interface AccountPermissionRepository extends AccountPermissionCustomRepository, JpaRepository<AccountPermission, UUID>{
public void deleteAllByAccountId(UUID accountId);
public List<AccountPermission> findAllByAccountId(UUID accountId);
}

View File

@@ -0,0 +1,9 @@
package com.mattrixwv.raidbuilder.repository.account_permission;
import org.springframework.stereotype.Repository;
@Repository
public class AccountPermissionRepositoryImpl implements AccountPermissionCustomRepository{
}

View File

@@ -0,0 +1,5 @@
package com.mattrixwv.raidbuilder.repository.account_tutorial_status;
public interface AccountTutorialStatusCustomRepository{
}

View File

@@ -0,0 +1,16 @@
package com.mattrixwv.raidbuilder.repository.account_tutorial_status;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.AccountTutorialStatus;
public interface AccountTutorialStatusRepository extends AccountTutorialStatusCustomRepository, JpaRepository<AccountTutorialStatus, UUID>{
public void deleteAllByAccountId(UUID accountId);
public AccountTutorialStatus findByAccountId(UUID accountId);
}

View File

@@ -0,0 +1,9 @@
package com.mattrixwv.raidbuilder.repository.account_tutorial_status;
import org.springframework.stereotype.Repository;
@Repository
public class AccountTutorialStatusRepositoryImpl implements AccountTutorialStatusCustomRepository{
}

View File

@@ -0,0 +1,12 @@
package com.mattrixwv.raidbuilder.repository.class_group;
import java.util.List;
import java.util.UUID;
import com.mattrixwv.raidbuilder.entity.ClassGroup;
public interface ClassGroupCustomRepository{
public List<ClassGroup> findAllByRaidLayoutId(UUID raidLayoutId);
}

View File

@@ -0,0 +1,27 @@
package com.mattrixwv.raidbuilder.repository.class_group;
import java.util.List;
import java.util.UUID;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.ClassGroup;
public interface ClassGroupRepository extends ClassGroupCustomRepository, JpaRepository<ClassGroup, UUID>{
public void deleteByClassGroupIdAndRaidGroupId(UUID classGroupId, UUID raidGroupId);
public void deleteAllByRaidGroupId(UUID raidGroupId);
public void deleteAllByRaidGroupIdIn(Iterable<UUID> raidGroupIds);
public ClassGroup findByClassGroupIdAndRaidGroupId(UUID classGroupId, UUID raidGroupId);
public List<ClassGroup> findAllByRaidGroupId(UUID raidGroupId);
public List<ClassGroup> findAllByRaidGroupIdIn(Iterable<UUID> raidGroupIds);
public List<ClassGroup> findAllByRaidGroupId(UUID raidGroupIds, PageRequest pageRequest);
public List<ClassGroup> findAllByRaidGroupIdAndClassGroupNameContainingIgnoreCase(UUID raidGroupId, String searchTerm, PageRequest pageRequest);
public long countByRaidGroupId(UUID raidGroupId);
public long countByRaidGroupIdAndClassGroupNameContainingIgnoreCase(UUID raidGroupId, String searchTerm);
}

View File

@@ -0,0 +1,41 @@
package com.mattrixwv.raidbuilder.repository.class_group;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Repository;
import com.mattrixwv.raidbuilder.entity.ClassGroup;
import com.mattrixwv.raidbuilder.entity.RaidLayoutClassGroupXref;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Repository
@RequiredArgsConstructor
public class ClassGroupRepositoryImpl implements ClassGroupCustomRepository{
private final EntityManager entityManager;
@Override
public List<ClassGroup> findAllByRaidLayoutId(UUID raidLayoutId){
String selectString = """
SELECT xref FROM RaidLayoutClassGroupXref xref
LEFT JOIN FETCH xref.classGroup cg
WHERE xref.raidLayoutId = :raidLayoutId
ORDER BY xref.layoutLocation
""";
List<RaidLayoutClassGroupXref> xrefs = entityManager.createQuery(selectString, RaidLayoutClassGroupXref.class)
.setParameter("raidLayoutId", raidLayoutId)
.getResultList();
List<ClassGroup> classGroups = xrefs.stream().map(RaidLayoutClassGroupXref::getClassGroup).toList();
return classGroups;
}
}

View File

@@ -0,0 +1,5 @@
package com.mattrixwv.raidbuilder.repository.class_group_game_class_xref;
public interface ClassGroupGameClassXrefCustomRepository{
}

View File

@@ -0,0 +1,20 @@
package com.mattrixwv.raidbuilder.repository.class_group_game_class_xref;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.ClassGroupGameClassXref;
public interface ClassGroupGameClassXrefRepository extends ClassGroupGameClassXrefCustomRepository, JpaRepository<ClassGroupGameClassXref, UUID>{
public List<ClassGroupGameClassXref> findAllByClassGroupId(UUID classGroupId);
public void deleteAllByClassGroupId(UUID classGroupId);
public void deleteAllByClassGroupIdIn(Iterable<UUID> classGroupIds);
public void deleteAllByGameClassId(UUID gameClassId);
public void deleteAllByGameClassIdIn(Iterable<UUID> gameClassId);
}

View File

@@ -0,0 +1,9 @@
package com.mattrixwv.raidbuilder.repository.class_group_game_class_xref;
import org.springframework.stereotype.Repository;
@Repository
public class ClassGroupGameClassXrefRepositoryImpl implements ClassGroupGameClassXrefCustomRepository{
}

View File

@@ -0,0 +1,5 @@
package com.mattrixwv.raidbuilder.repository.game;
public interface GameCustomRepository{
}

View File

@@ -0,0 +1,16 @@
package com.mattrixwv.raidbuilder.repository.game;
import java.util.List;
import java.util.UUID;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.Game;
public interface GameRepository extends GameCustomRepository, JpaRepository<Game, UUID>{
public List<Game> findAllByGameNameContainingIgnoreCase(String searchTerm, PageRequest pageRequest);
public long countAllByGameNameContainingIgnoreCase(String searchTerm);
}

View File

@@ -0,0 +1,9 @@
package com.mattrixwv.raidbuilder.repository.game;
import org.springframework.stereotype.Repository;
@Repository
public class GameRepositoryImpl implements GameCustomRepository{
}

View File

@@ -0,0 +1,5 @@
package com.mattrixwv.raidbuilder.repository.game_calendar_event;
public interface GameCalendarEventCustomRepository{
}

View File

@@ -0,0 +1,17 @@
package com.mattrixwv.raidbuilder.repository.game_calendar_event;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.GameCalendarEvent;
public interface GameCalendarEventRepository extends GameCalendarEventCustomRepository, JpaRepository<GameCalendarEvent, UUID>{
public void deleteAllByGameId(UUID gameId);
public List<GameCalendarEvent> findAllByGameId(UUID gameId);
}

View File

@@ -0,0 +1,9 @@
package com.mattrixwv.raidbuilder.repository.game_calendar_event;
import org.springframework.stereotype.Repository;
@Repository
public class GameCalendarEventRepositoryImpl{
}

View File

@@ -0,0 +1,12 @@
package com.mattrixwv.raidbuilder.repository.game_class;
import java.util.List;
import java.util.UUID;
import com.mattrixwv.raidbuilder.entity.GameClass;
public interface GameClassCustomRepository{
public List<GameClass> findAllByClassGroupId(UUID classGroupId);
}

View File

@@ -0,0 +1,23 @@
package com.mattrixwv.raidbuilder.repository.game_class;
import java.util.List;
import java.util.UUID;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mattrixwv.raidbuilder.entity.GameClass;
public interface GameClassRepository extends GameClassCustomRepository, JpaRepository<GameClass, UUID>{
public void deleteByGameId(UUID gameId);
public List<GameClass> findAllByGameId(UUID gameId);
public List<GameClass> findAllByGameId(UUID gameId, PageRequest pageRequest);
public List<GameClass> findAllByGameIdAndGameClassNameContainingIgnoreCase(UUID gameId, String searchTerm, PageRequest pageRequest);
public long countByGameId(UUID gameId);
public long countByGameIdAndGameClassNameContainingIgnoreCase(UUID gameId, String searchTerm);
}

View File

@@ -0,0 +1,33 @@
package com.mattrixwv.raidbuilder.repository.game_class;
import java.util.List;
import java.util.UUID;
import org.springframework.stereotype.Repository;
import com.mattrixwv.raidbuilder.entity.GameClass;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class GameClassRepositoryImpl implements GameClassCustomRepository{
private final EntityManager entityManager;
@Override
public List<GameClass> findAllByClassGroupId(UUID classGroupId){
String selectString = """
SELECT gc FROM GameClass gc
INNER JOIN ClassGroupGameClassXref xref ON gc.gameClassId = xref.gameClassId
WHERE xref.classGroupId = :classGroupId
""";
return entityManager.createQuery(selectString, GameClass.class)
.setParameter("classGroupId", classGroupId)
.getResultList();
}
}

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