diff options
| author | lucashemi <lucasxberger@gmail.com> | 2023-02-08 14:57:43 -0300 |
|---|---|---|
| committer | lucashemi <lucasxberger@gmail.com> | 2023-02-08 14:57:43 -0300 |
| commit | 514f2e7194a875cfc53d7e1bccd922db2bbb3f3f (patch) | |
| tree | 6b62dada02b9d02e624c40b7f3d4704537cbcb80 /api | |
readme
Diffstat (limited to 'api')
41 files changed, 1451 insertions, 0 deletions
diff --git a/api/.mvn/wrapper/maven-wrapper.jar b/api/.mvn/wrapper/maven-wrapper.jar Binary files differnew file mode 100644 index 0000000..c1dd12f --- /dev/null +++ b/api/.mvn/wrapper/maven-wrapper.jar diff --git a/api/.mvn/wrapper/maven-wrapper.properties b/api/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b74bf7f --- /dev/null +++ b/api/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/api/mvnw b/api/mvnw new file mode 100755 index 0000000..8a8fb22 --- /dev/null +++ b/api/mvnw @@ -0,0 +1,316 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# 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 + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -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 "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; 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; \\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 + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# 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/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# 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. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +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 "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -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 + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/api/mvnw.cmd b/api/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/api/mvnw.cmd @@ -0,0 +1,188 @@ +@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 Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@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 DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_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 DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_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('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@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% diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..d7bb09b --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,98 @@ +<?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.0.1</version> + <relativePath/> <!-- lookup parent from repository --> + </parent> + <groupId>med.voll</groupId> + <artifactId>api</artifactId> + <version>0.0.1-SNAPSHOT</version> + <name>api</name> + <description>API Rest da aplicacao Voll.med</description> + <properties> + <java.version>17</java.version> + </properties> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-jpa</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-validation</artifactId> + </dependency> + <dependency> + <groupId>org.flywaydb</groupId> + <artifactId>flyway-core</artifactId> + </dependency> + <dependency> + <groupId>org.flywaydb</groupId> + <artifactId>flyway-mysql</artifactId> + </dependency> + + <dependency> + <groupId>org.mariadb.jdbc</groupId> + <artifactId>mariadb-java-client</artifactId> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>4.2.1</version> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <scope>runtime</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <configuration> + <excludes> + <exclude> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/api/src/main/java/med/voll/api/ApiApplication.java b/api/src/main/java/med/voll/api/ApiApplication.java new file mode 100644 index 0000000..e08ef48 --- /dev/null +++ b/api/src/main/java/med/voll/api/ApiApplication.java @@ -0,0 +1,13 @@ +package med.voll.api; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ApiApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiApplication.class, args); + } + +} diff --git a/api/src/main/java/med/voll/api/controller/AuthenticationController.java b/api/src/main/java/med/voll/api/controller/AuthenticationController.java new file mode 100644 index 0000000..b7dcf27 --- /dev/null +++ b/api/src/main/java/med/voll/api/controller/AuthenticationController.java @@ -0,0 +1,37 @@ +package med.voll.api.controller; + +import jakarta.validation.Valid; +import med.voll.api.domain.user.AuthenticationData; +import med.voll.api.domain.user.User; +import med.voll.api.infra.security.TokenJWTData; +import med.voll.api.infra.security.TokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequestMapping("/login") +public class AuthenticationController { + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private TokenService tokenService; + + @PostMapping + public ResponseEntity performLogin(@RequestBody @Valid AuthenticationData data) { + var authenticationToken = new UsernamePasswordAuthenticationToken(data.login(), data.password()); + Authentication authentication = authenticationManager.authenticate(authenticationToken); + + var tokenJWT = tokenService.generateToken((User) authentication.getPrincipal()); + + return ResponseEntity.ok(new TokenJWTData(tokenJWT)); + } +} diff --git a/api/src/main/java/med/voll/api/controller/DoctorController.java b/api/src/main/java/med/voll/api/controller/DoctorController.java new file mode 100644 index 0000000..a262d34 --- /dev/null +++ b/api/src/main/java/med/voll/api/controller/DoctorController.java @@ -0,0 +1,66 @@ +package med.voll.api.controller; + +import jakarta.validation.Valid; +import med.voll.api.domain.doctor.DoctorListingData; +import med.voll.api.domain.doctor.Doctor; +import med.voll.api.domain.doctor.DoctorRepository; +import med.voll.api.domain.doctor.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +@RestController +@RequestMapping("/doctors") +public class DoctorController { + + @Autowired + private DoctorRepository doctorRepository; + + @PostMapping + @Transactional + public ResponseEntity register(@RequestBody @Valid DoctorRegistrationData data, UriComponentsBuilder uriBuilder) { + var doctor = new Doctor(data); + doctorRepository.save(doctor); + + var uri = uriBuilder.path("/doctors/{id}").buildAndExpand(doctor.getId()).toUri(); + + return ResponseEntity.created(uri).body(new DoctorDetailingData(doctor)); + } + + @GetMapping + public ResponseEntity<Page<DoctorListingData>> list(@PageableDefault(sort = {"name"}, direction = Sort.Direction.ASC) Pageable pagination) { + var page = doctorRepository.findAllByActiveTrue(pagination).map(DoctorListingData::new); + return ResponseEntity.ok(page); + } + + @PutMapping + @Transactional + public ResponseEntity update(@RequestBody @Valid DoctorUpdateData data) { + Doctor doctor = doctorRepository.getReferenceById(data.id()); + doctor.updateInformation(data); + + return ResponseEntity.ok(new DoctorDetailingData(doctor)); + } + + @DeleteMapping("/{id}") + @Transactional + public ResponseEntity delete(@PathVariable Long id) { + Doctor doctor = doctorRepository.getReferenceById(id); + doctor.delete(); + + return ResponseEntity.noContent().build(); + } + + @GetMapping("/{id}") + public ResponseEntity detail(@PathVariable Long id) { + Doctor doctor = doctorRepository.getReferenceById(id); + + return ResponseEntity.ok(new DoctorDetailingData(doctor)); + } +} diff --git a/api/src/main/java/med/voll/api/controller/PatientController.java b/api/src/main/java/med/voll/api/controller/PatientController.java new file mode 100644 index 0000000..bd774a1 --- /dev/null +++ b/api/src/main/java/med/voll/api/controller/PatientController.java @@ -0,0 +1,65 @@ +package med.voll.api.controller; + +import jakarta.validation.Valid; +import med.voll.api.domain.patient.PatientListingData; +import med.voll.api.domain.patient.Patient; +import med.voll.api.domain.patient.PatientRepository; +import med.voll.api.domain.patient.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +@RestController +@RequestMapping("/patients") +public class PatientController { + @Autowired + PatientRepository patientRepository; + + @PostMapping + @Transactional + public ResponseEntity register(@RequestBody @Valid PatientRegistrationData data, UriComponentsBuilder uriBuilder) { + var patient = new Patient(data); + patientRepository.save(patient); + + var uri = uriBuilder.path("/patients/{id}").buildAndExpand(patient.getId()).toUri(); + + return ResponseEntity.created(uri).body(new PatientDetailingData(patient)); + } + + @GetMapping + public ResponseEntity<Page<PatientListingData>> list(@PageableDefault(sort = {"name"}, direction = Sort.Direction.ASC) Pageable pagination) { + var page = patientRepository.findAllByActiveTrue(pagination).map(PatientListingData::new); + return ResponseEntity.ok(page); + } + + @PutMapping + @Transactional + public ResponseEntity update(@RequestBody @Valid PatientUpdateData data) { + Patient patient = patientRepository.getReferenceById(data.id()); + patient.updateInformation(data); + + return ResponseEntity.ok(new PatientDetailingData(patient)); + } + + @DeleteMapping("/{id}") + @Transactional + public ResponseEntity delete(@PathVariable Long id) { + Patient patient = patientRepository.getReferenceById(id); + patient.delete(); + + return ResponseEntity.noContent().build(); + } + + @GetMapping("/{id}") + public ResponseEntity detail(@PathVariable Long id) { + Patient patient = patientRepository.getReferenceById(id); + + return ResponseEntity.ok(new PatientDetailingData(patient)); + } +} diff --git a/api/src/main/java/med/voll/api/domain/address/Address.java b/api/src/main/java/med/voll/api/domain/address/Address.java new file mode 100644 index 0000000..b348d1f --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/address/Address.java @@ -0,0 +1,45 @@ +package med.voll.api.domain.address; + +import jakarta.persistence.Embeddable; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class Address { + + private String addressLine1; + private String addressLine2; + private String postalCode; + private String city; + private String state; + + public Address(AddressData data) { + this.addressLine1 = data.addressLine1(); + this.addressLine2 = data.addressLine2(); + this.postalCode = data.postalCode(); + this.city = data.city(); + this.state = data.state(); + } + + public void updateInformation(AddressData data) { + if (data.addressLine1() != null) { + this.addressLine1 = data.addressLine1(); + } + if (data.addressLine2() != null) { + this.addressLine2 = data.addressLine2(); + } + if (data.postalCode() != null) { + this.postalCode = data.postalCode(); + } + if (data.city() != null) { + this.city = data.city(); + } + if (data.state() != null) { + this.state = data.state(); + } + } +} diff --git a/api/src/main/java/med/voll/api/domain/address/AddressData.java b/api/src/main/java/med/voll/api/domain/address/AddressData.java new file mode 100644 index 0000000..386219b --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/address/AddressData.java @@ -0,0 +1,18 @@ +package med.voll.api.domain.address; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +public record AddressData( + @NotBlank(message = "Address1 is required") + String addressLine1, + String addressLine2, + @NotBlank(message = "Zip code is required") + @Pattern(regexp = "\\d{5}", message = "Zip code must have 9 digits") + String postalCode, + @NotBlank(message = "City is required") + String city, + @NotBlank(message = "State is required") + String state) { + +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/Doctor.java b/api/src/main/java/med/voll/api/domain/doctor/Doctor.java new file mode 100644 index 0000000..9c3f5f5 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/Doctor.java @@ -0,0 +1,58 @@ +package med.voll.api.domain.doctor; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import med.voll.api.domain.address.Address; + +@Table(name = "doctors") +@Entity(name = "Doctor") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = "id") +public class Doctor { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String email; + private String phone; + private Boolean active; + + @Enumerated(EnumType.STRING) + private Specialty specialty; + + @Embedded + private Address address; + + public Doctor(DoctorRegistrationData data) { + this.active = true; + this.name = data.name(); + this.email = data.email(); + this.phone = data.phone(); + this.specialty = data.specialty(); + this.address = new Address(data.addressData()); + } + + public void updateInformation(DoctorUpdateData data) { + if (data.name() != null) { + this.name = data.name(); + } + if (data.phone() != null) { + this.phone = data.phone(); + } + if (data.specialty() != null) { + this.specialty = data.specialty(); + } + if (data.addressData() != null) { + this.address.updateInformation(data.addressData()); + } + } + + public void delete() { + this.active = false; + } +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/DoctorDetailingData.java b/api/src/main/java/med/voll/api/domain/doctor/DoctorDetailingData.java new file mode 100644 index 0000000..fff6d9c --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/DoctorDetailingData.java @@ -0,0 +1,9 @@ +package med.voll.api.domain.doctor; + +import med.voll.api.domain.address.Address; + +public record DoctorDetailingData(Long id, String name, String email, String phone, Specialty specialty, Address address) { + public DoctorDetailingData(Doctor doctor) { + this(doctor.getId(), doctor.getName(), doctor.getEmail(), doctor.getPhone(), doctor.getSpecialty(), doctor.getAddress()); + } +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/DoctorListingData.java b/api/src/main/java/med/voll/api/domain/doctor/DoctorListingData.java new file mode 100644 index 0000000..ec0ea33 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/DoctorListingData.java @@ -0,0 +1,8 @@ +package med.voll.api.domain.doctor; + +public record DoctorListingData(Long id, String name, String email, Specialty specialty) { + + public DoctorListingData(Doctor doctor) { + this(doctor.getId(), doctor.getName(), doctor.getEmail(), doctor.getSpecialty()); + } +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/DoctorRegistrationData.java b/api/src/main/java/med/voll/api/domain/doctor/DoctorRegistrationData.java new file mode 100644 index 0000000..4ceaa2d --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/DoctorRegistrationData.java @@ -0,0 +1,23 @@ +package med.voll.api.domain.doctor; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import med.voll.api.domain.address.AddressData; + +public record DoctorRegistrationData( + @NotBlank(message = "Name is required") + String name, + @Email(message = "Invalid email format") + @NotBlank(message = "Email is required") + String email, + @Pattern(regexp = "\\d{10}", message = "Telephone number format is invalid") + @NotBlank(message = "Telephone number is required") + String phone, + @NotNull(message = "Specialty is required") + Specialty specialty, + @NotNull(message = "Address data is required") + @Valid AddressData addressData) { +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java b/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java new file mode 100644 index 0000000..1efd0af --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/DoctorRepository.java @@ -0,0 +1,11 @@ +package med.voll.api.domain.doctor; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface DoctorRepository extends JpaRepository<Doctor, Long> { + Page<Doctor> findAllByActiveTrue(Pageable pagination); +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/DoctorUpdateData.java b/api/src/main/java/med/voll/api/domain/doctor/DoctorUpdateData.java new file mode 100644 index 0000000..d3b9747 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/DoctorUpdateData.java @@ -0,0 +1,13 @@ +package med.voll.api.domain.doctor; + +import jakarta.validation.constraints.NotNull; +import med.voll.api.domain.address.AddressData; + +public record DoctorUpdateData( + @NotNull + Long id, + String name, + String phone, + Specialty specialty, + AddressData addressData) { +} diff --git a/api/src/main/java/med/voll/api/domain/doctor/Specialty.java b/api/src/main/java/med/voll/api/domain/doctor/Specialty.java new file mode 100644 index 0000000..7580089 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/doctor/Specialty.java @@ -0,0 +1,19 @@ +package med.voll.api.domain.doctor; + +public enum Specialty { + + ANESTHESIOLOGY, + CARDIOLOGY, + DERMATOLOGY, + GYNECOLOGY, + IMMUNOLOGY, + NEUROLOGY, + OPHTHALMOLOGY, + ORTHOPEDICS, + PATHOLOGY, + PEDIATRICS, + PSYCHIATRY, + RADIOLOGY, + SURGERY, + UROLOGY +} diff --git a/api/src/main/java/med/voll/api/domain/patient/Patient.java b/api/src/main/java/med/voll/api/domain/patient/Patient.java new file mode 100644 index 0000000..1e7562f --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/patient/Patient.java @@ -0,0 +1,53 @@ +package med.voll.api.domain.patient; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import med.voll.api.domain.address.Address; + +@Table(name = "patients") +@Entity(name = "Patient") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = "id") +public class Patient { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String email; + private String phone; + private String ssn; + private Boolean active; + + @Embedded + private Address address; + + public Patient(PatientRegistrationData data) { + this.active = true; + this.name = data.name(); + this.email = data.email(); + this.ssn = data.ssn(); + this.phone = data.phone(); + this.address = new Address(data.addressData()); + } + + public void updateInformation(PatientUpdateData data) { + if (data.name() != null) { + this.name = data.name(); + } + if (data.phone() != null) { + this.phone = data.phone(); + } + if (data.addressData() != null) { + this.address.updateInformation(data.addressData()); + } + } + + public void delete() { + this.active = false; + } +} diff --git a/api/src/main/java/med/voll/api/domain/patient/PatientDetailingData.java b/api/src/main/java/med/voll/api/domain/patient/PatientDetailingData.java new file mode 100644 index 0000000..76156be --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/patient/PatientDetailingData.java @@ -0,0 +1,9 @@ +package med.voll.api.domain.patient; + +import med.voll.api.domain.address.Address; + +public record PatientDetailingData(Long id, String name, String email, String phone, String ssn, Address address) { + public PatientDetailingData(Patient patient) { + this(patient.getId(), patient.getName(), patient.getEmail(), patient.getPhone(), patient.getSsn(), patient.getAddress()); + } +} diff --git a/api/src/main/java/med/voll/api/domain/patient/PatientListingData.java b/api/src/main/java/med/voll/api/domain/patient/PatientListingData.java new file mode 100644 index 0000000..1d4a15d --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/patient/PatientListingData.java @@ -0,0 +1,9 @@ +package med.voll.api.domain.patient; + +public record PatientListingData(Long id, String name, String phone) { + + public PatientListingData(Patient patient) { + this(patient.getId(), patient.getName(), patient.getPhone()); + } + +} diff --git a/api/src/main/java/med/voll/api/domain/patient/PatientRegistrationData.java b/api/src/main/java/med/voll/api/domain/patient/PatientRegistrationData.java new file mode 100644 index 0000000..ae1687d --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/patient/PatientRegistrationData.java @@ -0,0 +1,24 @@ +package med.voll.api.domain.patient; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import med.voll.api.domain.address.AddressData; + +public record PatientRegistrationData( + @NotBlank(message = "Name is required") + String name, + @NotBlank(message = "Email is required") + @Email(message = "Invalid email format") + String email, + @NotBlank(message = "Telephone number is required") + @Pattern(regexp = "\\d{10}", message = "Telephone number format is invalid") + String phone, + @NotBlank(message = "SSN is required") + String ssn, + @NotNull(message = "Address is required") + @Valid + AddressData addressData) { +} diff --git a/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java b/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java new file mode 100644 index 0000000..e550ac2 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/patient/PatientRepository.java @@ -0,0 +1,11 @@ +package med.voll.api.domain.patient; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface PatientRepository extends JpaRepository<Patient, Long> { + Page<Patient> findAllByActiveTrue(Pageable pagination); +} diff --git a/api/src/main/java/med/voll/api/domain/patient/PatientUpdateData.java b/api/src/main/java/med/voll/api/domain/patient/PatientUpdateData.java new file mode 100644 index 0000000..55bf145 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/patient/PatientUpdateData.java @@ -0,0 +1,12 @@ +package med.voll.api.domain.patient; + +import jakarta.validation.constraints.NotNull; +import med.voll.api.domain.address.AddressData; + +public record PatientUpdateData( + @NotNull + Long id, + String name, + String phone, + AddressData addressData) { +} diff --git a/api/src/main/java/med/voll/api/domain/user/AuthenticationData.java b/api/src/main/java/med/voll/api/domain/user/AuthenticationData.java new file mode 100644 index 0000000..ce753b9 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/user/AuthenticationData.java @@ -0,0 +1,6 @@ +package med.voll.api.domain.user; + +import jakarta.validation.constraints.NotBlank; + +public record AuthenticationData(@NotBlank(message = "Email is required") String login, @NotBlank(message = "Password is required") String password) { +} diff --git a/api/src/main/java/med/voll/api/domain/user/AuthenticationService.java b/api/src/main/java/med/voll/api/domain/user/AuthenticationService.java new file mode 100644 index 0000000..b089a9f --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/user/AuthenticationService.java @@ -0,0 +1,18 @@ +package med.voll.api.domain.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class AuthenticationService implements UserDetailsService { + @Autowired + private UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return userRepository.findByLogin(username); + } +} diff --git a/api/src/main/java/med/voll/api/domain/user/User.java b/api/src/main/java/med/voll/api/domain/user/User.java new file mode 100644 index 0000000..23d74e0 --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/user/User.java @@ -0,0 +1,63 @@ +package med.voll.api.domain.user; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +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.List; + +@Table(name = "users") +@Entity(name = "User") +@Getter +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = "id") +public class User implements UserDetails { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String login; + private String password; + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() { + return List.of(new SimpleGrantedAuthority("ROLE_USER")); + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.login; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/api/src/main/java/med/voll/api/domain/user/UserRepository.java b/api/src/main/java/med/voll/api/domain/user/UserRepository.java new file mode 100644 index 0000000..20ce55d --- /dev/null +++ b/api/src/main/java/med/voll/api/domain/user/UserRepository.java @@ -0,0 +1,8 @@ +package med.voll.api.domain.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.security.core.userdetails.UserDetails; + +public interface UserRepository extends JpaRepository<User, Long> { + UserDetails findByLogin(String login); +} diff --git a/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java b/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java new file mode 100644 index 0000000..9e49fd3 --- /dev/null +++ b/api/src/main/java/med/voll/api/infra/exception/ErrorTreatment.java @@ -0,0 +1,29 @@ +package med.voll.api.infra.exception; + +import jakarta.persistence.EntityNotFoundException; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ErrorTreatment { + + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity treatError404() { + return ResponseEntity.notFound().build(); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity treatError400(MethodArgumentNotValidException ex) { + var errors = ex.getFieldErrors(); + return ResponseEntity.badRequest().body(errors.stream().map(ValidationErrorData::new).toList()); + } + + private record ValidationErrorData(String field, String message) { + public ValidationErrorData(FieldError error) { + this(error.getField(), error.getDefaultMessage()); + } + } +} diff --git a/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java b/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java new file mode 100644 index 0000000..5055162 --- /dev/null +++ b/api/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java @@ -0,0 +1,44 @@ +package med.voll.api.infra.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +public class SecurityConfigurations { + + @Autowired + private SecurityFilter securityFilter; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + return httpSecurity.csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and().authorizeHttpRequests() + .requestMatchers(HttpMethod.POST, "/login").permitAll() + .anyRequest().authenticated() + .and().addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { + return authenticationConfiguration.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/api/src/main/java/med/voll/api/infra/security/SecurityFilter.java b/api/src/main/java/med/voll/api/infra/security/SecurityFilter.java new file mode 100644 index 0000000..42f3df5 --- /dev/null +++ b/api/src/main/java/med/voll/api/infra/security/SecurityFilter.java @@ -0,0 +1,48 @@ +package med.voll.api.infra.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import med.voll.api.domain.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +public class SecurityFilter extends OncePerRequestFilter { + + @Autowired + private TokenService tokenService; + + @Autowired + private UserRepository userRepository; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + var tokenJWT = retrieveToken(request); + + if (tokenJWT != null) { + var subject = tokenService.getSubject(tokenJWT); + var user = userRepository.findByLogin(subject); + + var authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(request, response); + } + + private String retrieveToken(HttpServletRequest request) { + var authorizationHeader = request.getHeader("Authorization"); + if (authorizationHeader != null) { + return authorizationHeader.replace("Bearer ", ""); + } + + return null; + } +} diff --git a/api/src/main/java/med/voll/api/infra/security/TokenJWTData.java b/api/src/main/java/med/voll/api/infra/security/TokenJWTData.java new file mode 100644 index 0000000..2be8aa8 --- /dev/null +++ b/api/src/main/java/med/voll/api/infra/security/TokenJWTData.java @@ -0,0 +1,4 @@ +package med.voll.api.infra.security; + +public record TokenJWTData(String token) { +} diff --git a/api/src/main/java/med/voll/api/infra/security/TokenService.java b/api/src/main/java/med/voll/api/infra/security/TokenService.java new file mode 100644 index 0000000..63dda73 --- /dev/null +++ b/api/src/main/java/med/voll/api/infra/security/TokenService.java @@ -0,0 +1,50 @@ +package med.voll.api.infra.security; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTCreationException; +import com.auth0.jwt.exceptions.JWTVerificationException; +import med.voll.api.domain.user.User; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +@Service +public class TokenService { + + @Value("${api.security.token.secret}") + private String secret; + + public String generateToken(User user) { + try { + var algorithm = Algorithm.HMAC256(secret); + return JWT.create() + .withIssuer("API Voll.med") + .withSubject(user.getLogin()) + .withExpiresAt(expirationDate()) + .sign(algorithm); + } catch (JWTCreationException exception){ + throw new RuntimeException("Error when generating token JWT", exception); + } + } + + public String getSubject(String tokenJWT) { + try { + var algorithm = Algorithm.HMAC256(secret); + return JWT.require(algorithm) + .withIssuer("API Voll.med") + .build() + .verify(tokenJWT) + .getSubject(); + } catch (JWTVerificationException exception){ + throw new RuntimeException("Invalid or expired TokenJWT!"); + } + } + + private Instant expirationDate() { + return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00")); + } +} diff --git a/api/src/main/resources/application.properties b/api/src/main/resources/application.properties new file mode 100644 index 0000000..5c91a19 --- /dev/null +++ b/api/src/main/resources/application.properties @@ -0,0 +1,16 @@ +# Port +server.port=8081 + +# Database config +spring.datasource.url=jdbc:mariadb://localhost/voll_med_api +spring.datasource.username=root +spring.datasource.password= + +# Show sql queries +#spring.jpa.show-sql=true +#spring.jpa.properties.hibernate.format_sql=true + +# Don't return stacktrace in error 500 +server.error.include-stacktrace=never + +api.security.token.secret=${JWT_SECRET:12345678}
\ No newline at end of file diff --git a/api/src/main/resources/db/migration/V1__create-table-doctors.sql b/api/src/main/resources/db/migration/V1__create-table-doctors.sql new file mode 100644 index 0000000..e4afbe8 --- /dev/null +++ b/api/src/main/resources/db/migration/V1__create-table-doctors.sql @@ -0,0 +1,15 @@ +create table doctors( + + id bigint not null auto_increment, + name varchar(100) not null, + email varchar(100) not null unique, + specialty varchar(100) not null, + address_line1 varchar(100) not null, + address_line2 varchar(100) not null, + postal_code varchar(9) not null, + state varchar(100) not null, + city varchar(100) not null, + + primary key(id) + +);
\ No newline at end of file diff --git a/api/src/main/resources/db/migration/V2__alter-table-doctors-column-telephone.sql b/api/src/main/resources/db/migration/V2__alter-table-doctors-column-telephone.sql new file mode 100644 index 0000000..3955b36 --- /dev/null +++ b/api/src/main/resources/db/migration/V2__alter-table-doctors-column-telephone.sql @@ -0,0 +1 @@ +alter table doctors add phone varchar(20) not null;
\ No newline at end of file diff --git a/api/src/main/resources/db/migration/V3__create-table-patients.sql b/api/src/main/resources/db/migration/V3__create-table-patients.sql new file mode 100644 index 0000000..a0cc01c --- /dev/null +++ b/api/src/main/resources/db/migration/V3__create-table-patients.sql @@ -0,0 +1,16 @@ +create table patients( + + id bigint not null auto_increment, + name varchar(100) not null, + email varchar(100) not null unique, + phone varchar(20) not null, + ssn varchar(11) not null unique, + address_line1 varchar(100) not null, + address_line2 varchar(100) not null, + postal_code varchar(9) not null, + state char(100) not null, + city varchar(100) not null, + + primary key(id) + +);
\ No newline at end of file diff --git a/api/src/main/resources/db/migration/V4__alter-table-doctors-add-column-active.sql b/api/src/main/resources/db/migration/V4__alter-table-doctors-add-column-active.sql new file mode 100644 index 0000000..bdbfa76 --- /dev/null +++ b/api/src/main/resources/db/migration/V4__alter-table-doctors-add-column-active.sql @@ -0,0 +1,2 @@ +alter table doctors add active tinyint; +update doctors set active = 1;
\ No newline at end of file diff --git a/api/src/main/resources/db/migration/V5__alter-table-patients-add-column-active.sql b/api/src/main/resources/db/migration/V5__alter-table-patients-add-column-active.sql new file mode 100644 index 0000000..c6432e6 --- /dev/null +++ b/api/src/main/resources/db/migration/V5__alter-table-patients-add-column-active.sql @@ -0,0 +1,2 @@ +alter table patients add active tinyint; +update patients set active = 1;
\ No newline at end of file diff --git a/api/src/main/resources/db/migration/V6__create-table-users.sql b/api/src/main/resources/db/migration/V6__create-table-users.sql new file mode 100644 index 0000000..07d3d79 --- /dev/null +++ b/api/src/main/resources/db/migration/V6__create-table-users.sql @@ -0,0 +1,9 @@ +create table users +( + id bigint not null auto_increment, + login varchar(100) not null, + password varchar(255) not null, + + primary key (id) + +);
\ No newline at end of file diff --git a/api/src/test/java/med/voll/api/ApiApplicationTests.java b/api/src/test/java/med/voll/api/ApiApplicationTests.java new file mode 100644 index 0000000..eb360a5 --- /dev/null +++ b/api/src/test/java/med/voll/api/ApiApplicationTests.java @@ -0,0 +1,13 @@ +package med.voll.api; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApiApplicationTests { + + @Test + void contextLoads() { + } + +} |
