DONE: first init
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
Vendored
BIN
Binary file not shown.
+2
@@ -0,0 +1,2 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM openjdk:17-jdk-alpine
|
||||
COPY target/bbqms-0.0.1-SNAPSHOT.jar ./app.jar
|
||||
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
||||
@@ -0,0 +1,9 @@
|
||||
## Build instructions
|
||||
|
||||
- Enter your database details in the **src/main/resources/application.yml** file
|
||||
- Through IntelliJ simply click on the run button
|
||||
- Or through console
|
||||
- ./mvnw dependency:resolve
|
||||
- ./mvnw spring-boot:run
|
||||
|
||||
##### **NOTE:** requires Java 17 and MySQL 8
|
||||
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8
|
||||
container_name: bbqms-mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
MYSQL_DATABASE: bbqms
|
||||
volumes:
|
||||
- bbqms-mysql-data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-ppassword"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
volumes:
|
||||
bbqms-mysql-data:
|
||||
@@ -0,0 +1,308 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /usr/local/etc/mavenrc ] ; then
|
||||
. /usr/local/etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "$(uname)" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
|
||||
else
|
||||
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=$(java-config --jre-home)
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="$(which javac)"
|
||||
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=$(which readlink)
|
||||
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
|
||||
else
|
||||
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
|
||||
fi
|
||||
javaHome="$(dirname "\"$javaExecutable\"")"
|
||||
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=$(cd "$wdir/.." || exit 1; pwd)
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
# Remove \r in case we run on Windows within Git Bash
|
||||
# and check out the repository with auto CRLF management
|
||||
# enabled. Otherwise, we may read lines that are delimited with
|
||||
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
|
||||
# splitting rules.
|
||||
tr -s '\r\n' ' ' < "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
log() {
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
printf '%s\n' "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
|
||||
log "$MAVEN_PROJECTBASEDIR"
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if [ -r "$wrapperJarPath" ]; then
|
||||
log "Found $wrapperJarPath"
|
||||
else
|
||||
log "Couldn't find $wrapperJarPath, downloading it ..."
|
||||
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
else
|
||||
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
fi
|
||||
while IFS="=" read -r key value; do
|
||||
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
|
||||
safeValue=$(echo "$value" | tr -d '\r')
|
||||
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
log "Downloading from: $wrapperUrl"
|
||||
|
||||
if $cygwin; then
|
||||
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
log "Found wget ... using wget"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
else
|
||||
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
log "Found curl ... using curl"
|
||||
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
else
|
||||
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
else
|
||||
log "Falling back to using Java to download"
|
||||
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaSource=$(cygpath --path --windows "$javaSource")
|
||||
javaClass=$(cygpath --path --windows "$javaClass")
|
||||
fi
|
||||
if [ -e "$javaSource" ]; then
|
||||
if [ ! -e "$javaClass" ]; then
|
||||
log " - Compiling MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/javac" "$javaSource")
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
log " - Running MavenWrapperDownloader.java ..."
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
wrapperSha256Sum=""
|
||||
while IFS="=" read -r key value; do
|
||||
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
|
||||
esac
|
||||
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ -n "$wrapperSha256Sum" ]; then
|
||||
wrapperSha256Result=false
|
||||
if command -v sha256sum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
elif command -v shasum > /dev/null; then
|
||||
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
|
||||
wrapperSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
|
||||
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
|
||||
exit 1
|
||||
fi
|
||||
if [ $wrapperSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
|
||||
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
# shellcheck disable=SC2086 # safe args
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
$MAVEN_DEBUG_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
Vendored
+205
@@ -0,0 +1,205 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
|
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %WRAPPER_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
|
||||
SET WRAPPER_SHA_256_SUM=""
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
|
||||
)
|
||||
IF NOT %WRAPPER_SHA_256_SUM%=="" (
|
||||
powershell -Command "&{"^
|
||||
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
|
||||
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
|
||||
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
|
||||
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
|
||||
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
|
||||
" exit 1;"^
|
||||
"}"^
|
||||
"}"
|
||||
if ERRORLEVEL 1 goto error
|
||||
)
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% ^
|
||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
||||
%MAVEN_OPTS% ^
|
||||
%MAVEN_DEBUG_OPTS% ^
|
||||
-classpath %WRAPPER_JAR% ^
|
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
|
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
|
||||
|
||||
cmd /C exit /B %ERROR_CODE%
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>ba.unsa.etf.si</groupId>
|
||||
<artifactId>bbqms</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>bbqms</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.12.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.samstevens.totp</groupId>
|
||||
<artifactId>totp-spring-boot-starter</artifactId>
|
||||
<version>1.7.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
<artifactId>google-api-client</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>json-schema-validator</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.10.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<version>1.10.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>html2pdf</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>1.76</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf-spring5</artifactId>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
package ba.unsa.etf.si.bbqms;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableTransactionManagement
|
||||
public class BbqmsApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(BbqmsApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.UserDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface AdminService {
|
||||
List<User> findUsersByCode(final String tenantCode, final String roleNames);
|
||||
User addUser(final User admin, final String tenantCode, final String roleName) throws AuthException;
|
||||
User updateUser(final UserDto request, final String tenantCode, final Long adminId);
|
||||
void removeUser(final String tenantCode, final Long adminId);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.utils.Wrapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface BranchService extends Wrapper {
|
||||
Optional<Branch> findById(final long branchId);
|
||||
|
||||
Branch createBranch(final String name, final List<String> tellerStations, final String tenantCode) throws Exception;
|
||||
|
||||
List<Branch> getBranches(final String tenantCode);
|
||||
|
||||
Branch updateBranch(final long branchId, final String name, final List<String> tellerStations,
|
||||
final String tenantCode) throws Exception;
|
||||
|
||||
void removeBranch(final long branchId);
|
||||
|
||||
TellerStation addStation(final String name, final long branchId) throws Exception;
|
||||
|
||||
void removeStation(final long stationId) throws Exception;
|
||||
|
||||
TellerStation updateStation(final long stationId, final String name) throws Exception;
|
||||
|
||||
Set<Service> extractPossibleServices(final Branch branch);
|
||||
|
||||
Set<TellerStation> getStationsWithService(final Branch branch, final Service service);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Display;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DisplayService {
|
||||
Display createDisplay(final String name, final long branchId);
|
||||
|
||||
List<Display> getDisplays(final long branchId);
|
||||
|
||||
List<Display> getDisplaysByTenant(final String tenantCode);
|
||||
|
||||
List<Display> getUnassignedDisplaysByTenant(final String tenantCode);
|
||||
|
||||
List<Display> getUnassignedDisplaysByBranch(final long branchId);
|
||||
|
||||
Display updateDisplay(final long displayId, final String name) throws Exception;
|
||||
|
||||
void removeDisplay(final long displayId);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.BranchGroupCreateDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.BranchGroupUpdateDto;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface GroupService {
|
||||
BranchGroup get(final long id);
|
||||
List<Branch> findAssignableBranches(final long groupId, final String tenantCode);
|
||||
List<Service> findAssignableServices(final long groupId, final String tenantCode);
|
||||
BranchGroup addBranchGroup(final String tenantCode, final BranchGroupCreateDto name) throws Exception;
|
||||
BranchGroup updateBranchGroup(final long branchGroupId, final BranchGroupUpdateDto request);
|
||||
BranchGroup addBranchGroupService(final long branchGroupId, final long serviceId);
|
||||
BranchGroup addBranchGroupBranch(final long branchGroupId, final long branchId);
|
||||
BranchGroup deleteBranchGroupBranch(final long branchGroupId, final long branchId);
|
||||
BranchGroup deleteBranchGroupService(final long branchGroupId, final long serviceId);
|
||||
List<BranchGroup> getAllByTenant(final String tenantCode);
|
||||
void deleteBranchGroup(final long branchGroupId);
|
||||
Set<BranchGroup> getAllOfferingService(final Service service);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface StationService {
|
||||
List<TellerStation> getAllByTenant(final String tenantCode);
|
||||
|
||||
Set<Service> findAssignableServices(final long stationId);
|
||||
|
||||
TellerStation addTellerStationService(final long stationId, final long serviceId);
|
||||
|
||||
TellerStation deleteTellerStationService(final long stationId, final long serviceId);
|
||||
|
||||
TellerStation addTellerStationDisplay(final long stationId, final long displayId);
|
||||
|
||||
TellerStation deleteTellerStationDisplay(final long stationId, final long displayId);
|
||||
|
||||
Set<Service> getServicesByAssigned(final long stationId, final boolean assigned);
|
||||
|
||||
List<TellerStation> getAllByBranch(final long branchId);
|
||||
|
||||
Set<TellerStation> getAllOfferingService(final Service service);
|
||||
|
||||
Optional<TellerStation> findById(final long serviceId);
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.AdminService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.RoleService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.UserService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Role;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.repository.UserRepository;
|
||||
import ba.unsa.etf.si.bbqms.tenant_service.api.TenantService;
|
||||
import ba.unsa.etf.si.bbqms.tfa_service.api.TwoFactorService;
|
||||
import ba.unsa.etf.si.bbqms.utils.UserValidator;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.UserDto;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class DefaultAdminService implements AdminService {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final UserService userService;
|
||||
private final TwoFactorService twoFactorService;
|
||||
private final TenantService tenantService;
|
||||
private final RoleService roleService;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final AuthService authService;
|
||||
public DefaultAdminService(final UserRepository userRepository,
|
||||
final UserService userService,
|
||||
final TwoFactorService twoFactorService,
|
||||
final TenantService tenantService,
|
||||
final RoleService roleService,
|
||||
final PasswordEncoder passwordEncoder,
|
||||
final AuthService authService) {
|
||||
this.userRepository = userRepository;
|
||||
this.userService = userService;
|
||||
this.twoFactorService = twoFactorService;
|
||||
this.tenantService = tenantService;
|
||||
this.roleService = roleService;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> findUsersByCode(final String tenantCode, final String roleName){
|
||||
final Set<RoleName> roleNameSet = Set.of(RoleName.valueOf(roleName));
|
||||
return this.userRepository.findAllByTenant_CodeAndRoles_NameIn(tenantCode, roleNameSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User addUser(final User user, final String tenantCode, final String roleName) throws AuthException {
|
||||
if (!UserValidator.validateData(user)) {
|
||||
throw new AuthException("Invalid email/password format");
|
||||
}
|
||||
|
||||
final Optional<User> optionalExistingUser = this.userService.findByEmail(user.getEmail());
|
||||
if (optionalExistingUser.isPresent()) {
|
||||
throw new AuthException("Tried making a user that already exists");
|
||||
}
|
||||
|
||||
final Role role = this.roleService.getRoleByName(RoleName.valueOf(roleName))
|
||||
.orElseThrow(() -> new AuthException("Tried setting a role that doesn't exist"));
|
||||
final Tenant tenant = this.tenantService.findByCode(tenantCode);
|
||||
|
||||
user.setRoles(Set.of(role));
|
||||
user.setTenant(tenant);
|
||||
user.setOauth(false);
|
||||
user.setTfaSecret(this.twoFactorService.generateNewSecret());
|
||||
user.setPassword(this.passwordEncoder.encode(user.getPassword().trim()));
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User updateUser(final UserDto request, final String tenantCode, final Long adminId) {
|
||||
final User user = this.userRepository.findById(adminId)
|
||||
.orElseThrow(() -> new NoSuchElementException("User not found"));
|
||||
|
||||
if (!user.getTenant().getCode().equals(tenantCode)) {
|
||||
throw new IllegalArgumentException("Specified tenant is not associated with user");
|
||||
}
|
||||
|
||||
final Set<Role> userRoles = user.getRoles();
|
||||
Set<RoleName> roleNameSet = userRoles.stream()
|
||||
.map(Role::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (roleNameSet.stream().anyMatch(this.authService::canOnlyCRUDUser)) {
|
||||
throw new IllegalArgumentException("Only super admin can update admin");
|
||||
}
|
||||
|
||||
if (request.email() != null) {
|
||||
user.setEmail(request.email());
|
||||
}
|
||||
|
||||
if (request.phoneNumber() != null) {
|
||||
user.setPhoneNumber(request.phoneNumber());
|
||||
}
|
||||
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUser(final String tenantCode, final Long userId) {
|
||||
final User user = this.userRepository.findById(userId)
|
||||
.orElseThrow(() -> new NoSuchElementException("User not found"));
|
||||
|
||||
if (!user.getTenant().getCode().equals(tenantCode)) {
|
||||
throw new IllegalArgumentException("Specified tenant is not associated with user");
|
||||
}
|
||||
|
||||
final Set<Role> userRoles = user.getRoles();
|
||||
Set<RoleName> roleNameSet = userRoles.stream()
|
||||
.map(Role::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (roleNameSet.stream().anyMatch(this.authService::canOnlyCRUDUser)) {
|
||||
throw new IllegalArgumentException("Only super admin can remove admin");
|
||||
}
|
||||
|
||||
this.userRepository.deleteById(userId);
|
||||
}
|
||||
}
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.GroupService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.repository.BranchRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TenantRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@org.springframework.stereotype.Service
|
||||
public class DefaultBranchService implements BranchService {
|
||||
private final BranchRepository branchRepository;
|
||||
private final TenantRepository tenantRepository;
|
||||
private final TellerStationRepository tellerStationRepository;
|
||||
private final TicketRepository ticketRepository;
|
||||
private final GroupService groupService;
|
||||
|
||||
public DefaultBranchService(final BranchRepository branchRepository,
|
||||
final TenantRepository tenantRepository,
|
||||
final TellerStationRepository tellerStationRepository,
|
||||
final TicketRepository ticketRepository,
|
||||
final GroupService groupService) {
|
||||
this.branchRepository = branchRepository;
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.tellerStationRepository = tellerStationRepository;
|
||||
this.ticketRepository = ticketRepository;
|
||||
this.groupService = groupService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Branch> findById(final long branchId) {
|
||||
return this.branchRepository.findById(branchId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Branch createBranch(final String name, final List<String> tellerStations, final String tenantCode) throws Exception {
|
||||
final Tenant userTenant = this.tenantRepository.findByCode(tenantCode)
|
||||
.orElseThrow(() -> new Exception("Tenant with code: " + tenantCode + " doesn't exist."));
|
||||
|
||||
final Branch newBranch = new Branch(name, userTenant);
|
||||
|
||||
final Set<TellerStation> newTellerStations;
|
||||
if (tellerStations != null) {
|
||||
newTellerStations = tellerStations.stream()
|
||||
.map(stationName -> new TellerStation(stationName, newBranch))
|
||||
.collect(Collectors.toSet());
|
||||
} else {
|
||||
newTellerStations = null;
|
||||
}
|
||||
newBranch.setTellerStations(newTellerStations);
|
||||
|
||||
return this.branchRepository.save(newBranch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Branch> getBranches(final String tenantCode) {
|
||||
return this.branchRepository.findByTenant_Code(tenantCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Branch updateBranch(final long branchId,
|
||||
final String name,
|
||||
final List<String> newTellerStations,
|
||||
final String tenantCode) throws Exception {
|
||||
|
||||
final Branch existingBranch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new Exception("Branch with id: " + branchId + " not found."));
|
||||
|
||||
existingBranch.setName(name);
|
||||
|
||||
if (newTellerStations != null) {
|
||||
if (existingBranch.getTellerStations() != null) {
|
||||
existingBranch.getTellerStations().clear();
|
||||
}
|
||||
|
||||
final Set<TellerStation> tellerStations = newTellerStations.stream()
|
||||
.map(stationName -> new TellerStation(stationName, existingBranch))
|
||||
.collect(Collectors.toSet());
|
||||
existingBranch.getTellerStations().addAll(tellerStations);
|
||||
}
|
||||
|
||||
return this.branchRepository.save(existingBranch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBranch(final long branchId) {
|
||||
final Branch branch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Branch with id: " + branchId + " doesn't exist."));
|
||||
|
||||
final List<Ticket> ticketsToDiscard = this.ticketRepository.findByBranch_Id(branch.getId());
|
||||
|
||||
final Set<Long> ticketsToBeDiscarded = ticketsToDiscard.stream()
|
||||
.map(Ticket::getId)
|
||||
.collect(Collectors.toSet());
|
||||
this.ticketRepository.deleteAllById(ticketsToBeDiscarded);
|
||||
|
||||
for (final BranchGroup group : branch.getBranchGroups()) {
|
||||
this.groupService.deleteBranchGroupBranch(group.getId(), branch.getId());
|
||||
}
|
||||
|
||||
this.branchRepository.deleteById(branchId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TellerStation addStation(final String name, final long branchId) throws Exception {
|
||||
final Branch branch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new Exception("Branch with id: " + branchId + " not found."));
|
||||
|
||||
branch.getTellerStations().add(new TellerStation(name, branch));
|
||||
|
||||
final Branch savedBranch = this.branchRepository.save(branch);
|
||||
return savedBranch.getTellerStations().stream()
|
||||
.filter(station -> station.getName().equals(name))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new Exception("Did not add the station properly."));
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: If station is removed, and some ticket is currently on it?
|
||||
Also, if station is removed, and some tickets service is only available at that station,
|
||||
it should be discarded.
|
||||
*/
|
||||
@Override
|
||||
public void removeStation(final long stationId) throws Exception {
|
||||
this.tellerStationRepository.deleteById(stationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TellerStation updateStation(final long stationId, final String name) throws Exception {
|
||||
final TellerStation existingStation = this.tellerStationRepository.findById(stationId)
|
||||
.orElseThrow(() -> new Exception("No station with id: " + stationId));
|
||||
|
||||
existingStation.setName(name);
|
||||
return this.tellerStationRepository.save(existingStation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Service> extractPossibleServices(final Branch branch) {
|
||||
final Set<BranchGroup> groups = branch.getBranchGroups();
|
||||
final Set<Service> possibleServices = new TreeSet<>(Comparator.comparing(Service::getId));
|
||||
for (final BranchGroup group : groups) {
|
||||
possibleServices.addAll(group.getServices());
|
||||
}
|
||||
return possibleServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TellerStation> getStationsWithService(final Branch branch, final Service service) {
|
||||
return branch.getTellerStations().stream()
|
||||
.filter(station -> station.getServices().contains(service))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(final Class<T> tClass) {
|
||||
if (tClass.isAssignableFrom(BranchRepository.class)) {
|
||||
return tClass.cast(this.branchRepository);
|
||||
}
|
||||
|
||||
return BranchService.super.unwrap(tClass);
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.DisplayService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.Display;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.repository.BranchRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.DisplayRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class DefaultDisplayService implements DisplayService {
|
||||
private final DisplayRepository displayRepository;
|
||||
private final TellerStationRepository tellerStationRepository;
|
||||
private final BranchRepository branchRepository;
|
||||
|
||||
public DefaultDisplayService(final DisplayRepository displayRepository,
|
||||
final TellerStationRepository tellerStationRepository,
|
||||
final BranchRepository branchRepository) {
|
||||
this.displayRepository = displayRepository;
|
||||
this.tellerStationRepository = tellerStationRepository;
|
||||
this.branchRepository = branchRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Display createDisplay(final String name, final long branchId) {
|
||||
final Branch branch = this.branchRepository.get(branchId);
|
||||
final Display newDisplay = new Display(name, branch);
|
||||
|
||||
return this.displayRepository.save(newDisplay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Display> getDisplays(final long branchId) {
|
||||
return this.displayRepository.findByBranch_Id(branchId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Display> getDisplaysByTenant(final String tenantCode) {
|
||||
return this.displayRepository.findByBranch_Tenant_Code(tenantCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Display> getUnassignedDisplaysByTenant(final String tenantCode) {
|
||||
return this.displayRepository.findByBranch_Tenant_Code(tenantCode).stream()
|
||||
.filter(display -> display.getTellerStation() == null)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Display> getUnassignedDisplaysByBranch(final long branchId) {
|
||||
return this.displayRepository.findByBranch_Id(branchId).stream()
|
||||
.filter(display -> display.getTellerStation() == null)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Display updateDisplay(final long displayId, final String name) {
|
||||
final Display existingDisplay = displayRepository.get(displayId);
|
||||
existingDisplay.setName(name);
|
||||
|
||||
return this.displayRepository.save(existingDisplay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDisplay(final long displayId) {
|
||||
final Display display = this.displayRepository.get(displayId);
|
||||
|
||||
if (display.getTellerStation() != null) {
|
||||
final TellerStation tellerStation = display.getTellerStation();
|
||||
tellerStation.setDisplay(null);
|
||||
this.tellerStationRepository.save(tellerStation);
|
||||
}
|
||||
|
||||
this.displayRepository.deleteById(displayId);
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.GroupService;
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.StationService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.repository.BranchGroupRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.BranchRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.ServiceRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TenantRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.BranchGroupCreateDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.BranchGroupUpdateDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@org.springframework.stereotype.Service
|
||||
public class DefaultGroupService implements GroupService {
|
||||
private final StationService stationService;
|
||||
private final BranchGroupRepository branchGroupRepository;
|
||||
private final BranchRepository branchRepository;
|
||||
private final ServiceRepository serviceRepository;
|
||||
private final TenantRepository tenantRepository;
|
||||
private final TellerStationRepository stationRepository;
|
||||
private final TicketRepository ticketRepository;
|
||||
|
||||
public DefaultGroupService(final StationService stationService,
|
||||
final BranchGroupRepository branchGroupRepository,
|
||||
final BranchRepository branchRepository,
|
||||
final ServiceRepository serviceRepository,
|
||||
final TenantRepository tenantRepository,
|
||||
final TellerStationRepository stationRepository,
|
||||
final TicketRepository ticketRepository) {
|
||||
this.stationService = stationService;
|
||||
this.branchGroupRepository = branchGroupRepository;
|
||||
this.branchRepository = branchRepository;
|
||||
this.serviceRepository = serviceRepository;
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.stationRepository = stationRepository;
|
||||
this.ticketRepository = ticketRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchGroup get(final long id) {
|
||||
return this.branchGroupRepository.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Branch> findAssignableBranches(final long groupId, final String tenantCode) {
|
||||
final List<Branch> tenantBranches = this.branchRepository.findByTenant_Code(tenantCode);
|
||||
|
||||
final BranchGroup group = this.branchGroupRepository.get(groupId);
|
||||
|
||||
tenantBranches.removeAll(group.getBranches());
|
||||
|
||||
return tenantBranches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Service> findAssignableServices(final long groupId, final String tenantCode) {
|
||||
final List<Service> tenantServices = this.serviceRepository.findByTenant_Code(tenantCode);
|
||||
|
||||
final BranchGroup group = this.branchGroupRepository.get(groupId);
|
||||
|
||||
tenantServices.removeAll(group.getServices());
|
||||
|
||||
return tenantServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchGroup addBranchGroup(final String tenantCode, final BranchGroupCreateDto request) throws Exception {
|
||||
final Set<Service> services = new HashSet<>();
|
||||
final Set<Branch> branches = new HashSet<>();
|
||||
final Tenant tenant = this.tenantRepository.findByCode(tenantCode)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No tenant with code " + tenantCode));
|
||||
|
||||
if (request.serviceIds() != null) {
|
||||
for (final long serviceId : request.serviceIds()) {
|
||||
Service service = this.serviceRepository.findById(serviceId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service found with id: " + serviceId));
|
||||
services.add(service);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.branchIds() == null || request.branchIds().isEmpty()) {
|
||||
throw new Exception("Branch group must contain at least one branch");
|
||||
}
|
||||
|
||||
for (final long branchId : request.branchIds()) {
|
||||
Branch branch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service found with Id: " + branchId));
|
||||
branches.add(branch);
|
||||
}
|
||||
|
||||
final BranchGroup branchGroup = new BranchGroup(request.name(), branches, services, tenant);
|
||||
return this.branchGroupRepository.save(branchGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchGroup updateBranchGroup(final long branchGroupId, final BranchGroupUpdateDto request) throws EntityNotFoundException {
|
||||
final BranchGroup branchGroup = this.branchGroupRepository.findById(branchGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch group found with Id: " + branchGroupId));
|
||||
|
||||
if (request.name() != null && !request.name().trim().isEmpty()) {
|
||||
branchGroup.setName(request.name());
|
||||
}
|
||||
return this.branchGroupRepository.save(branchGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchGroup addBranchGroupService(final long branchGroupId, final long serviceId) throws EntityNotFoundException {
|
||||
final BranchGroup branchGroup = this.branchGroupRepository.findById(branchGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch group found with id: " + branchGroupId));
|
||||
final Service existingService = this.serviceRepository.findById(serviceId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service found with id: " + serviceId));
|
||||
|
||||
branchGroup.getServices().add(existingService);
|
||||
|
||||
return this.branchGroupRepository.save(branchGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchGroup addBranchGroupBranch(final long branchGroupId, final long branchId) throws EntityNotFoundException {
|
||||
final BranchGroup branchGroup = this.branchGroupRepository.findById(branchGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch group found with id: " + branchGroupId));
|
||||
final Branch existingBranch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch found with id: " + branchId));
|
||||
|
||||
branchGroup.getBranches().add(existingBranch);
|
||||
|
||||
return this.branchGroupRepository.save(branchGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BranchGroup> getAllByTenant(final String tenantCode) {
|
||||
return this.branchGroupRepository.findByTenant_Code(tenantCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteBranchGroup(final long branchGroupId) {
|
||||
final BranchGroup group = this.branchGroupRepository.findById(branchGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branchgroup with id: " + branchGroupId));
|
||||
|
||||
final Set<Branch> branches = group.getBranches();
|
||||
for (final Branch existingBranch : branches) {
|
||||
final Set<Service> noLongerAvailable = discardUnavailableServicesFromStations(existingBranch, group);
|
||||
|
||||
discardUnavailableTickets(noLongerAvailable, existingBranch.getId());
|
||||
|
||||
this.branchRepository.save(existingBranch);
|
||||
}
|
||||
|
||||
this.branchGroupRepository.deleteById(branchGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BranchGroup deleteBranchGroupService(final long branchGroupId, final long serviceId) throws EntityNotFoundException {
|
||||
final BranchGroup branchGroup = this.branchGroupRepository.findById(branchGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch group found with id: " + branchGroupId));
|
||||
final Service existingService = this.serviceRepository.findById(serviceId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service found with id: " + serviceId));
|
||||
|
||||
for (final TellerStation station : this.stationService.getAllOfferingService(existingService)) {
|
||||
this.stationService.deleteTellerStationService(station.getId(), serviceId);
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: After removing a service from station, for each ticket that was affected, we should check
|
||||
if there are any other stations in its corresponding branch that offer this service, and if none,
|
||||
discard the ticket. This is easier said than done. Will be implemented if time allows.
|
||||
For now, if this situation happens, the ticket will simply say it has no available stations,
|
||||
but will not be automatically discarded.
|
||||
*/
|
||||
|
||||
if (branchGroup.getServices().remove(existingService)) {
|
||||
return this.branchGroupRepository.save(branchGroup);
|
||||
}
|
||||
throw new EntityNotFoundException("Group doesn't contain service with id: " + existingService.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public BranchGroup deleteBranchGroupBranch(final long branchGroupId, final long branchId) throws EntityNotFoundException {
|
||||
final BranchGroup branchGroup = this.branchGroupRepository.findById(branchGroupId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch group found with id: " + branchGroupId));
|
||||
final Branch existingBranch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch found with id: " + branchId));
|
||||
|
||||
if (branchGroup.getBranches().remove(existingBranch)) {
|
||||
final Set<Service> noLongerAvailable = discardUnavailableServicesFromStations(existingBranch, branchGroup);
|
||||
|
||||
discardUnavailableTickets(noLongerAvailable, branchId);
|
||||
|
||||
this.branchRepository.save(existingBranch);
|
||||
return this.branchGroupRepository.save(branchGroup);
|
||||
}
|
||||
throw new EntityNotFoundException("Group doesn't contain branch with id: " + existingBranch.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BranchGroup> getAllOfferingService(final Service service) {
|
||||
return this.branchGroupRepository.findAllByServicesContains(service);
|
||||
}
|
||||
|
||||
private Set<Service> discardUnavailableServicesFromStations(final Branch branch, final BranchGroup removedGroup) {
|
||||
branch.getBranchGroups().remove(removedGroup);
|
||||
final Set<Service> noLongerAvailableServices = new HashSet<>(removedGroup.getServices());
|
||||
|
||||
for (final BranchGroup group : branch.getBranchGroups()) {
|
||||
noLongerAvailableServices.removeAll(group.getServices());
|
||||
}
|
||||
|
||||
for (final TellerStation station : branch.getTellerStations()) {
|
||||
station.getServices().removeAll(noLongerAvailableServices);
|
||||
}
|
||||
|
||||
this.stationRepository.saveAll(branch.getTellerStations());
|
||||
|
||||
return noLongerAvailableServices;
|
||||
}
|
||||
|
||||
private void discardUnavailableTickets(final Set<Service> noLongerAvailableServices, final long branchId) {
|
||||
final List<Ticket> ticketsToDiscard = this.ticketRepository.findByService_IdInAndBranch_Id(
|
||||
noLongerAvailableServices.stream().map(Service::getId).toList(),
|
||||
branchId
|
||||
);
|
||||
|
||||
final List<Long> ticketsToBeDiscarded = ticketsToDiscard.stream()
|
||||
.map(Ticket::getId)
|
||||
.toList();
|
||||
|
||||
this.ticketRepository.deleteAllById(ticketsToBeDiscarded);
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package ba.unsa.etf.si.bbqms.admin_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.StationService;
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.domain.Display;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.repository.DisplayRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.ServiceRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@org.springframework.stereotype.Service
|
||||
public class DefaultStationService implements StationService {
|
||||
private final TellerStationRepository tellerStationRepository;
|
||||
private final ServiceRepository serviceRepository;
|
||||
private final DisplayRepository displayRepository;
|
||||
|
||||
public DefaultStationService(final TellerStationRepository tellerStationRepository,
|
||||
final ServiceRepository serviceRepository,
|
||||
final DisplayRepository displayRepository) {
|
||||
this.tellerStationRepository = tellerStationRepository;
|
||||
this.serviceRepository = serviceRepository;
|
||||
this.displayRepository = displayRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TellerStation> getAllByTenant(final String tenantCode) {
|
||||
return this.tellerStationRepository.findByBranch_Tenant_Code(tenantCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Service> findAssignableServices(final long stationId) {
|
||||
final TellerStation station = this.tellerStationRepository.get(stationId);
|
||||
|
||||
final Set<BranchGroup> groups = station.getBranch().getBranchGroups();
|
||||
final Set<Service> possibleServices = new TreeSet<>(Comparator.comparing(Service::getId));
|
||||
for (final BranchGroup group : groups) {
|
||||
possibleServices.addAll(group.getServices());
|
||||
}
|
||||
|
||||
possibleServices.removeAll(station.getServices());
|
||||
|
||||
return possibleServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TellerStation addTellerStationService(final long stationId, final long serviceId) {
|
||||
final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
|
||||
final Service service = this.serviceRepository.get(serviceId);
|
||||
|
||||
tellerStation.getServices().add(service);
|
||||
|
||||
return this.tellerStationRepository.save(tellerStation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TellerStation deleteTellerStationService(final long stationId, final long serviceId) {
|
||||
final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
|
||||
final Service service = this.serviceRepository.get(serviceId);
|
||||
|
||||
if (tellerStation.getServices().remove(service)) {
|
||||
return this.tellerStationRepository.save(tellerStation);
|
||||
}
|
||||
throw new EntityNotFoundException("Teller station doesn't contain service with id: " + serviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TellerStation addTellerStationDisplay(final long stationId, final long displayId) {
|
||||
final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
|
||||
final Display display = this.displayRepository.get(displayId);
|
||||
|
||||
tellerStation.setDisplay(display);
|
||||
display.setTellerStation(tellerStation);
|
||||
this.displayRepository.save(display);
|
||||
return this.tellerStationRepository.save(tellerStation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TellerStation deleteTellerStationDisplay(final long stationId, final long displayId) {
|
||||
final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
|
||||
|
||||
if (tellerStation.getDisplay().getId() != displayId) {
|
||||
throw new RuntimeException("Display is not assigned to station: " + stationId);
|
||||
}
|
||||
|
||||
final Display display = this.displayRepository.get(displayId);
|
||||
|
||||
tellerStation.setDisplay(null);
|
||||
display.setTellerStation(null);
|
||||
this.displayRepository.save(display);
|
||||
return this.tellerStationRepository.save(tellerStation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Service> getServicesByAssigned(final long stationId, final boolean assigned) {
|
||||
final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
|
||||
|
||||
if (assigned) {
|
||||
return tellerStation.getServices();
|
||||
} else {
|
||||
final Set<Service> assignedServices = tellerStation.getServices();
|
||||
final Set<Service> allServicesForGroup = new HashSet<>();
|
||||
final Set<BranchGroup> branchGroupSet = tellerStation.getBranch().getBranchGroups();
|
||||
|
||||
for (BranchGroup branchGroup : branchGroupSet) {
|
||||
allServicesForGroup.addAll(branchGroup.getServices());
|
||||
}
|
||||
|
||||
allServicesForGroup.removeAll(assignedServices);
|
||||
return allServicesForGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TellerStation> getAllByBranch(final long branchId) {
|
||||
return this.tellerStationRepository.findByBranch_Id(branchId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TellerStation> getAllOfferingService(final Service service) {
|
||||
return this.tellerStationRepository.findAllByServicesContains(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TellerStation> findById(final long serviceId) {
|
||||
return this.tellerStationRepository.findById(serviceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AuthService {
|
||||
User registerUser(final User user) throws AuthException;
|
||||
Optional<User> loginUser(final User user);
|
||||
Optional<User> findByEmail(final String email);
|
||||
String generateUserToken(final User user);
|
||||
Optional<User> getCurrentUser();
|
||||
String generateUserQrCode(final User user);
|
||||
boolean verifyUserTfaCode(final User user, final String code);
|
||||
boolean setTfaUse(final boolean tfa) throws Exception;
|
||||
User getAuthenticatedUser();
|
||||
boolean canChangeTenant(final String tenantCode);
|
||||
boolean canOnlyCRUDUser(final RoleName roleName);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.api;
|
||||
|
||||
import jakarta.servlet.http.Cookie;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CookieService {
|
||||
Cookie generateDefaultJwtCookie(final String token);
|
||||
Optional<Cookie> extractJwtCookie(final Cookie[] cookies);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface OAuthService {
|
||||
Optional<User> authenticateUser(final String googleCredentials);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Role;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
|
||||
import javax.naming.AuthenticationException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface RoleService {
|
||||
Optional<Role> getRoleByName(final RoleName roleName);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserService extends UserDetailsService {
|
||||
User save(final User user);
|
||||
Optional<User> findByEmail(final String email);
|
||||
boolean setUserTfa(final boolean tfa) throws AuthException;
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.RoleService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.UserService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Role;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.jwt_service.api.JwtService;
|
||||
import ba.unsa.etf.si.bbqms.tenant_service.api.TenantService;
|
||||
import ba.unsa.etf.si.bbqms.tfa_service.api.TwoFactorService;
|
||||
import ba.unsa.etf.si.bbqms.utils.UserValidator;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class DefaultAuthService implements AuthService {
|
||||
@Value("${tenancy.default-code}")
|
||||
public String DEFAULT_TENANT_CODE;
|
||||
|
||||
private final UserService userService;
|
||||
private final RoleService roleService;
|
||||
private final JwtService jwtService;
|
||||
private final TwoFactorService twoFactorService;
|
||||
private final TenantService tenantService;
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public DefaultAuthService(final UserService userService,
|
||||
final RoleService roleService,
|
||||
final JwtService jwtService,
|
||||
final TwoFactorService twoFactorService,
|
||||
final TenantService tenantService,
|
||||
final AuthenticationManager authenticationManager,
|
||||
final PasswordEncoder passwordEncoder) {
|
||||
this.userService = userService;
|
||||
this.roleService = roleService;
|
||||
this.jwtService = jwtService;
|
||||
this.twoFactorService = twoFactorService;
|
||||
this.tenantService = tenantService;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User registerUser(final User user) throws AuthException {
|
||||
if (!UserValidator.validateData(user)) {
|
||||
throw new AuthException("Invalid email/password format.");
|
||||
}
|
||||
|
||||
final Optional<User> optionalExistingUser = this.userService.findByEmail(user.getEmail());
|
||||
if (optionalExistingUser.isPresent()) {
|
||||
throw new AuthException("Tried making a user that already exists.");
|
||||
}
|
||||
|
||||
final Role role = this.roleService.getRoleByName(RoleName.ROLE_SUPER_ADMIN)
|
||||
.orElseThrow(() -> new AuthException("Tried setting a role that doesn't exist."));//nek za sad svi budu super admin
|
||||
|
||||
final Tenant tenant = this.tenantService.findByCode(this.DEFAULT_TENANT_CODE);
|
||||
|
||||
user.setRoles(Set.of(role));
|
||||
user.setTenant(tenant);
|
||||
user.setOauth(false);
|
||||
user.setTfaSecret(this.twoFactorService.generateNewSecret());
|
||||
user.setPassword(this.passwordEncoder.encode(user.getPassword().trim()));
|
||||
|
||||
return userService.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> loginUser(final User userData) {
|
||||
final Authentication authentication =
|
||||
this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userData.getEmail(), userData.getPassword()));
|
||||
return this.userService.findByEmail(authentication.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findByEmail(final String email) {
|
||||
return this.userService.findByEmail(email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateUserToken(final User user) {
|
||||
return this.jwtService.generateToken(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> getCurrentUser() {
|
||||
final String currentEmail = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
return this.userService.findByEmail(currentEmail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateUserQrCode(final User user) {
|
||||
return this.twoFactorService.generateQrCodeUri(user.getTfaSecret())
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyUserTfaCode(final User user, final String code) {
|
||||
try {
|
||||
return this.twoFactorService.isCodeValid(code, user.getTfaSecret());
|
||||
} catch(final Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setTfaUse(final boolean tfa) throws Exception {
|
||||
return this.userService.setUserTfa(tfa);
|
||||
}
|
||||
|
||||
/*
|
||||
ONLY USE THIS METHOD IF 100% SURE USER IS AUTHORIZED
|
||||
*/
|
||||
@Override
|
||||
public User getAuthenticatedUser() {
|
||||
final String currentEmail = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
return this.userService.findByEmail(currentEmail)
|
||||
.orElseThrow(() -> new RuntimeException("This should never happen. You should not call this in non-authorized endpoints!"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canChangeTenant(final String tenantCode) {
|
||||
final User currentUser = getAuthenticatedUser();
|
||||
return currentUser.getTenant().getCode().equals(tenantCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOnlyCRUDUser(final RoleName roleName) {
|
||||
Set<Role> currentUsersRoles = getAuthenticatedUser().getRoles();
|
||||
|
||||
return currentUsersRoles.stream()
|
||||
.noneMatch(role -> role.getName().equals(RoleName.ROLE_SUPER_ADMIN))
|
||||
&& !roleName.equals(RoleName.ROLE_USER);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.CookieService;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class DefaultCookieService implements CookieService {
|
||||
|
||||
public String cookieJwtTitle;
|
||||
|
||||
@Override
|
||||
public Cookie generateDefaultJwtCookie(final String token) {
|
||||
Cookie jwtCookie = new Cookie(cookieJwtTitle, token);
|
||||
jwtCookie.setPath("/");
|
||||
jwtCookie.setHttpOnly(true);
|
||||
jwtCookie.setMaxAge(60*30); //in seconds, equal to 30 minutes
|
||||
|
||||
return jwtCookie;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Cookie> extractJwtCookie(final Cookie[] cookies) {
|
||||
return Arrays.stream(cookies)
|
||||
.filter(cookie -> cookie.getName().equals(cookieJwtTitle))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.RoleService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Role;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.repository.RoleRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class DefaultRoleService implements RoleService {
|
||||
private final RoleRepository roleRepository;
|
||||
|
||||
public DefaultRoleService(final RoleRepository roleRepository) {
|
||||
this.roleRepository = roleRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Role> getRoleByName(final RoleName roleName) {
|
||||
return this.roleRepository.findByName(roleName);
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.OAuthService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.RoleService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Role;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.repository.UserRepository;
|
||||
import ba.unsa.etf.si.bbqms.tenant_service.api.TenantService;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class GoogleOAuthService implements OAuthService {
|
||||
@Value("${spring.security.oauth2.client.registration.google.client-id}")
|
||||
private String GOOGLE_CLIENT_ID;
|
||||
|
||||
@Value("${tenancy.default-code}")
|
||||
private String TENANCY_DEFAULT_CODE;
|
||||
|
||||
private final UserRepository userRepository;
|
||||
private final RoleService roleService;
|
||||
private final TenantService tenantService;
|
||||
|
||||
public GoogleOAuthService(final UserRepository userRepository,
|
||||
final RoleService roleService,
|
||||
final TenantService tenantService) {
|
||||
this.userRepository = userRepository;
|
||||
this.roleService = roleService;
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> authenticateUser(final String googleCredentials) {
|
||||
try {
|
||||
final String userEmail = this.extractToken(googleCredentials);
|
||||
|
||||
final Role role = this.roleService.getRoleByName(RoleName.ROLE_SUPER_ADMIN)
|
||||
.orElseThrow(() -> new AuthException("Tried setting a role that doesn't exist."));
|
||||
|
||||
final Tenant tenant = this.tenantService.findByCode(this.TENANCY_DEFAULT_CODE);
|
||||
|
||||
final User user = this.userRepository.findByEmailEquals(userEmail)
|
||||
.orElseGet(() -> this.userRepository.save(
|
||||
User.builder()
|
||||
.email(userEmail)
|
||||
.roles(Set.of(role))
|
||||
.tenant(tenant)
|
||||
.oAuth(true)
|
||||
.build()
|
||||
));
|
||||
return Optional.of(user);
|
||||
} catch (final Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private String extractToken(final String googleToken) throws GeneralSecurityException, IOException {
|
||||
final GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), new GsonFactory())
|
||||
.setAudience(Collections.singletonList(this.GOOGLE_CLIENT_ID))
|
||||
.build();
|
||||
|
||||
final GoogleIdToken idToken = verifier.verify(googleToken);
|
||||
return idToken.getPayload().getEmail();
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package ba.unsa.etf.si.bbqms.auth_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.UserService;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.repository.UserRepository;
|
||||
import ba.unsa.etf.si.bbqms.utils.EmailValidator;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UsernamePasswordUserService implements UserService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
public UsernamePasswordUserService(final UserRepository userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User loadUserByUsername(final String email) throws UsernameNotFoundException {
|
||||
if (!EmailValidator.validate(email)) {
|
||||
throw new UsernameNotFoundException("Invalid email format");
|
||||
}
|
||||
|
||||
return this.userRepository.findByEmailEquals(email)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("No user with email: " + email + " found"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public User save(final User user) {
|
||||
return this.userRepository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<User> findByEmail(final String email) {
|
||||
return this.userRepository.findByEmailEqualsAndOauthEquals(email, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setUserTfa(final boolean tfa) throws AuthException {
|
||||
final String currentEmail = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
final User currentUser = this.findByEmail(currentEmail)
|
||||
.orElseThrow(() -> new AuthException("User is not logged in."));
|
||||
currentUser.setTfa(tfa);
|
||||
return this.userRepository.save(currentUser).isTfa();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ba.unsa.etf.si.bbqms.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class RestRequestConfig {
|
||||
@Bean
|
||||
public RestTemplate restTemplate(final List<HttpMessageConverter<?>> messageConverters) {
|
||||
return new RestTemplate(messageConverters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
|
||||
return new ByteArrayHttpMessageConverter();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ba.unsa.etf.si.bbqms.config;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.UserService;
|
||||
import ba.unsa.etf.si.bbqms.jwt_service.config.JwtAuthorizationFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
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.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity
|
||||
public class SecurityConfig {
|
||||
private final UserService userService;
|
||||
private final JwtAuthorizationFilter jwtAuthorizationFilter;
|
||||
|
||||
public SecurityConfig(final UserService userService,
|
||||
final JwtAuthorizationFilter jwtAuthorizationFilter) {
|
||||
this.userService = userService;
|
||||
this.jwtAuthorizationFilter = jwtAuthorizationFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager(final HttpSecurity http, final PasswordEncoder passwordEncoder) throws Exception {
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
|
||||
authenticationManagerBuilder.userDetailsService(userService).passwordEncoder(passwordEncoder);
|
||||
|
||||
return authenticationManagerBuilder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
|
||||
return http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()))
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
|
||||
.sessionManagement(sessions -> sessions.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.setAllowedOrigins(List.of("*"));
|
||||
corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
|
||||
corsConfiguration.setAllowedHeaders(List.of("*"));
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", corsConfiguration);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "branch")
|
||||
public class Branch {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tenant_id", referencedColumnName = "id")
|
||||
private Tenant tenant;
|
||||
|
||||
@OneToMany(mappedBy = "branch", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<TellerStation> tellerStations;
|
||||
|
||||
@ManyToMany(mappedBy = "branches")
|
||||
private Set<BranchGroup> branchGroups;
|
||||
|
||||
public Branch() {
|
||||
}
|
||||
|
||||
public Branch(final String name, final Tenant tenant, final Set<TellerStation> tellerStations) {
|
||||
this.name = name;
|
||||
this.tenant = tenant;
|
||||
this.tellerStations = tellerStations;
|
||||
}
|
||||
|
||||
public Branch(final String name, final Tenant tenant) {
|
||||
this(name, tenant, null);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Tenant getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(final Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
public Set<TellerStation> getTellerStations() {
|
||||
return tellerStations;
|
||||
}
|
||||
|
||||
public void setTellerStations(final Set<TellerStation> tellerStations) {
|
||||
this.tellerStations = tellerStations;
|
||||
}
|
||||
|
||||
public Set<BranchGroup> getBranchGroups() {
|
||||
return branchGroups;
|
||||
}
|
||||
|
||||
public void setBranchGroups(final Set<BranchGroup> branchGroups) {
|
||||
this.branchGroups = branchGroups;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "branch_group")
|
||||
public class BranchGroup {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "branch_group_branch",
|
||||
joinColumns = @JoinColumn(name = "branch_group_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "branch_id")
|
||||
)
|
||||
private Set<Branch> branches;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "branch_group_service",
|
||||
joinColumns = @JoinColumn(name = "branch_group_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "service_id")
|
||||
)
|
||||
private Set<Service> services;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tenant_id")
|
||||
private Tenant tenant;
|
||||
|
||||
public BranchGroup() {
|
||||
}
|
||||
|
||||
public BranchGroup(final String name, final Set<Branch> branches, final Set<Service> services, final Tenant tenant) {
|
||||
this.name = name;
|
||||
this.branches = branches;
|
||||
this.services = services;
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<Branch> getBranches() {
|
||||
return branches;
|
||||
}
|
||||
|
||||
public void setBranches(final Set<Branch> branches) {
|
||||
this.branches = branches;
|
||||
}
|
||||
|
||||
public Set<Service> getServices() {
|
||||
return services;
|
||||
}
|
||||
|
||||
public void setServices(final Set<Service> services) {
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
public Tenant getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(final Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "display")
|
||||
public class Display {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
|
||||
@OneToOne(mappedBy = "display")
|
||||
private TellerStation tellerStation;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "branch_id")
|
||||
private Branch branch;
|
||||
|
||||
public Display(){
|
||||
}
|
||||
|
||||
public Display(final String name, final TellerStation tellerStation, final Branch branch) {
|
||||
this.name = name;
|
||||
this.tellerStation = tellerStation;
|
||||
this.branch = branch;
|
||||
}
|
||||
|
||||
public Display(final String name, final Branch branch) { this(name, null, branch); }
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public TellerStation getTellerStation() {
|
||||
return tellerStation;
|
||||
}
|
||||
|
||||
public void setTellerStation(final TellerStation tellerStation) {
|
||||
this.tellerStation = tellerStation;
|
||||
}
|
||||
|
||||
public Branch getBranch() {
|
||||
return branch;
|
||||
}
|
||||
|
||||
public void setBranch(final Branch branch) {
|
||||
this.branch = branch;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "role")
|
||||
public class Role {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
@Enumerated(EnumType.STRING)
|
||||
private RoleName name;
|
||||
|
||||
public Role(final long id, final RoleName name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Role(final RoleName name) {
|
||||
this(-1, name);
|
||||
}
|
||||
|
||||
public Role() {
|
||||
}
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public RoleName getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final RoleName name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
public enum RoleName {
|
||||
ROLE_SUPER_ADMIN,
|
||||
ROLE_BRANCH_ADMIN,
|
||||
ROLE_STAFF_ADMIN,
|
||||
ROLE_USER
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "service")
|
||||
public class Service {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tenant_id", referencedColumnName = "id")
|
||||
private Tenant tenant;
|
||||
|
||||
public Service() {
|
||||
}
|
||||
public Service(final String name, final Tenant tenant) {
|
||||
this.name = name;
|
||||
this.tenant = tenant;
|
||||
}
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Tenant getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(final Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "teller_station")
|
||||
public class TellerStation {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String name;
|
||||
private boolean active;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "branch_id", referencedColumnName = "id")
|
||||
private Branch branch;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "display_id", referencedColumnName = "id")
|
||||
private Display display;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "teller_station_service",
|
||||
joinColumns = @JoinColumn(name = "teller_station_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "service_id")
|
||||
)
|
||||
private Set<Service> services;
|
||||
|
||||
public TellerStation() {
|
||||
}
|
||||
|
||||
public TellerStation(final String name, final boolean active, final Branch branch, final Display display) {
|
||||
this.name = name;
|
||||
this.active = active;
|
||||
this.branch = branch;
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
public TellerStation(final String name, final Branch branch, final Display display) {
|
||||
this(name, true, branch, display);
|
||||
}
|
||||
|
||||
public TellerStation(final String name, final Branch branch) {
|
||||
this(name, branch, null);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public void setActive(final boolean active) {
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
public Branch getBranch() {
|
||||
return branch;
|
||||
}
|
||||
|
||||
public void setBranch(final Branch branch) {
|
||||
this.branch = branch;
|
||||
}
|
||||
|
||||
public Display getDisplay() {
|
||||
return display;
|
||||
}
|
||||
|
||||
public void setDisplay(final Display display) {
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
public Set<Service> getServices() {
|
||||
return services;
|
||||
}
|
||||
|
||||
public void setServices(final Set<Service> services) {
|
||||
this.services = services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "tenant")
|
||||
public class Tenant {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "code")
|
||||
private String code;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "hq_address")
|
||||
private String hqAddress;
|
||||
|
||||
@Column(name = "font")
|
||||
private String font;
|
||||
|
||||
@Column(name = "welcome_message")
|
||||
private String welcomeMessage;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "logo_id", referencedColumnName = "id")
|
||||
private TenantLogo logo;
|
||||
|
||||
@OneToMany(mappedBy = "tenant")
|
||||
private Set<Service> service;
|
||||
|
||||
@OneToMany(mappedBy = "tenant")
|
||||
private Set<Branch> branches;
|
||||
|
||||
public Tenant() {
|
||||
}
|
||||
|
||||
public Tenant(final String code,
|
||||
final String name,
|
||||
final String hqAddress,
|
||||
final String font,
|
||||
final String welcomeMessage,
|
||||
final TenantLogo logo) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.hqAddress = hqAddress;
|
||||
this.font = font;
|
||||
this.welcomeMessage = welcomeMessage;
|
||||
this.logo = logo;
|
||||
}
|
||||
|
||||
public Tenant(final String code,
|
||||
final String name,
|
||||
final String hqAddress,
|
||||
final String font,
|
||||
final String welcomeMessage) {
|
||||
this(code, name, hqAddress, font, welcomeMessage, null);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(final String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getHqAddress() {
|
||||
return hqAddress;
|
||||
}
|
||||
|
||||
public void setHqAddress(final String hqAddress) {
|
||||
this.hqAddress = hqAddress;
|
||||
}
|
||||
|
||||
public String getFont() {
|
||||
return font;
|
||||
}
|
||||
|
||||
public void setFont(final String font) {
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
public String getWelcomeMessage() {
|
||||
return welcomeMessage;
|
||||
}
|
||||
|
||||
public void setWelcomeMessage(final String welcomeMessage) {
|
||||
this.welcomeMessage = welcomeMessage;
|
||||
}
|
||||
|
||||
public TenantLogo getLogo() {
|
||||
return logo;
|
||||
}
|
||||
|
||||
public void setLogo(final TenantLogo logo) {
|
||||
this.logo = logo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tenant{" +
|
||||
"id=" + id +
|
||||
", code='" + code + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", hq_address='" + hqAddress + '\'' +
|
||||
", font='" + font + '\'' +
|
||||
", welcomeMessage='" + welcomeMessage + '\'' +
|
||||
", logo=" + logo +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
public class TenantLogo {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private Long id;
|
||||
|
||||
@Column(name = "base64_logo")
|
||||
private String base64Logo;
|
||||
|
||||
public TenantLogo() {
|
||||
}
|
||||
|
||||
public TenantLogo(final String base64Logo) {
|
||||
this.base64Logo = base64Logo;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getBase64Logo() {
|
||||
return base64Logo;
|
||||
}
|
||||
|
||||
public void setBase64Logo(final String base64Logo) {
|
||||
this.base64Logo = base64Logo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TenantLogo{" +
|
||||
"id=" + id +
|
||||
", base64Logo='" + base64Logo +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Entity
|
||||
@Table(name = "ticket")
|
||||
public class Ticket {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String number;
|
||||
private Instant createdAt;
|
||||
private String deviceToken;
|
||||
private boolean deleted;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "service_id", referencedColumnName = "id")
|
||||
private Service service;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "branch_id", referencedColumnName = "id")
|
||||
private Branch branch;
|
||||
|
||||
/**
|
||||
* Represents the teller station to which the ticket is currently assigned, if assigned at all.
|
||||
*/
|
||||
@OneToOne
|
||||
@JoinColumn(name = "teller_station_id", referencedColumnName = "id")
|
||||
private TellerStation tellerStation;
|
||||
|
||||
public Ticket() {
|
||||
}
|
||||
|
||||
public Ticket(final String number, final Instant createdAt, final String deviceToken, final boolean deleted, final Service service, final Branch branch, final TellerStation tellerStation) {
|
||||
this.number = number;
|
||||
this.createdAt = createdAt;
|
||||
this.deviceToken = deviceToken;
|
||||
this.deleted = deleted;
|
||||
this.service = service;
|
||||
this.branch = branch;
|
||||
this.tellerStation = tellerStation;
|
||||
}
|
||||
|
||||
public Ticket(final String number, final Instant createdAt, final String deviceToken, final Service service, final Branch branch) {
|
||||
this(number, createdAt, deviceToken, false, service, branch, null);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(final String number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(final Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public String getDeviceToken() {
|
||||
return deviceToken;
|
||||
}
|
||||
|
||||
public void setDeviceToken(final String deviceToken) {
|
||||
this.deviceToken = deviceToken;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(final boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public Service getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
public void setService(final Service service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public Branch getBranch() {
|
||||
return branch;
|
||||
}
|
||||
|
||||
public void setBranch(final Branch branch) {
|
||||
this.branch = branch;
|
||||
}
|
||||
|
||||
public TellerStation getTellerStation() {
|
||||
return tellerStation;
|
||||
}
|
||||
|
||||
public void setTellerStation(final TellerStation tellerStation) {
|
||||
this.tellerStation = tellerStation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package ba.unsa.etf.si.bbqms.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.CascadeType;
|
||||
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.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user")
|
||||
public class User implements UserDetails {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
private String email;
|
||||
|
||||
@JsonIgnore
|
||||
private String password;
|
||||
private String phoneNumber;
|
||||
private boolean oauth;
|
||||
private boolean tfa;
|
||||
private String tfaSecret;
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(name = "user_role",
|
||||
joinColumns = @JoinColumn(name = "user_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "role_id"))
|
||||
private Set<Role> roles;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tenant_id")
|
||||
private Tenant tenant;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(final String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public void setPassword(final String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
public void setPhoneNumber(final String phoneNumber) {
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
public boolean isOauth() {
|
||||
return oauth;
|
||||
}
|
||||
|
||||
public void setOauth(final boolean oauth) {
|
||||
this.oauth = oauth;
|
||||
}
|
||||
|
||||
public boolean isTfa() {
|
||||
return tfa;
|
||||
}
|
||||
|
||||
public void setTfa(final boolean tfa) {
|
||||
this.tfa = tfa;
|
||||
}
|
||||
|
||||
public String getTfaSecret() {
|
||||
return tfaSecret;
|
||||
}
|
||||
|
||||
public void setTfaSecret(final String tfaSecret) {
|
||||
this.tfaSecret = tfaSecret;
|
||||
}
|
||||
|
||||
public Set<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(final Set<Role> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public Tenant getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setTenant(final Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return roles.stream()
|
||||
.map(role -> new SimpleGrantedAuthority(role.getName().name()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static UserBuilder builder() {
|
||||
return new UserBuilder();
|
||||
}
|
||||
|
||||
public static class UserBuilder {
|
||||
final User user = new User();
|
||||
|
||||
public UserBuilder id(final long id) {
|
||||
this.user.setId(id);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder email(final String email) {
|
||||
this.user.setEmail(email);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder password(final String password) {
|
||||
this.user.setPassword(password);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder phoneNumber(final String phoneNumber) {
|
||||
this.user.setPhoneNumber(phoneNumber);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder oAuth(final boolean oauth) {
|
||||
this.user.setOauth(oauth);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder tfa(final boolean tfa) {
|
||||
this.user.setTfa(tfa);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder tfaSecret(final String tfaSecret) {
|
||||
this.user.setTfaSecret(tfaSecret);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder roles(final Set<Role> roles) {
|
||||
this.user.setRoles(roles);
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder tenant(final Tenant tenant) {
|
||||
this.user.setTenant(tenant);
|
||||
return this;
|
||||
}
|
||||
|
||||
public User build() {
|
||||
return this.user;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ba.unsa.etf.si.bbqms.exceptions;
|
||||
|
||||
public class AuthException extends Exception {
|
||||
public AuthException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ba.unsa.etf.si.bbqms.export.api;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
public interface Exporter<T> {
|
||||
Resource exportPdf(T entity);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ba.unsa.etf.si.bbqms.export.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.thymeleaf.spring5.SpringTemplateEngine;
|
||||
import org.thymeleaf.templatemode.TemplateMode;
|
||||
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
|
||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||
|
||||
@Configuration
|
||||
public class ThymeLeafConfig {
|
||||
|
||||
@Bean
|
||||
public SpringTemplateEngine springTemplateEngine(final ITemplateResolver templateResolver) {
|
||||
final SpringTemplateEngine engine = new SpringTemplateEngine();
|
||||
engine.setTemplateResolver(templateResolver);
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ITemplateResolver templateResolver() {
|
||||
final ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
|
||||
resolver.setPrefix("templates/");
|
||||
resolver.setSuffix(".html");
|
||||
resolver.setTemplateMode(TemplateMode.HTML);
|
||||
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package ba.unsa.etf.si.bbqms.export.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.export.api.Exporter;
|
||||
import ba.unsa.etf.si.bbqms.utils.DateUtils;
|
||||
import com.itextpdf.html2pdf.HtmlConverter;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.thymeleaf.spring5.SpringTemplateEngine;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class TicketToPdfExporter implements Exporter<Ticket> {
|
||||
private final SpringTemplateEngine templateEngine;
|
||||
private final BranchService branchService;
|
||||
|
||||
public TicketToPdfExporter(final SpringTemplateEngine templateEngine, final BranchService branchService) {
|
||||
this.templateEngine = templateEngine;
|
||||
this.branchService = branchService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource exportPdf(final Ticket entity) {
|
||||
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
final Context context = new Context();
|
||||
context.setVariable("ticketTitle", "Ticket " + entity.getNumber());
|
||||
context.setVariable("ticketNumber", entity.getNumber());
|
||||
context.setVariable("ticketCreatedAt", DateUtils.getHourMinute(entity.getCreatedAt()));
|
||||
|
||||
final Set<TellerStation> availableStations = extractPossibleStations(entity);
|
||||
context.setVariable("anyAvailableStations", !availableStations.isEmpty());
|
||||
final String availableStationsText = availableStations.stream()
|
||||
.reduce("", (rest, nextStation) -> {
|
||||
if (rest.isEmpty()) {
|
||||
return nextStation.getName();
|
||||
} else {
|
||||
return rest + ", " + nextStation.getName();
|
||||
}
|
||||
}, String::concat);
|
||||
context.setVariable("ticketPossibleStations", availableStationsText);
|
||||
|
||||
|
||||
final String printHtml = this.templateEngine.process("ticket-template", context);
|
||||
HtmlConverter.convertToPdf(printHtml, outputStream);
|
||||
return new ByteArrayResource(outputStream.toByteArray());
|
||||
}
|
||||
|
||||
private Set<TellerStation> extractPossibleStations(final Ticket ticket) {
|
||||
return this.branchService.getStationsWithService(ticket.getBranch(), ticket.getService());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ba.unsa.etf.si.bbqms.jwt_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface JwtService {
|
||||
String generateToken(final User user);
|
||||
|
||||
Claims resolveClaims(final String token);
|
||||
|
||||
boolean isExpired(final Claims claims);
|
||||
|
||||
Set<GrantedAuthority> getAuthoritiesFromClaims(final Claims claims);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package ba.unsa.etf.si.bbqms.jwt_service.config;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.CookieService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.UserService;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.jwt_service.api.JwtService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class JwtAuthorizationFilter extends OncePerRequestFilter {
|
||||
@Value("${jwt.header-title}")
|
||||
public String AUTH_HEADER_TITLE;
|
||||
|
||||
@Value("${jwt.token-prefix}")
|
||||
public String TOKEN_PREFIX;
|
||||
|
||||
private final JwtService jwtService;
|
||||
private final UserService userService;
|
||||
|
||||
public JwtAuthorizationFilter(final JwtService jwtService,
|
||||
final UserService userService) {
|
||||
this.jwtService = jwtService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(final HttpServletRequest request,
|
||||
final HttpServletResponse response,
|
||||
final FilterChain filterChain) throws ServletException, IOException {
|
||||
try {
|
||||
final String bearerToken = request.getHeader(this.AUTH_HEADER_TITLE);
|
||||
if (bearerToken == null) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
final String token = bearerToken.replace("Bearer ", "");
|
||||
|
||||
final Claims claims = jwtService.resolveClaims(token);
|
||||
if (claims != null && !jwtService.isExpired(claims)) {
|
||||
final String username = claims.getSubject();
|
||||
final Set<GrantedAuthority> userAuthorities = jwtService.getAuthoritiesFromClaims(claims);
|
||||
|
||||
final User currentUser = this.userService.findByEmail(username)
|
||||
.orElseThrow(() -> new AuthException("No user with username " + username + " found."));
|
||||
|
||||
final Authentication authentication =
|
||||
new UsernamePasswordAuthenticationToken(username, "", userAuthorities);
|
||||
|
||||
final String newToken = this.jwtService.generateToken(currentUser);
|
||||
response.addHeader("Access-Control-Expose-Headers", "Auth-Token");
|
||||
response.addHeader("Auth-Token", newToken);
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
} catch (final Exception exception) {
|
||||
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package ba.unsa.etf.si.bbqms.jwt_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.jwt_service.api.JwtService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtParser;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class DefaultJwtService implements JwtService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultJwtService.class);
|
||||
|
||||
@Value("${jwt.authorities-key}")
|
||||
public String AUTHORITIES_KEY;
|
||||
|
||||
@Value("${jwt.secret-key}")
|
||||
public String SECRET_KEY;
|
||||
|
||||
@Value("${jwt.token-validity-time}")
|
||||
public Duration TOKEN_VALIDITY_TIME;
|
||||
|
||||
@Override
|
||||
public String generateToken(final User user) {
|
||||
try{
|
||||
final String authorities = user.getRoles().stream()
|
||||
.map(role -> role.getName().name())
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
return Jwts.builder()
|
||||
.subject(user.getEmail())
|
||||
.claim(this.AUTHORITIES_KEY, authorities)
|
||||
.issuedAt(Date.from(Instant.now()))
|
||||
.expiration(Date.from(Instant.now().plus(this.TOKEN_VALIDITY_TIME)))
|
||||
.signWith(getSignInKey())
|
||||
.compact();
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Claims resolveClaims(final String token) {
|
||||
return Jwts.parser()
|
||||
.verifyWith((SecretKey) getSignInKey())
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired(final Claims claims) {
|
||||
final Instant expiration = claims.getExpiration().toInstant();
|
||||
return expiration.isBefore(Instant.now());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<GrantedAuthority> getAuthoritiesFromClaims(final Claims claims) {
|
||||
return Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Key getSignInKey(){
|
||||
byte[] keyBytes = this.SECRET_KEY.getBytes(StandardCharsets.UTF_8);
|
||||
return Keys.hmacShaKeyFor(keyBytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package ba.unsa.etf.si.bbqms.notification_service.api;
|
||||
|
||||
public record Notification(String recipient, String title, String body) {
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package ba.unsa.etf.si.bbqms.notification_service.api;
|
||||
|
||||
public interface NotificationService {
|
||||
boolean sendNotification(final Notification notification);
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package ba.unsa.etf.si.bbqms.notification_service.config;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.NotificationService;
|
||||
import ba.unsa.etf.si.bbqms.notification_service.implementation.ExpoNotificationService;
|
||||
import ba.unsa.etf.si.bbqms.notification_service.implementation.MockNotificationService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class NotificationConfiguration {
|
||||
@Value("${notifications.expo-url}")
|
||||
public String expoPushUrl;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "notifications", name = "mock", havingValue = "false")
|
||||
public NotificationService notificationService(final RestTemplate restTemplate) {
|
||||
return new ExpoNotificationService(this.expoPushUrl, restTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "notifications", name = "mock", havingValue = "true", matchIfMissing = true)
|
||||
public NotificationService mockNotificationService() {
|
||||
return new MockNotificationService();
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package ba.unsa.etf.si.bbqms.notification_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.Notification;
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.NotificationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
public class ExpoNotificationService implements NotificationService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ExpoNotificationService.class);
|
||||
private final String expoPushUrl;
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public ExpoNotificationService(final String expoPushUrl, final RestTemplate restTemplate) {
|
||||
this.expoPushUrl = expoPushUrl;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendNotification(final Notification notification) {
|
||||
try {
|
||||
final ExpoResponse response = this.restTemplate.postForObject(
|
||||
expoPushUrl,
|
||||
new ExpoNotification(notification.recipient(), notification.title(), notification.body()),
|
||||
ExpoResponse.class);
|
||||
|
||||
if (response.data().status().equals("ok")) {
|
||||
logger.info("Successfully sent notification to recepient: " + notification.recipient());
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("Could not send notification to recepient: " + notification.recipient());
|
||||
return false;
|
||||
}
|
||||
} catch (final Exception exception) {
|
||||
logger.warn("Could not send notification to recepient: " + notification.recipient());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public record ExpoNotification(String to, String title, String body) {
|
||||
}
|
||||
|
||||
public record ExpoResponse(ExpoData data) {
|
||||
public record ExpoData(String status, String id) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package ba.unsa.etf.si.bbqms.notification_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.Notification;
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.NotificationService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MockNotificationService implements NotificationService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MockNotificationService.class);
|
||||
|
||||
@Override
|
||||
public boolean sendNotification(final Notification notification) {
|
||||
logger.info("Pretending to send a notification to recipient: " + notification.recipient());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ba.unsa.etf.si.bbqms.queue_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface QueueService {
|
||||
Optional<Ticket> findNextTicketForStation(final TellerStation tellerStation);
|
||||
|
||||
Optional<Ticket> advanceQueueState(final TellerStation tellerStation);
|
||||
|
||||
Optional<Ticket> findCurrentTicketForStation(final TellerStation tellerStation);
|
||||
|
||||
/**
|
||||
* Current ticket on the station is undo-ed (effectively teller station foreign key returned to null).
|
||||
* The last ticket which was assigned to the station is then returned (if there is one).
|
||||
* @param stationId - station whose state we want to return to previous
|
||||
* @return - the ticket which is now assigned to the station (if any)
|
||||
*/
|
||||
Optional<Ticket> undoStationState(final long stationId);
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package ba.unsa.etf.si.bbqms.queue_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.Notification;
|
||||
import ba.unsa.etf.si.bbqms.notification_service.api.NotificationService;
|
||||
import ba.unsa.etf.si.bbqms.queue_service.api.QueueService;
|
||||
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@org.springframework.stereotype.Service
|
||||
public class DefaultQueueService implements QueueService {
|
||||
private final TicketRepository ticketRepository;
|
||||
private final TellerStationRepository tellerStationRepository;
|
||||
private final NotificationService notificationService;
|
||||
|
||||
public DefaultQueueService(final TicketRepository ticketRepository,
|
||||
final TellerStationRepository tellerStationRepository,
|
||||
final NotificationService notificationService) {
|
||||
this.ticketRepository = ticketRepository;
|
||||
this.tellerStationRepository = tellerStationRepository;
|
||||
this.notificationService = notificationService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Ticket> findNextTicketForStation(final TellerStation tellerStation) {
|
||||
final List<Long> serviceIds = tellerStation.getServices().stream().map(Service::getId).toList();
|
||||
final List<Ticket> tickets = this.ticketRepository
|
||||
.findByService_IdInAndBranch_IdAndTellerStationIsNullAndDeletedFalseOrderByCreatedAtAsc(
|
||||
serviceIds,
|
||||
tellerStation.getBranch().getId()
|
||||
);
|
||||
|
||||
return tickets.stream().findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Optional<Ticket> advanceQueueState(final TellerStation tellerStation) {
|
||||
final Optional<Ticket> optionalTicketAtStation =
|
||||
this.ticketRepository.findByTellerStation_IdAndDeletedFalse(tellerStation.getId());
|
||||
|
||||
if (optionalTicketAtStation.isPresent()) {
|
||||
final Ticket ticketAtStation = optionalTicketAtStation.get();
|
||||
ticketAtStation.setDeleted(true);
|
||||
ticketRepository.save(ticketAtStation);
|
||||
}
|
||||
|
||||
final Optional<Ticket> optionalNextTicket = findNextTicketForStation(tellerStation);
|
||||
if (optionalNextTicket.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final Ticket nextTicket = optionalNextTicket.get();
|
||||
nextTicket.setTellerStation(tellerStation);
|
||||
|
||||
final Notification newNotification = new Notification(
|
||||
nextTicket.getDeviceToken(),
|
||||
"It's your turn!",
|
||||
"Please approach station: " + nextTicket.getTellerStation().getName()
|
||||
);
|
||||
this.notificationService.sendNotification(newNotification);
|
||||
|
||||
return Optional.of(this.ticketRepository.save(nextTicket));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Ticket> findCurrentTicketForStation(final TellerStation tellerStation) {
|
||||
return this.ticketRepository.findByTellerStation_IdAndDeletedFalse(tellerStation.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Ticket> undoStationState(final long stationId) {
|
||||
final TellerStation station = this.tellerStationRepository.get(stationId);
|
||||
|
||||
final Optional<Ticket> optionalCurrentlyAssigned = findCurrentTicketForStation(station);
|
||||
|
||||
if (optionalCurrentlyAssigned.isEmpty()) {
|
||||
throw new IllegalStateException("Detected attempt to undo station state to previous ticket while there is no current ticket. Nothing to undo.");
|
||||
}
|
||||
|
||||
final Ticket currentlyAssigned = optionalCurrentlyAssigned.get();
|
||||
|
||||
currentlyAssigned.setTellerStation(null);
|
||||
this.ticketRepository.save(currentlyAssigned);
|
||||
|
||||
final List<Ticket> previousTickets = this.ticketRepository
|
||||
.findByTellerStation_IdAndDeletedTrueAndCreatedAtLessThanOrderByCreatedAtDesc(
|
||||
station.getId(),
|
||||
currentlyAssigned.getCreatedAt()
|
||||
);
|
||||
|
||||
final Optional<Ticket> optionalPreviousTicket = previousTickets.stream().findFirst();
|
||||
|
||||
if (optionalPreviousTicket.isPresent()) {
|
||||
final Ticket previousTicket = optionalPreviousTicket.get();
|
||||
|
||||
previousTicket.setDeleted(false);
|
||||
previousTicket.setTellerStation(station);
|
||||
|
||||
this.ticketRepository.save(previousTicket);
|
||||
return Optional.of(previousTicket);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ba.unsa.etf.si.bbqms.report_service.api;
|
||||
|
||||
public interface ReportService {
|
||||
void generateReport();
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package ba.unsa.etf.si.bbqms.report_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.report_service.api.ReportService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class DefaultReportService implements ReportService {
|
||||
@Override
|
||||
public void generateReport() {
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
|
||||
@NoRepositoryBean
|
||||
public interface BaseRepository<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
|
||||
default T get(final ID id) {
|
||||
return findById(id).orElseThrow(() -> new EntityNotFoundException("Entity not found with id: " + id));
|
||||
}
|
||||
|
||||
default T get(final Specification<T> specification) {
|
||||
return findOne(specification).orElseThrow(() -> new EntityNotFoundException("Entity not found"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface BranchGroupRepository extends BaseRepository<BranchGroup, Long> {
|
||||
Set<BranchGroup> findAllByServicesContains(Service service);
|
||||
|
||||
List<BranchGroup> findByTenant_Code(String tenantCode);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface BranchRepository extends BaseRepository<Branch, Long> {
|
||||
List<Branch> findByTenant_Code(String tenantCode);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Display;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DisplayRepository extends BaseRepository<Display, Long> {
|
||||
List<Display> findByBranch_Id(long branchId);
|
||||
|
||||
List<Display> findByBranch_Tenant_Code(String tenantCode);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Role;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RoleRepository extends JpaRepository<Role, Long> {
|
||||
Optional<Role> findByName(final RoleName name);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface ServiceRepository extends BaseRepository<Service, Long> {
|
||||
List<Service> findByTenant_Code(String tenantCode);
|
||||
|
||||
List<Service> findByIdIn(Collection<Long> ids);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface TellerStationRepository extends BaseRepository<TellerStation, Long> {
|
||||
Set<TellerStation> findAllByServicesContains(Service service);
|
||||
|
||||
List<TellerStation> findByBranch_Tenant_Code(String tenantCode);
|
||||
|
||||
List<TellerStation> findByBranch_Id(long branchId);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.TenantLogo;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface TenantLogoRepository extends JpaRepository<TenantLogo,Long> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TenantRepository extends JpaRepository<Tenant, Long> {
|
||||
Optional<Tenant> findByCode(final String code);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TicketRepository extends BaseRepository<Ticket, Long> {
|
||||
List<Ticket> findByDeviceToken(String deviceToken);
|
||||
|
||||
List<Ticket> findByBranch_Id(long branchId);
|
||||
|
||||
List<Ticket> findByService_Id(long serviceId);
|
||||
|
||||
List<Ticket> findByService_IdInAndBranch_Id(Collection<Long> serviceIds, long branchId);
|
||||
|
||||
List<Ticket> findByService_IdInAndBranch_IdAndDeletedOrderByCreatedAtAsc(
|
||||
Collection<Long> serviceIds, long branchId, boolean deleted);
|
||||
|
||||
List<Ticket> findByService_IdInAndBranch_IdAndTellerStationIsNullAndDeletedFalseOrderByCreatedAtAsc(
|
||||
Collection<Long> serviceIds, long branchId);
|
||||
|
||||
Optional<Ticket> findByTellerStation_IdAndDeletedFalse(long stationId);
|
||||
|
||||
List<Ticket> findByTellerStation_IdAndDeletedTrueAndCreatedAtLessThanOrderByCreatedAtDesc(
|
||||
long stationId, Instant createdAt);
|
||||
|
||||
List<Ticket> findByTellerStationIsNullOrderByCreatedAtAsc();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ba.unsa.etf.si.bbqms.repository;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByEmailEqualsAndOauthEquals(final String email, final boolean isOauth);
|
||||
Optional<User> findByEmailEquals(final String email);
|
||||
List<User> findAllByTenant_CodeAndRoles_NameIn(final String tenant_code, final Collection<RoleName> roles);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package ba.unsa.etf.si.bbqms.repository.specs;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
|
||||
public final class TicketSpecs {
|
||||
private TicketSpecs() {
|
||||
}
|
||||
|
||||
public static Specification<Ticket> branchIdEquals(final long branchId) {
|
||||
return (root, query, cb) -> cb.equal(root.get("branch").get("id"), branchId);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> serviceIdEquals(final Long serviceId) {
|
||||
return (root, query, cb) -> cb.equal(root.get("service").get("id"), serviceId);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> serviceIdIn(final Collection<Long> serviceIds) {
|
||||
return (root, query, cb) -> root.get("service").get("id").in(serviceIds);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> tellerStationIdEquals(final Long stationId) {
|
||||
if (stationId == null) {
|
||||
return (root, query, cb) -> cb.isNull(root.get("tellerStation"));
|
||||
}
|
||||
return (root, query, cb) -> cb.equal(root.get("tellerStation").get("id"), stationId);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> deletedEquals(final boolean deleted) {
|
||||
return (root, query, cb) -> cb.equal(root.get("deleted"), deleted);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> deviceTokenEquals(final String deviceToken) {
|
||||
return (root, query, cb) -> cb.equal(root.get("deviceToken"), deviceToken);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> createdAtGreaterThanOrEqual(final Instant createdAfter) {
|
||||
return (root, query, cb) -> cb.greaterThanOrEqualTo(root.get("createdAt"), createdAfter);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> createdAtLessThanOrEqual(final Instant createdBefore) {
|
||||
return (root, query, cb) -> cb.lessThanOrEqualTo(root.get("createdAt"), createdBefore);
|
||||
}
|
||||
|
||||
public static Specification<Ticket> createdAtLessThan(final Instant createdAt) {
|
||||
return (root, query, cb) -> cb.lessThan(root.get("createdAt"), createdAt);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package ba.unsa.etf.si.bbqms.scheduled.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.ticket_service.api.TicketService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TicketResetScheduler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TicketResetScheduler.class);
|
||||
private final TicketService ticketService;
|
||||
public TicketResetScheduler(final TicketService ticketService) {
|
||||
this.ticketService = ticketService;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
@Transactional
|
||||
public void run() {
|
||||
logger.info("Running ticket reset task scheduler.");
|
||||
try {
|
||||
this.ticketService.deleteAll();
|
||||
logger.info("Tickets reset.");
|
||||
} catch (final Exception exception) {
|
||||
logger.error("Failed to reset tickets.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ba.unsa.etf.si.bbqms.tenant_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.utils.Wrapper;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ServiceRequestDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TenantDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TenantService extends Wrapper {
|
||||
Tenant addTenant(final TenantDto request);
|
||||
Tenant findByCode(final String code);
|
||||
Tenant updateTenant(final String code, final TenantDto request);
|
||||
Service getServiceById(final long id);
|
||||
List<Service> getAllServicesByTenant(final String Code);
|
||||
Service addService(final String code, final ServiceRequestDto request) throws Exception;
|
||||
Service updateService(final String Code, final long id, final ServiceRequestDto request) throws Exception;
|
||||
void deleteService(final long id);
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
package ba.unsa.etf.si.bbqms.tenant_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.GroupService;
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
import ba.unsa.etf.si.bbqms.domain.TenantLogo;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.repository.ServiceRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TenantLogoRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TenantRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import ba.unsa.etf.si.bbqms.tenant_service.api.TenantService;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ServiceRequestDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TenantDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@org.springframework.stereotype.Service
|
||||
public class TenantServiceImpl implements TenantService {
|
||||
private final TenantRepository tenantRepository;
|
||||
private final TenantLogoRepository tenantLogoRepository;
|
||||
private final ServiceRepository serviceRepository;
|
||||
private final TicketRepository ticketRepository;
|
||||
private final GroupService groupService;
|
||||
|
||||
@Autowired
|
||||
public TenantServiceImpl(final TenantRepository tenantRepository,
|
||||
final TenantLogoRepository tenantLogoRepository,
|
||||
final ServiceRepository serviceRepository,
|
||||
final TicketRepository ticketRepository,
|
||||
final GroupService groupService) {
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.tenantLogoRepository = tenantLogoRepository;
|
||||
this.serviceRepository = serviceRepository;
|
||||
this.groupService = groupService;
|
||||
this.ticketRepository = ticketRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tenant addTenant(final TenantDto request) {
|
||||
final Tenant tenant = new Tenant(
|
||||
request.code(),
|
||||
request.name(),
|
||||
request.hqAddress(),
|
||||
request.font(),
|
||||
request.welcomeMessage()
|
||||
);
|
||||
|
||||
final TenantLogo newLogo = new TenantLogo(request.logo());
|
||||
final TenantLogo savedLogo = tenantLogoRepository.save(newLogo);
|
||||
tenant.setLogo(savedLogo);
|
||||
return tenantRepository.save(tenant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tenant findByCode(final String code) throws EntityNotFoundException {
|
||||
return tenantRepository.findByCode(code).orElseThrow(() -> new EntityNotFoundException("No tenant found with code: " + code));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tenant updateTenant(final String code, final TenantDto request) throws EntityNotFoundException {
|
||||
final Tenant tenant = findByCode(code);
|
||||
|
||||
if (request.name() != null) {
|
||||
tenant.setName(request.name());
|
||||
}
|
||||
if (request.hqAddress() != null) {
|
||||
tenant.setHqAddress(request.hqAddress());
|
||||
}
|
||||
if (request.font() != null) {
|
||||
tenant.setFont(request.font());
|
||||
}
|
||||
if (request.welcomeMessage() != null) {
|
||||
tenant.setWelcomeMessage(request.welcomeMessage());
|
||||
}
|
||||
if (request.logo() != null) {
|
||||
tenant.getLogo().setBase64Logo(request.logo());
|
||||
}
|
||||
return tenantRepository.save(tenant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Service getServiceById(final long id) {
|
||||
return serviceRepository.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service found with id: " + id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Service> getAllServicesByTenant(final String code) {
|
||||
return findByTenantCode(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Service addService(final String code, final ServiceRequestDto request) throws Exception {
|
||||
final Tenant tenant = tenantRepository.findByCode(code)
|
||||
.orElseThrow(() -> new Exception("Tenant not found"));
|
||||
|
||||
final List<Service> services = findByTenantCode(code);
|
||||
final boolean nameExists = services.stream()
|
||||
.anyMatch(service -> service.getName().equals(request.name()));
|
||||
if (nameExists) {
|
||||
throw new Exception("Name already in use!");
|
||||
}
|
||||
|
||||
final Service service = new Service(
|
||||
request.name(),
|
||||
tenant
|
||||
);
|
||||
return serviceRepository.save(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Service updateService(final String code, final long id, final ServiceRequestDto request) throws Exception {
|
||||
final List<Service> services = findByTenantCode(code);
|
||||
final boolean nameExists = services.stream()
|
||||
.anyMatch(service -> service.getName().equals(request.name()));
|
||||
if (nameExists) {
|
||||
throw new Exception("Name already in use!");
|
||||
}
|
||||
final Service service = getServiceById(id);
|
||||
if (request.name() != null) {
|
||||
service.setName(request.name());
|
||||
}
|
||||
return serviceRepository.save(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteService(final long id) {
|
||||
final Service service = this.serviceRepository.findById(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service with id: " + id));
|
||||
|
||||
final List<Ticket> ticketsToDiscard = this.ticketRepository.findByService_Id(id);
|
||||
final List<Long> ticketIds = ticketsToDiscard.stream()
|
||||
.map(Ticket::getId)
|
||||
.toList();
|
||||
|
||||
this.ticketRepository.deleteAllById(ticketIds);
|
||||
|
||||
for (final BranchGroup group : this.groupService.getAllOfferingService(service)) {
|
||||
this.groupService.deleteBranchGroupService(group.getId(), service.getId());
|
||||
}
|
||||
|
||||
serviceRepository.deleteById(id);
|
||||
}
|
||||
|
||||
private List<Service> findByTenantCode(final String tenantCode) {
|
||||
return this.serviceRepository.findByTenant_Code(tenantCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(final Class<T> tClass) {
|
||||
if (tClass.isAssignableFrom(TenantRepository.class)) {
|
||||
return tClass.cast(this.tenantRepository);
|
||||
} else if (tClass.isAssignableFrom(ServiceRepository.class)) {
|
||||
return tClass.cast(this.serviceRepository);
|
||||
}
|
||||
|
||||
return TenantService.super.unwrap(tClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ba.unsa.etf.si.bbqms.tfa_service.api;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TwoFactorService {
|
||||
String generateNewSecret();
|
||||
Optional<String> generateQrCodeUri(final String secret);
|
||||
boolean isCodeValid(final String code, final String secret) throws UnknownHostException;
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package ba.unsa.etf.si.bbqms.tfa_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.tfa_service.api.TwoFactorService;
|
||||
import dev.samstevens.totp.code.DefaultCodeGenerator;
|
||||
import dev.samstevens.totp.code.DefaultCodeVerifier;
|
||||
import dev.samstevens.totp.exceptions.QrGenerationException;
|
||||
import dev.samstevens.totp.qr.QrData;
|
||||
import dev.samstevens.totp.qr.QrGenerator;
|
||||
import dev.samstevens.totp.qr.ZxingPngQrGenerator;
|
||||
import dev.samstevens.totp.secret.DefaultSecretGenerator;
|
||||
import dev.samstevens.totp.time.SystemTimeProvider;
|
||||
import dev.samstevens.totp.util.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class TotpTwoFactorService implements TwoFactorService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TotpTwoFactorService.class);
|
||||
|
||||
@Value("${tfa.label}")
|
||||
public String TFA_LABEL;
|
||||
|
||||
@Value("${tfa.issuer}")
|
||||
public String TFA_ISSUER;
|
||||
|
||||
|
||||
@Override
|
||||
public String generateNewSecret() {
|
||||
return new DefaultSecretGenerator().generate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> generateQrCodeUri(final String secret) {
|
||||
final QrData qrData = new QrData.Builder()
|
||||
.label(this.TFA_LABEL)
|
||||
.issuer(this.TFA_ISSUER)
|
||||
.secret(secret)
|
||||
.build();
|
||||
try {
|
||||
QrGenerator qrGenerator = new ZxingPngQrGenerator();
|
||||
return Optional.of(Utils.getDataUriForImage(qrGenerator.generate(qrData), qrGenerator.getImageMimeType()));
|
||||
} catch (QrGenerationException e) {
|
||||
logger.error("Error while trying trying to generate QR-code.");
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeValid(final String code, final String secret) {
|
||||
DefaultCodeVerifier codeVerifier = new DefaultCodeVerifier(new DefaultCodeGenerator(), new SystemTimeProvider());
|
||||
return codeVerifier.isValidCode(secret, code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ba.unsa.etf.si.bbqms.ticket_service.api;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.utils.Wrapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface TicketService extends Wrapper {
|
||||
Ticket get(final long id);
|
||||
|
||||
Ticket create(final long serviceId, final long branchId, final String deviceToken);
|
||||
|
||||
List<Ticket> getByDevice(final String deviceToken);
|
||||
|
||||
void cancelTicket(final long ticketId);
|
||||
|
||||
void deleteAll();
|
||||
|
||||
default List<Ticket> findWithStation(final long stationId) {
|
||||
return findWithStation(stationId, false);
|
||||
}
|
||||
|
||||
List<Ticket> findWithStation(final long stationId, final boolean deleted);
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package ba.unsa.etf.si.bbqms.ticket_service.implementation;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.queue_service.api.QueueService;
|
||||
import ba.unsa.etf.si.bbqms.repository.BranchRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.ServiceRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TellerStationRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import ba.unsa.etf.si.bbqms.ticket_service.api.TicketService;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@org.springframework.stereotype.Service
|
||||
public class DefaultTicketService implements TicketService {
|
||||
private final TicketRepository ticketRepository;
|
||||
private final ServiceRepository serviceRepository;
|
||||
private final BranchRepository branchRepository;
|
||||
private final TellerStationRepository tellerStationRepository;
|
||||
private final BranchService branchService;
|
||||
private final QueueService queueService;
|
||||
|
||||
public DefaultTicketService(final TicketRepository ticketRepository,
|
||||
final ServiceRepository serviceRepository,
|
||||
final BranchRepository branchRepository,
|
||||
final TellerStationRepository tellerStationRepository,
|
||||
final BranchService branchService,
|
||||
final QueueService queueService) {
|
||||
this.ticketRepository = ticketRepository;
|
||||
this.serviceRepository = serviceRepository;
|
||||
this.branchRepository = branchRepository;
|
||||
this.tellerStationRepository = tellerStationRepository;
|
||||
this.branchService = branchService;
|
||||
this.queueService = queueService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ticket create(final long serviceId, final long branchId, final String deviceToken) {
|
||||
final Service service = this.serviceRepository.findById(serviceId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No service with id: " + serviceId));
|
||||
|
||||
final Branch branch = this.branchRepository.findById(branchId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No branch with id: " + branchId));
|
||||
|
||||
final Set<Service> possibleServices = this.branchService.extractPossibleServices(branch);
|
||||
if (!possibleServices.contains(service)) {
|
||||
throw new IllegalArgumentException("Branch with id: " + branchId + " does not offer service with id: " + serviceId);
|
||||
}
|
||||
|
||||
int serviceIndex = 0;
|
||||
for (Service possibleService : possibleServices) {
|
||||
if (possibleService.getId() == serviceId) {
|
||||
break;
|
||||
}
|
||||
serviceIndex++;
|
||||
}
|
||||
|
||||
final char serviceLetter = (char) ('A' + serviceIndex);
|
||||
final String formattedNumber = serviceLetter + String.valueOf(getNextTicketNumber(possibleServices, branch));
|
||||
final Ticket newTicket = new Ticket(formattedNumber, Instant.now(), deviceToken, service, branch);
|
||||
|
||||
return this.ticketRepository.save(newTicket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Ticket> getByDevice(final String deviceToken) {
|
||||
return this.ticketRepository.findByDeviceToken(deviceToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTicket(final long ticketId) {
|
||||
final Ticket ticket = this.ticketRepository.findById(ticketId)
|
||||
.orElseThrow(() -> new EntityNotFoundException("No ticket with id: " + ticketId));
|
||||
|
||||
final TellerStation tellerStation = ticket.getTellerStation();
|
||||
if (tellerStation != null) {
|
||||
this.queueService.advanceQueueState(tellerStation);
|
||||
} else {
|
||||
this.ticketRepository.delete(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ticket get(final long id) {
|
||||
return this.ticketRepository.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
this.ticketRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Ticket> findWithStation(final long stationId, final boolean deleted) {
|
||||
final TellerStation tellerStation = this.tellerStationRepository.get(stationId);
|
||||
final List<Long> serviceIds = tellerStation.getServices().stream().map(Service::getId).toList();
|
||||
|
||||
return this.ticketRepository.findByService_IdInAndBranch_IdAndDeletedOrderByCreatedAtAsc(
|
||||
serviceIds,
|
||||
tellerStation.getBranch().getId(),
|
||||
deleted
|
||||
);
|
||||
}
|
||||
|
||||
private long getNextTicketNumber(final Set<Service> possibleServices, final Branch branch) {
|
||||
final List<Ticket> tickets = this.ticketRepository.findByService_IdInAndBranch_Id(
|
||||
possibleServices.stream().map(Service::getId).toList(),
|
||||
branch.getId()
|
||||
);
|
||||
|
||||
final Set<String> ticketNumbers = tickets.stream()
|
||||
.map(Ticket::getNumber)
|
||||
.map(this::extractNumericPart)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
final long maxNumber = ticketNumbers.isEmpty() ? 0 : ticketNumbers.stream().mapToLong(Long::parseLong).max().getAsLong();
|
||||
|
||||
return maxNumber + 1;
|
||||
}
|
||||
|
||||
private String extractNumericPart(final String ticketNumber) {
|
||||
return ticketNumber.replaceAll("[^0-9]", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(final Class<T> tClass) {
|
||||
if (tClass.isAssignableFrom(TicketRepository.class)) {
|
||||
return tClass.cast(this.ticketRepository);
|
||||
}
|
||||
|
||||
return TicketService.super.unwrap(tClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ba.unsa.etf.si.bbqms.utils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class DateUtils {
|
||||
|
||||
public static String formatDate(final String patternFormat, final Instant time) {
|
||||
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(patternFormat)
|
||||
.withZone(ZoneId.of("Europe/Sarajevo"));
|
||||
|
||||
return formatter.format(time);
|
||||
}
|
||||
|
||||
public static String getHourMinute(final Instant time) {
|
||||
return formatDate("hh:mm", time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ba.unsa.etf.si.bbqms.utils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EmailValidator {
|
||||
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$");
|
||||
|
||||
public static boolean validate(String email){
|
||||
return email != null && EMAIL_PATTERN.matcher(email).matches();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ba.unsa.etf.si.bbqms.utils;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
|
||||
public class UserValidator {
|
||||
public static boolean validateData(final User user) {
|
||||
return user.getEmail() != null &&
|
||||
EmailValidator.validate(user.getEmail().trim()) &&
|
||||
user.getPassword() != null &&
|
||||
user.getPassword().trim().length() >= 4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ba.unsa.etf.si.bbqms.utils;
|
||||
|
||||
import javax.annotation.OverridingMethodsMustInvokeSuper;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public interface Wrapper {
|
||||
|
||||
@OverridingMethodsMustInvokeSuper
|
||||
default <T> T unwrap(final Class<T> tClass) {
|
||||
if (tClass.isAssignableFrom(this.getClass())) {
|
||||
return tClass.cast(this);
|
||||
}
|
||||
|
||||
throw new NoSuchElementException("Unable to unwrap " + tClass.getName() + " from " + getClass());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.AdminService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.RoleName;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.exceptions.AuthException;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.UserDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/admin")
|
||||
public class AdminController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
|
||||
private final AdminService adminService;
|
||||
private final AuthService authService;
|
||||
|
||||
public AdminController(final AdminService adminService, final AuthService authService) {
|
||||
this.adminService = adminService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/{code}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
|
||||
public ResponseEntity getUsers(@RequestBody RoleRequest request, @PathVariable(name = "code") final String tenantCode) {
|
||||
RoleName roleName;
|
||||
try {
|
||||
roleName = RoleName.valueOf(request.roleName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Role doesn't exist");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
if(this.authService.canOnlyCRUDUser(roleName)){
|
||||
logger.warn("Only super admin can read admins");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
logger.warn("Super admin does not belong to the specified tenant");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
try {
|
||||
return ResponseEntity.ok().body(
|
||||
this.adminService.findUsersByCode(tenantCode, request.roleName).stream()
|
||||
.map(UserDto::fromEntity)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} catch (EntityNotFoundException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{code}/user")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
|
||||
public ResponseEntity addUser(@RequestBody final AdminRequest request, @PathVariable(name = "code") final String tenantCode) throws AuthException {
|
||||
RoleName roleName;
|
||||
try {
|
||||
roleName = RoleName.valueOf(request.roleName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warn("Role doesn't exist");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
if(this.authService.canOnlyCRUDUser(roleName)){
|
||||
logger.warn("Only super admin can add admin");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
logger.warn("Admin does not belong to the specified tenant");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
final User newUser = User.builder()
|
||||
.password(request.password())
|
||||
.email(request.email)
|
||||
.build();
|
||||
try {
|
||||
final User user = this.adminService.addUser(newUser, tenantCode, request.roleName);
|
||||
return ResponseEntity.ok(UserDto.fromEntity(user));
|
||||
} catch (Exception exception) {
|
||||
logger.warn(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{code}/user/{userId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
|
||||
public ResponseEntity updateAdmin(@RequestBody final UserDto request, @PathVariable(name = "code") final String tenantCode, @PathVariable(name = "userId") final long adminId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
logger.warn("Admin does not belong to the specified tenant");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final User user = this.adminService.updateUser(request, tenantCode, adminId);
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Updated user: " + user.getUsername()));
|
||||
} catch (Exception exception) {
|
||||
logger.warn(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{code}/user/{userId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN', 'ROLE_STAFF_ADMIN')")
|
||||
public ResponseEntity removeAdmin(@PathVariable(name = "code") final String tenantCode, @PathVariable(name = "userId") final long adminId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
logger.warn("Admin does not belong to the specified tenant");
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
this.adminService.removeUser(tenantCode, adminId);
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Removed user"));
|
||||
} catch (Exception exception) {
|
||||
logger.warn(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
public record AdminRequest(String email, String password, String roleName) {
|
||||
}
|
||||
|
||||
public record RoleRequest(String roleName){
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.CookieService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.OAuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ErrorMessage;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.UserDto;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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 java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
public class AuthController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
|
||||
|
||||
private final AuthService authService;
|
||||
private final CookieService cookieService;
|
||||
private final OAuthService oAuthService;
|
||||
|
||||
public AuthController(final AuthService authService,
|
||||
final CookieService cookieService,
|
||||
final OAuthService oAuthService) {
|
||||
this.authService = authService;
|
||||
this.cookieService = cookieService;
|
||||
this.oAuthService = oAuthService;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity register(@RequestBody final RegisterRequest request) {
|
||||
final User newUser = User.builder()
|
||||
.password(request.password())
|
||||
.email(request.email)
|
||||
.build();
|
||||
try {
|
||||
final User user = this.authService.registerUser(newUser);
|
||||
return ResponseEntity.ok(UserDto.fromEntity(user));
|
||||
} catch (Exception exception) {
|
||||
logger.warn(exception.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(new ErrorMessage("Couldn't register user: " + newUser.getUsername()));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity login(@RequestBody final LoginRequest loginRequest,
|
||||
final HttpServletResponse response) {
|
||||
final User userData = User.builder()
|
||||
.email(loginRequest.email().trim())
|
||||
.password(loginRequest.password().trim())
|
||||
.build();
|
||||
|
||||
final Optional<User> optionalUser = this.authService.loginUser(userData);
|
||||
|
||||
if (optionalUser.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
final User user = optionalUser.get();
|
||||
|
||||
if (user.isTfa()) {
|
||||
return ResponseEntity.ok().body(UserDto.fromEntity(user));
|
||||
// if user is using 2FA, then we indicate everything is OK, but not give the
|
||||
// user their token
|
||||
// because they still need to do 2FA
|
||||
}
|
||||
|
||||
final String token = this.authService.generateUserToken(user);
|
||||
response.addHeader("Auth-Token", token);
|
||||
|
||||
return ResponseEntity.ok().body(new LoginResponse(UserDto.fromEntity(user), token));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity verifyToken() {
|
||||
final boolean userLoggedIn = this.authService.getCurrentUser().isPresent();
|
||||
if (userLoggedIn) {
|
||||
return ResponseEntity.ok(new SimpleMessageDto("Success"));
|
||||
} else {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/tfa")
|
||||
public ResponseEntity getQrCode(@RequestParam final String email) {
|
||||
final Optional<User> optionalUser = this.authService.findByEmail(email);
|
||||
if (optionalUser.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
String qrCodeUri = this.authService.generateUserQrCode(optionalUser.get());
|
||||
|
||||
return ResponseEntity.ok(new QrCodeResponse(qrCodeUri));
|
||||
}
|
||||
|
||||
@PostMapping("/tfa")
|
||||
public ResponseEntity verifyCode(final HttpServletResponse response,
|
||||
@RequestBody final TfaCodeVerificationRequest request) {
|
||||
final Optional<User> optionalUser = this.authService.findByEmail(request.email());
|
||||
if (optionalUser.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
final User user = optionalUser.get();
|
||||
|
||||
final boolean verified = this.authService.verifyUserTfaCode(user, request.code());
|
||||
if (verified) {
|
||||
final String token = this.authService.generateUserToken(user);
|
||||
response.addHeader("Auth-Token", token);
|
||||
|
||||
return ResponseEntity.ok(new LoginResponse(UserDto.fromEntity(user), token));
|
||||
} else {
|
||||
return ResponseEntity.badRequest().body(new ErrorMessage("Could not verify the code."));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/tfa")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity setTfaState(@RequestBody final TfaSetRequest request) throws Exception {
|
||||
final boolean tfa = this.authService.setTfaUse(Boolean.parseBoolean(request.isTfa));
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("User tfa set to: " + tfa));
|
||||
}
|
||||
|
||||
@PostMapping("/login/oauth2/google")
|
||||
public ResponseEntity loginWithGoogle(final HttpServletResponse response,
|
||||
@RequestBody final GoogleLoginRequest request) {
|
||||
final Optional<User> optionalUser = this.oAuthService.authenticateUser(request.googleToken());
|
||||
if (optionalUser.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
} else {
|
||||
final User user = optionalUser.get();
|
||||
final String token = this.authService.generateUserToken(user);
|
||||
response.addHeader("Auth-Token", token);
|
||||
|
||||
return ResponseEntity.ok().body(new LoginResponse(UserDto.fromEntity(user), token));
|
||||
}
|
||||
}
|
||||
|
||||
public record RegisterRequest(String email, String password) {
|
||||
}
|
||||
|
||||
public record LoginRequest(String email, String password) {
|
||||
}
|
||||
|
||||
public record LoginResponse(UserDto userData, String token) {
|
||||
}
|
||||
|
||||
public record TfaCodeVerificationRequest(String code, String email) {
|
||||
}
|
||||
|
||||
public record QrCodeResponse(String qrCode) {
|
||||
}
|
||||
|
||||
public record GoogleLoginRequest(String googleToken) {
|
||||
}
|
||||
|
||||
public record TfaSetRequest(String isTfa) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.domain.User;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import ba.unsa.etf.si.bbqms.repository.specs.TicketSpecs;
|
||||
import ba.unsa.etf.si.bbqms.tenant_service.api.TenantService;
|
||||
import ba.unsa.etf.si.bbqms.ticket_service.api.TicketService;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.BranchWithStationsDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ServiceDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TellerStationDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TicketDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.params.branch.QueueStateParams;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
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 java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/branches")
|
||||
public class BranchController {
|
||||
private final AuthService authService;
|
||||
private final BranchService branchService;
|
||||
private final TicketService ticketService;
|
||||
private final TenantService tenantService;
|
||||
|
||||
public BranchController(final AuthService authService,
|
||||
final BranchService branchService,
|
||||
final TicketService ticketService,
|
||||
final TenantService tenantService) {
|
||||
this.authService = authService;
|
||||
this.branchService = branchService;
|
||||
this.ticketService = ticketService;
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
@PostMapping("/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity createBranch(@RequestBody final BranchRequest request,
|
||||
@PathVariable final String tenantCode) {
|
||||
final User currentUser = this.authService.getAuthenticatedUser();
|
||||
|
||||
if (!currentUser.getTenant().getCode().equals(tenantCode) ||
|
||||
request.name == null ||
|
||||
request.name.trim().isEmpty()) {
|
||||
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Branch created = this.branchService.createBranch(request.name(), request.tellerStations(), tenantCode);
|
||||
return ResponseEntity.ok().body(BranchWithStationsDto.fromEntity(created));
|
||||
} catch (final Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}")
|
||||
public ResponseEntity getBranches(@PathVariable final String tenantCode) {
|
||||
final List<BranchWithStationsDto> dtos = this.branchService.getBranches(tenantCode).stream()
|
||||
.map(BranchWithStationsDto::fromEntity)
|
||||
.toList();
|
||||
|
||||
return ResponseEntity.ok().body(dtos);
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{branchId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity updateBranch(@RequestBody final BranchRequest request,
|
||||
@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId) {
|
||||
final User currentUser = this.authService.getAuthenticatedUser();
|
||||
if (!currentUser.getTenant().getCode().equals(tenantCode) ||
|
||||
request.name == null ||
|
||||
request.name.trim().isEmpty()) {
|
||||
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Branch updated = this.branchService.updateBranch(
|
||||
Long.parseLong(branchId),
|
||||
request.name(),
|
||||
request.tellerStations(),
|
||||
tenantCode
|
||||
);
|
||||
return ResponseEntity.ok().body(BranchWithStationsDto.fromEntity(updated));
|
||||
} catch (final Exception exception) {
|
||||
System.out.println(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{branchId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity removeBranch(@PathVariable final String tenantCode, @PathVariable final String branchId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
this.branchService.removeBranch(Long.parseLong(branchId));
|
||||
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Deleted branch with id: " + branchId));
|
||||
}
|
||||
|
||||
@PostMapping("/{tenantCode}/{branchId}/stations")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity addStation(@RequestBody final StationRequest request,
|
||||
@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final TellerStation created = this.branchService.addStation(request.name(), Long.parseLong(branchId));
|
||||
return ResponseEntity.ok().body(TellerStationDto.fromEntity(created));
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{branchId}/stations/{stationId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity updateStation(@RequestBody StationRequest request,
|
||||
@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId,
|
||||
@PathVariable final String stationId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final TellerStation updated = this.branchService.updateStation(Long.parseLong(stationId), request.name());
|
||||
return ResponseEntity.ok().body(TellerStationDto.fromEntity(updated));
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{branchId}/stations/{stationId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity removeStation(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId,
|
||||
@PathVariable final String stationId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
this.branchService.removeStation(Long.parseLong(stationId));
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Removed station."));
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}/{branchId}/services")
|
||||
public ResponseEntity getBranchServices(@PathVariable final String branchId,
|
||||
@PathVariable final String tenantCode) {
|
||||
return this.branchService.findById(Long.parseLong(branchId))
|
||||
.map(this.branchService::extractPossibleServices)
|
||||
.map(services -> services.stream().map(ServiceDto::fromEntity).collect(Collectors.toSet()))
|
||||
.map(serviceDtos -> ResponseEntity.ok().body(serviceDtos))
|
||||
.orElse(ResponseEntity.badRequest().build());
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}/{branchId}/queue")
|
||||
public ResponseEntity getBranchQueue(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId,
|
||||
final QueueStateParams queueStateParams,
|
||||
final Sort sort) {
|
||||
final Branch branch = this.branchService.findById(Long.parseLong(branchId)).orElseThrow();
|
||||
|
||||
final TicketRepository ticketRepository = this.ticketService.unwrap(TicketRepository.class);
|
||||
|
||||
Specification<Ticket> filter = TicketSpecs.branchIdEquals(branch.getId());
|
||||
if (queueStateParams.serviceId() != null && queueStateParams.serviceId().isPresent()) {
|
||||
filter = filter.and(TicketSpecs.serviceIdEquals(queueStateParams.serviceId().get()));
|
||||
}
|
||||
if (queueStateParams.createdAfter() != null && queueStateParams.createdAfter().isPresent()) {
|
||||
filter = filter.and(TicketSpecs.createdAtGreaterThanOrEqual(queueStateParams.createdAfter().get()));
|
||||
}
|
||||
if (queueStateParams.createdBefore() != null && queueStateParams.createdBefore().isPresent()) {
|
||||
filter = filter.and(TicketSpecs.createdAtLessThanOrEqual(queueStateParams.createdBefore().get()));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().body(
|
||||
ticketRepository.findAll(filter, sort).stream().map(TicketDto::fromEntity).toList()
|
||||
);
|
||||
}
|
||||
|
||||
public record BranchRequest(String name, List<String> tellerStations) {
|
||||
}
|
||||
|
||||
public record StationRequest(String name) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.DisplayService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Display;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.DisplayDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/displays")
|
||||
public class DisplayController {
|
||||
private final DisplayService displayService;
|
||||
private final AuthService authService;
|
||||
|
||||
public DisplayController(final DisplayService displayService, final AuthService authService) {
|
||||
this.displayService = displayService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity createDisplay(@PathVariable final String tenantCode,
|
||||
@RequestBody final DisplayCreateRequest request) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Display createdDisplay = this.displayService.createDisplay(request.name(), request.branchId());
|
||||
return ResponseEntity.ok().body(DisplayDto.fromEntity(createdDisplay));
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}/{branchId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getDisplays(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Set<DisplayDto> displays = this.displayService.getDisplays(Long.parseLong(branchId)).stream()
|
||||
.map(DisplayDto::fromEntity)
|
||||
.collect(Collectors.toSet());
|
||||
return ResponseEntity.ok().body(displays);
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getDisplaysByTenant(@PathVariable final String tenantCode) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Set<DisplayDto> displays = this.displayService.getDisplaysByTenant(tenantCode).stream()
|
||||
.map(DisplayDto::fromEntity)
|
||||
.collect(Collectors.toSet());
|
||||
return ResponseEntity.ok().body(displays);
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/unassigned/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getUnassignedDisplaysByTenant(@PathVariable final String tenantCode) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Set<DisplayDto> displays = this.displayService.getUnassignedDisplaysByTenant(tenantCode).stream()
|
||||
.map(DisplayDto::fromEntity)
|
||||
.collect(Collectors.toSet());
|
||||
return ResponseEntity.ok().body(displays);
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/unassigned/{tenantCode}/{branchId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getUnassignedByBranch(@PathVariable final String tenantCode, @PathVariable final String branchId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
return ResponseEntity.ok().body(
|
||||
this.displayService.getUnassignedDisplaysByBranch(Long.parseLong(branchId)).stream()
|
||||
.map(DisplayDto::fromEntity)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{displayId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity updateDisplay(@PathVariable final String tenantCode,
|
||||
@PathVariable final String displayId,
|
||||
@RequestBody final DisplayUpdateRequest request) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try{
|
||||
final Display updatedDisplay = this.displayService.updateDisplay(Long.parseLong(displayId), request.name());
|
||||
return ResponseEntity.ok().body(DisplayDto.fromEntity(updatedDisplay));
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{displayId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity removeDisplay(@PathVariable final String tenantCode,
|
||||
@PathVariable final String displayId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
this.displayService.removeDisplay(Long.parseLong(displayId));
|
||||
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Deleted display with id: " + displayId));
|
||||
}
|
||||
|
||||
public record DisplayCreateRequest(String name, long branchId){
|
||||
}
|
||||
|
||||
public record DisplayUpdateRequest(String name){
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.BranchGroup;
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.GroupService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.*;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/groups")
|
||||
public class GroupController {
|
||||
private final GroupService groupService;
|
||||
private final BranchService branchService;
|
||||
private final AuthService authService;
|
||||
|
||||
public GroupController(final GroupService groupService,
|
||||
final BranchService branchService,
|
||||
final AuthService authService) {
|
||||
this.groupService = groupService;
|
||||
this.branchService = branchService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getAll(@PathVariable final String tenantCode){
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final List<BranchGroup> branchGroupList = this.groupService.getAllByTenant(tenantCode);
|
||||
final List<BranchGroupResponseDto> branchGroupResponseDtoList = branchGroupList.stream()
|
||||
.map(BranchGroupResponseDto::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok().body(branchGroupResponseDtoList);
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{code}/{groupId}/assignable/branch")
|
||||
public List<BranchDto> findAssignableBranches(@PathVariable final String code, @PathVariable final String groupId) {
|
||||
return this.groupService.findAssignableBranches(Long.parseLong(groupId), code).stream()
|
||||
.map(BranchDto::fromEntity)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{code}/{groupId}/assignable/service")
|
||||
public List<ServiceDto> findAssignableServices(@PathVariable final String code, @PathVariable final String groupId) {
|
||||
return this.groupService.findAssignableServices(Long.parseLong(groupId), code).stream()
|
||||
.map(ServiceDto::fromEntity)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping("/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity addBranchGroup(@PathVariable final String tenantCode, @RequestBody final BranchGroupCreateDto request) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final BranchGroup addedBranchGroup = this.groupService.addBranchGroup(tenantCode, request);
|
||||
return ResponseEntity.ok().body(BranchGroupResponseDto.fromEntity(addedBranchGroup));
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{groupId}/services/{serviceId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity addBranchGroupService(@PathVariable final String tenantCode,
|
||||
@PathVariable final String groupId,
|
||||
@PathVariable final String serviceId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final BranchGroup updatedBranchGroup = this.groupService.addBranchGroupService(Long.parseLong(groupId), Long.parseLong(serviceId));
|
||||
return ResponseEntity.ok().body(BranchGroupResponseDto.fromEntity(updatedBranchGroup));
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{groupId}/branches/{branchId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity addBranchGroupBranch(@PathVariable final String tenantCode,
|
||||
@PathVariable final String groupId,
|
||||
@PathVariable final String branchId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final BranchGroup updatedBranchGroup = this.groupService.addBranchGroupBranch(Long.parseLong(groupId), Long.parseLong(branchId));
|
||||
return ResponseEntity.ok().body(BranchGroupResponseDto.fromEntity(updatedBranchGroup));
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{branchGroupId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity updateBranchGroup(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchGroupId,
|
||||
@RequestBody final BranchGroupUpdateDto name) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final BranchGroup updatedBranchGroup = this.groupService.updateBranchGroup(Long.parseLong(branchGroupId), name);
|
||||
return ResponseEntity.ok().body(BranchGroupResponseDto.fromEntity(updatedBranchGroup));
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{branchGroupId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity deleteBranchGroup(@PathVariable final String tenantCode, @PathVariable final String branchGroupId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
this.groupService.deleteBranchGroup(Long.parseLong(branchGroupId));
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Deleted branch group with id: " + branchGroupId));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{branchGroupId}/branches/{branchId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity deleteBranchGroupBranch(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchGroupId,
|
||||
@PathVariable final String branchId){
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final BranchGroup updatedBranchGroup = this.groupService.deleteBranchGroupBranch(Long.parseLong(branchGroupId),Long.parseLong(branchId));
|
||||
return ResponseEntity.ok().body(BranchGroupResponseDto.fromEntity(updatedBranchGroup));
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{branchGroupId}/services/{serviceId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity deleteBranchGroupService(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchGroupId,
|
||||
@PathVariable final String serviceId){
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final BranchGroup updatedBranchGroup = this.groupService.deleteBranchGroupService(Long.parseLong(branchGroupId),Long.parseLong(serviceId));
|
||||
return ResponseEntity.ok().body(BranchGroupResponseDto.fromEntity(updatedBranchGroup));
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public record BranchGroupResponseDto (Long id, String name, Set<ServiceResponseDto> services, Set<BranchDto> branches) {
|
||||
public static BranchGroupResponseDto fromEntity(final BranchGroup branchGroup) {
|
||||
final Set<Branch> branchSet = branchGroup.getBranches();
|
||||
final Set<Service> serviceSet = branchGroup.getServices();
|
||||
return new BranchGroupResponseDto(
|
||||
branchGroup.getId(),
|
||||
branchGroup.getName(),
|
||||
serviceSet.stream().map(ServiceResponseDto::fromEntity).collect(Collectors.toSet()),
|
||||
branchSet.stream().map(BranchDto::fromEntity).collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.StationService;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.queue_service.api.QueueService;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TicketDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/teller")
|
||||
public class TellerController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TellerController.class);
|
||||
private final QueueService queueService;
|
||||
private final StationService stationService;
|
||||
|
||||
public TellerController(final QueueService queueService, final StationService stationService) {
|
||||
this.queueService = queueService;
|
||||
this.stationService = stationService;
|
||||
}
|
||||
|
||||
@PostMapping("/advance-queue/{stationId}")
|
||||
public ResponseEntity advanceQueueState(@PathVariable final String stationId) {
|
||||
try {
|
||||
final TellerStation station = this.stationService.findById(Long.parseLong(stationId))
|
||||
.orElseThrow(() -> new EntityNotFoundException("No station with id: " + stationId));
|
||||
|
||||
final Optional<Ticket> nextTicket = this.queueService.advanceQueueState(station);
|
||||
if (nextTicket.isEmpty()) {
|
||||
return ResponseEntity.ok().body(
|
||||
new SimpleMessageDto("Queue for this station is empty.")
|
||||
);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().body(TicketDto.fromEntity(nextTicket.get()));
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/current-ticket/{stationId}")
|
||||
public ResponseEntity returnCurrentTicket(@PathVariable final String stationId) {
|
||||
try {
|
||||
final TellerStation station = this.stationService.findById(Long.parseLong(stationId))
|
||||
.orElseThrow(() -> new EntityNotFoundException("No station with id: " + stationId));
|
||||
|
||||
final Optional<Ticket> currentTicket = this.queueService.findCurrentTicketForStation(station);
|
||||
if (currentTicket.isEmpty()) {
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("No ticket assigned to this station."));
|
||||
}
|
||||
return ResponseEntity.ok().body(TicketDto.fromEntity(currentTicket.get()));
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/undo-queue/{stationId}")
|
||||
public ResponseEntity undoLastTicket(@PathVariable final String stationId) {
|
||||
try{
|
||||
final Optional<Ticket> optionalNextTicket = this.queueService.undoStationState(Long.parseLong(stationId));
|
||||
|
||||
if (optionalNextTicket.isPresent()) {
|
||||
return ResponseEntity.ok().body(TicketDto.fromEntity(optionalNextTicket.get()));
|
||||
} else {
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("No next ticket."));
|
||||
}
|
||||
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.StationService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.domain.TellerStation;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.ticket_service.api.TicketService;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.DisplayDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ErrorResponseDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ServiceDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ServiceResponseDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TicketDto;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/stations")
|
||||
public class TellerStationController {
|
||||
private final StationService stationService;
|
||||
private final AuthService authService;
|
||||
private final TicketService ticketService;
|
||||
|
||||
public TellerStationController(final StationService stationService,
|
||||
final AuthService authService,
|
||||
final TicketService ticketService) {
|
||||
this.stationService = stationService;
|
||||
this.authService = authService;
|
||||
this.ticketService = ticketService;
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getAll(@PathVariable final String tenantCode){
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final List<TellerStation> tellerStationList = this.stationService.getAllByTenant(tenantCode);
|
||||
final List<TellerStationResponseDto> tellerStationResponseDtoList = tellerStationList.stream()
|
||||
.map(TellerStationResponseDto::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok().body(tellerStationResponseDtoList);
|
||||
} catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}/{stationId}/services")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity getServices(@PathVariable final String tenantCode,
|
||||
@PathVariable final String stationId,
|
||||
@RequestParam(defaultValue = "true") final boolean assigned) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final Set<Service> serviceSet = this.stationService.getServicesByAssigned(Long.parseLong(stationId),assigned);
|
||||
final Set<ServiceResponseDto> serviceResponseDtoSet = serviceSet.stream()
|
||||
.map(ServiceResponseDto::fromEntity)
|
||||
.collect(Collectors.toSet());
|
||||
return ResponseEntity.ok().body(serviceResponseDtoSet);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}/{stationId}/services/assignable")
|
||||
public ResponseEntity findAssignableServices(@PathVariable final String stationId,
|
||||
@PathVariable final String tenantCode) {
|
||||
return ResponseEntity.ok().body(
|
||||
this.stationService.findAssignableServices(Long.parseLong(stationId))
|
||||
);
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{stationId}/services/{serviceId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity addTellerStationService(@PathVariable final String tenantCode,
|
||||
@PathVariable final String stationId,
|
||||
@PathVariable final String serviceId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final TellerStation updatedTellerStation = this.stationService.addTellerStationService(Long.parseLong(stationId), Long.parseLong(serviceId));
|
||||
return ResponseEntity.ok().body(TellerStationResponseDto.fromEntity(updatedTellerStation));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{stationId}/services/{serviceId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity deleteTellerStationService(@PathVariable final String tenantCode,
|
||||
@PathVariable final String stationId,
|
||||
@PathVariable final String serviceId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final TellerStation updatedTellerStation = this.stationService.deleteTellerStationService(Long.parseLong(stationId), Long.parseLong(serviceId));
|
||||
return ResponseEntity.ok().body(TellerStationResponseDto.fromEntity(updatedTellerStation));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantCode}/{stationId}/displays/{displayId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity addDisplayToStation(@PathVariable final String tenantCode,
|
||||
@PathVariable final String stationId,
|
||||
@PathVariable final String displayId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final TellerStation updatedTellerStation = this.stationService.addTellerStationDisplay(Long.parseLong(stationId), Long.parseLong(displayId));
|
||||
return ResponseEntity.ok().body(TellerStationResponseDto.fromEntity(updatedTellerStation));
|
||||
} catch(final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantCode}/{stationId}/displays/{displayId}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_SUPER_ADMIN', 'ROLE_BRANCH_ADMIN')")
|
||||
public ResponseEntity removeDisplayFromStation(@PathVariable final String tenantCode,
|
||||
@PathVariable final String stationId,
|
||||
@PathVariable final String displayId) {
|
||||
if (!this.authService.canChangeTenant(tenantCode)) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
|
||||
try {
|
||||
final TellerStation updatedTellerStation = this.stationService.deleteTellerStationDisplay(Long.parseLong(stationId), Long.parseLong(displayId));
|
||||
return ResponseEntity.ok().body(TellerStationResponseDto.fromEntity(updatedTellerStation));
|
||||
} catch(final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantCode}/{branchId}")
|
||||
public ResponseEntity getBranchStations(@PathVariable final String tenantCode,
|
||||
@PathVariable final String branchId) {
|
||||
try{
|
||||
return ResponseEntity.ok().body(
|
||||
this.stationService.getAllByBranch(Long.parseLong(branchId)).stream()
|
||||
.map(TellerStationResponseDto::fromEntity)
|
||||
.toList()
|
||||
);
|
||||
} catch (final Exception exception) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{stationId}/tickets")
|
||||
public ResponseEntity getTicketsForTellerStation(@PathVariable final long stationId) {
|
||||
try {
|
||||
final List<TicketDto> ticketDtos = this.ticketService.findWithStation(stationId).stream()
|
||||
.filter(ticket -> ticket.getTellerStation() == null || ticket.getTellerStation().getId() == stationId)
|
||||
.map(TicketDto::fromEntity)
|
||||
.toList();
|
||||
|
||||
return ResponseEntity.ok().body(ticketDtos);
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
public record TellerStationResponseDto(long id, String name, DisplayDto display, Set<ServiceResponseDto> services) {
|
||||
public static TellerStationResponseDto fromEntity(final TellerStation tellerStation) {
|
||||
final Set<Service> serviceSet = tellerStation.getServices();
|
||||
return new TellerStationResponseDto(
|
||||
tellerStation.getId(),
|
||||
tellerStation.getName(),
|
||||
tellerStation.getDisplay() != null ? DisplayDto.fromEntity(tellerStation.getDisplay()) : null,
|
||||
serviceSet.stream().map(ServiceResponseDto::fromEntity).collect(Collectors.toSet())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.GroupService;
|
||||
import ba.unsa.etf.si.bbqms.auth_service.api.AuthService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
import ba.unsa.etf.si.bbqms.tenant_service.api.TenantService;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ErrorResponseDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.ServiceRequestDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TenantDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
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 java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/tenants")
|
||||
public class TenantController {
|
||||
private final TenantService tenantService;
|
||||
private final BranchService branchService;
|
||||
private final GroupService groupService;
|
||||
private final AuthService authService;
|
||||
|
||||
public TenantController(final TenantService tenantService,
|
||||
final BranchService branchService,
|
||||
final GroupService groupService,
|
||||
final AuthService authService) {
|
||||
this.tenantService = tenantService;
|
||||
this.branchService = branchService;
|
||||
this.groupService = groupService;
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity addTenant(@RequestBody final TenantDto tenantDto) {
|
||||
return ResponseEntity.ok().body(tenantService.addTenant(tenantDto));
|
||||
}
|
||||
|
||||
@GetMapping("/{code}")
|
||||
public ResponseEntity getTenantByCode(@PathVariable(name = "code") final String code) {
|
||||
try {
|
||||
return ResponseEntity.ok().body(tenantService.findByCode(code));
|
||||
} catch (EntityNotFoundException e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{code}")
|
||||
@PreAuthorize("isAuthenticated()")
|
||||
public ResponseEntity updateTenant(@PathVariable final String code, @RequestBody final TenantDto request) throws Exception {
|
||||
if (!this.authService.canChangeTenant(code)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
try {
|
||||
return ResponseEntity.ok().body(tenantService.updateTenant(code, request));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{code}/services")
|
||||
@PreAuthorize("hasAnyRole('ROLE_BRANCH_ADMIN', 'ROLE_SUPER_ADMIN')")
|
||||
public ResponseEntity addService(@PathVariable(name = "code") final String code, @RequestBody final ServiceRequestDto serviceRequestDto) {
|
||||
if (!this.authService.canChangeTenant(code)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
try {
|
||||
return ResponseEntity.ok().body(tenantService.addService(code, serviceRequestDto));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{code}/services")
|
||||
public ResponseEntity getAllServicesByTenant(@PathVariable(name = "code") final String code) {
|
||||
try {
|
||||
return ResponseEntity.ok().body(tenantService.getAllServicesByTenant(code));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{code}/services/group/{groupId}")
|
||||
public ResponseEntity listGroupAssignableServices(@PathVariable final String code, @PathVariable final String groupId) {
|
||||
final List<Service> possibleServices = this.tenantService.getAllServicesByTenant(code);
|
||||
final List<Service> assignedServices = this.groupService.get(Long.parseLong(groupId))
|
||||
.getServices().stream().toList();
|
||||
|
||||
possibleServices.removeAll(assignedServices);
|
||||
|
||||
return ResponseEntity.ok().body(possibleServices);
|
||||
}
|
||||
|
||||
@PutMapping("/{code}/services/{id}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_BRANCH_ADMIN', 'ROLE_SUPER_ADMIN')")
|
||||
public ResponseEntity updateService(@PathVariable(name = "code") final String code,
|
||||
@PathVariable(name = "id") final Long id,
|
||||
@RequestBody final ServiceRequestDto request) {
|
||||
if (!this.authService.canChangeTenant(code)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
try {
|
||||
return ResponseEntity.ok().body(tenantService.updateService(code, id, request));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().body(new ErrorResponseDto(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteMapping("/{code}/services/{id}")
|
||||
@PreAuthorize("hasAnyRole('ROLE_BRANCH_ADMIN', 'ROLE_SUPER_ADMIN')")
|
||||
public ResponseEntity deleteService(@PathVariable(name = "code") final String code, @PathVariable(name = "id") final Long id) {
|
||||
if (!this.authService.canChangeTenant(code)) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
try {
|
||||
tenantService.deleteService(id);
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Service with ID " + id + " deleted"));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.controllers;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.admin_service.api.BranchService;
|
||||
import ba.unsa.etf.si.bbqms.domain.Ticket;
|
||||
import ba.unsa.etf.si.bbqms.export.api.Exporter;
|
||||
import ba.unsa.etf.si.bbqms.repository.TicketRepository;
|
||||
import ba.unsa.etf.si.bbqms.ticket_service.api.TicketService;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.SimpleMessageDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TellerStationDto;
|
||||
import ba.unsa.etf.si.bbqms.ws.models.TicketDto;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/tickets")
|
||||
public class TicketController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(TicketController.class);
|
||||
private final TicketService ticketService;
|
||||
private final BranchService branchService;
|
||||
private final Exporter<Ticket> pdfExporter;
|
||||
|
||||
public TicketController(final TicketService ticketService,
|
||||
final BranchService branchService,
|
||||
final Exporter<Ticket> pdfExporter) {
|
||||
this.ticketService = ticketService;
|
||||
this.branchService = branchService;
|
||||
this.pdfExporter = pdfExporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ticket object, as well as all stations to which the visitor can potentially be routed to.
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity createNewTicket(@RequestBody final NewTicketRequest request) {
|
||||
try {
|
||||
final Ticket created = this.ticketService.create(request.serviceId(), request.branchId(), request.deviceToken());
|
||||
final Set<TellerStationDto> legibleStations = this.branchService.getStationsWithService(created.getBranch(), created.getService()).stream()
|
||||
.map(TellerStationDto::fromEntity)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return ResponseEntity.ok().body(new TicketResponse(
|
||||
TicketDto.fromEntity(created),
|
||||
legibleStations
|
||||
));
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{ticketId}")
|
||||
public ResponseEntity getTicketById(@PathVariable final String ticketId) {
|
||||
try {
|
||||
final Ticket ticket = this.ticketService.get(Long.parseLong(ticketId));
|
||||
return ResponseEntity.ok().body(TicketDto.fromEntity(ticket));
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/devices/{deviceToken}")
|
||||
public ResponseEntity getTicketsForDevice(@PathVariable final String deviceToken) {
|
||||
|
||||
final Set<TicketResponse> responses = new HashSet<>();
|
||||
for (final Ticket ticket : this.ticketService.getByDevice(deviceToken)) {
|
||||
final Set<TellerStationDto> legibleStations = this.branchService.getStationsWithService(ticket.getBranch(), ticket.getService()).stream()
|
||||
.map(TellerStationDto::fromEntity)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
responses.add(new TicketResponse(
|
||||
TicketDto.fromEntity(ticket),
|
||||
legibleStations
|
||||
));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok().body(responses);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{ticketId}")
|
||||
public ResponseEntity cancelTicket(@PathVariable final String ticketId) {
|
||||
try {
|
||||
this.ticketService.cancelTicket(Long.parseLong(ticketId));
|
||||
return ResponseEntity.ok().body(new SimpleMessageDto("Deleted ticket with id: " + ticketId));
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{ticketId}/print")
|
||||
public ResponseEntity printTicket(@PathVariable final String ticketId) {
|
||||
try {
|
||||
final Ticket ticket = this.ticketService.get(Long.parseLong(ticketId));
|
||||
final Resource pdfByteArray = this.pdfExporter.exportPdf(ticket);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + ticket.getId() + ".pdf")
|
||||
.contentType(MediaType.APPLICATION_PDF)
|
||||
.body(pdfByteArray);
|
||||
} catch (final Exception exception) {
|
||||
logger.error(exception.getMessage());
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("test")
|
||||
public List<TicketDto> test() {
|
||||
final TicketRepository ticketRepository = this.ticketService.unwrap(TicketRepository.class);
|
||||
|
||||
return ticketRepository.findByTellerStationIsNullOrderByCreatedAtAsc()
|
||||
.stream()
|
||||
.map(TicketDto::fromEntity)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public record NewTicketRequest(long branchId, long serviceId, String deviceToken) {
|
||||
}
|
||||
|
||||
public record TicketResponse(TicketDto ticket, Set<TellerStationDto> stations) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
import ba.unsa.etf.si.bbqms.domain.Tenant;
|
||||
|
||||
public record BranchDto(long id, String name, Tenant tenant) {
|
||||
public static BranchDto fromEntity(final Branch branch) {
|
||||
return new BranchDto(
|
||||
branch.getId(),
|
||||
branch.getName(),
|
||||
branch.getTenant()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public record BranchGroupCreateDto(String name, Set<Long> serviceIds, Set<Long> branchIds) {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
public record BranchGroupUpdateDto(String name) {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Branch;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public record BranchWithStationsDto(long id, String name, Set<TellerStationDto> tellerStations) {
|
||||
public static BranchWithStationsDto fromEntity(final Branch branch) {
|
||||
final Set<TellerStationDto> stations;
|
||||
if (branch.getTellerStations() != null) {
|
||||
stations = branch.getTellerStations().stream().map(TellerStationDto::fromEntity).collect(Collectors.toSet());
|
||||
} else {
|
||||
stations = null;
|
||||
}
|
||||
return new BranchWithStationsDto(
|
||||
branch.getId(),
|
||||
branch.getName(),
|
||||
stations
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Display;
|
||||
|
||||
public record DisplayDto(long id, String name, TellerStationDto tellerStation, BranchDto branch) {
|
||||
public static DisplayDto fromEntity(final Display display) {
|
||||
final TellerStationDto tellerStationDto;
|
||||
if (display.getTellerStation() == null) {
|
||||
tellerStationDto = null;
|
||||
}
|
||||
else {
|
||||
tellerStationDto = TellerStationDto.fromEntity(display.getTellerStation());
|
||||
}
|
||||
return new DisplayDto(
|
||||
display.getId(),
|
||||
display.getName(),
|
||||
tellerStationDto,
|
||||
BranchDto.fromEntity(display.getBranch())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
public record ErrorMessage(String error) {
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
public record ErrorResponseDto (String message) {}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ba.unsa.etf.si.bbqms.ws.models;
|
||||
|
||||
import ba.unsa.etf.si.bbqms.domain.Service;
|
||||
|
||||
public record ServiceDto(long id, String name) {
|
||||
public static ServiceDto fromEntity(final Service service) {
|
||||
return new ServiceDto(service.getId(), service.getName());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user