DONE: first init
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,38 @@
|
||||
# Bank Branch Queue Management System
|
||||
|
||||
This project aims to develop a comprehensive queue management system for bank branches, comprising three main interfaces: one for tellers, one for admins, and a mobile app for customers. These interfaces will connect to a backend system built using Java's Spring Boot framework.
|
||||
|
||||
## Interfaces
|
||||
|
||||
### Teller Interface
|
||||
The Teller Interface provides functionality for tellers to manage their availability status, allowing them to switch between available and non-available states. This interface will be developed using React.
|
||||
|
||||
### Admin Interface
|
||||
The Admin Interface is designed for administrators to oversee and manage both queues and tellers efficiently. Admins will have the ability to monitor queue statuses, assign tasks to tellers, and make necessary adjustments as needed. This interface will also be developed using React.
|
||||
|
||||
### Mobile App Interface
|
||||
The Mobile App Interface is catered towards customers visiting the bank branch. Customers can utilize the mobile app to generate a ticket for the queue, allowing them to efficiently manage their time while waiting for service. This interface will be developed using React Native, ensuring compatibility across both iOS and Android devices.
|
||||
|
||||
## Backend
|
||||
The backend of the queue management system will be developed using Java's Spring Boot framework. It will serve as the central component handling communication between the interfaces and managing the underlying data and business logic. The backend will be responsible for tasks such as processing queue requests, managing teller availability, and maintaining queue status updates.
|
||||
|
||||
## Setup Instructions
|
||||
1. Clone the repository to your local machine.
|
||||
2. Navigate to the respective directories for each interface and backend.
|
||||
3. Follow the setup instructions provided in each directory's README file to configure and run the application.
|
||||
|
||||
## Contributors
|
||||
- [Nejra Adilovic](https://github.com/nejradilovic)
|
||||
- [Emir Agovic](https://github.com/eagovic1)
|
||||
- [Eldar Babic](https://github.com/ebabic1)
|
||||
- [Edna Basic](https://github.com/Edna1010)
|
||||
- [Ali Boudellaa](https://github.com/boudellaa)
|
||||
- [Emina Efendic](https://github.com/efendicemina)
|
||||
- [Lejla Heleg](https://github.com/lheleg)
|
||||
- [Amina Hromic](https://github.com/Ahromic1)
|
||||
- [Aldin Islamagic](https://github.com/aislamagic1)
|
||||
- [Benjamin Kadic](https://github.com/BenjaminKadic)
|
||||
- [Emir Kapic](https://github.com/EmirKapic)
|
||||
- [Ahmed Ljubuncic](https://github.com/aljubuncic)
|
||||
- [Vedran Mujic](https://github.com/vmujic1)
|
||||
- [Amar Tahirovic](https://github.com/amarderschrecklicher)
|
||||
@@ -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) {
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user