commit 58806b8e3abc9e69f070c611bc11e8b7333b7e1d Author: Camille Petitalot Date: Fri Sep 1 15:38:21 2023 +0200 :sparkles: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f038488 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ + +# Others +pgdata/ +mongodata/ +swagger/ \ No newline at end of file diff --git a/.mvn/wrapper/.gitignore b/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..84d1e60 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,98 @@ +/* + * 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 + * + * http://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. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +public final class MavenWrapperDownloader +{ + private static final String WRAPPER_VERSION = "3.2.0"; + + private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) ); + + public static void main( String[] args ) + { + log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION ); + + if ( args.length != 2 ) + { + System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" ); + System.exit( 1 ); + } + + try + { + log( " - Downloader started" ); + final URL wrapperUrl = new URL( args[0] ); + final String jarPath = args[1].replace( "..", "" ); // Sanitize path + final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize(); + downloadFileFromURL( wrapperUrl, wrapperJarPath ); + log( "Done" ); + } + catch ( IOException e ) + { + System.err.println( "- Error downloading: " + e.getMessage() ); + if ( VERBOSE ) + { + e.printStackTrace(); + } + System.exit( 1 ); + } + } + + private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath ) + throws IOException + { + log( " - Downloading to: " + wrapperJarPath ); + if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) + { + final String username = System.getenv( "MVNW_USERNAME" ); + final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); + Authenticator.setDefault( new Authenticator() + { + @Override + protected PasswordAuthentication getPasswordAuthentication() + { + return new PasswordAuthentication( username, password ); + } + } ); + } + try ( InputStream inStream = wrapperUrl.openStream() ) + { + Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING ); + } + log( " - Downloader complete" ); + } + + private static void log( String msg ) + { + if ( VERBOSE ) + { + System.out.println( msg ); + } + } + +} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..6d3a566 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# 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 +# +# http://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. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/README.md b/README.md new file mode 100644 index 0000000..d5ec58d --- /dev/null +++ b/README.md @@ -0,0 +1,189 @@ + + + + + + + + + +
+
+Logo + +

Personal Library Manager API

+ +

+ This API is designed to help you list all your books. +
+
+

+
+ + + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. + Getting Started + +
  4. +
  5. Usage
  6. +
+
+ + + + + +## About The Project + +This API helps you to manage your books. You can add new ones, list yours, loan or borrow some others. + +

(back to top)

+ +### Built With + +* ![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white) +* ![Quarkus](https://img.shields.io/badge/quarkus-grey.svg?style=for-the-badge&logo=quarkus) +* ![MongoDB](https://img.shields.io/badge/mongodb-darkgreen.svg?style=for-the-badge&logo=mongodb&logoColor=white) +* ![PostgreSQL](https://img.shields.io/badge/postgresql-blue.svg?style=for-the-badge&logo=postgresql&logoColor=white) + +

(back to top)

+ + + + +## Getting Started + +To get a local copy up and running follow these simple example steps. + +### Prerequisites + +* Java (version >= 17) +* Maven (version == 3.8.4) +* MongoDB (version == 7) +* Postgres (version == 14) +* IntelliJ Ultimate Edition to use included tests files + +### Local Installation + +1. Clone the repo + ```sh + git clone TODO + ``` +2. Create a local `.env` file at your project root and fill these variables + ```properties + MONGODB_CONNECTION_STRING= + MONGODB_DATABASE_NAME= + PG_USERNAME= + PG_PASSWORD= + PG_HOST= + ``` +3. Build a local version of the application + ``` + mvn clean package + ``` + It will generate your compiled application into target/quarkus-app directory and your Swagger documentation files into the swagger directory. +4. Run tests locally and publish sonar reports + ``` + mvn verify sonar:sonar + ``` + +

(back to top)

+ + + + +## Usage + +_For more examples, please refer to the Swagger [Documentation](https://codefirst.iut.uca.fr/swagger?url=/documentation/PROJECT_OWNER/swagger/PROJECT_NAME/swagger.json)_ + +

(back to top)

+ + + + + +[contributors-shield]: https://img.shields.io/github/contributors/github_username/repo_name.svg?style=for-the-badge + +[contributors-url]: https://github.com/github_username/repo_name/graphs/contributors + +[forks-shield]: https://img.shields.io/github/forks/github_username/repo_name.svg?style=for-the-badge + +[forks-url]: https://github.com/github_username/repo_name/network/members + +[stars-shield]: https://img.shields.io/github/stars/github_username/repo_name.svg?style=for-the-badge + +[stars-url]: https://github.com/github_username/repo_name/stargazers + +[issues-shield]: https://img.shields.io/github/issues/github_username/repo_name.svg?style=for-the-badge + +[issues-url]: https://github.com/github_username/repo_name/issues + +[license-shield]: https://img.shields.io/github/license/github_username/repo_name.svg?style=for-the-badge + +[license-url]: https://github.com/github_username/repo_name/blob/master/LICENSE.txt + +[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 + +[linkedin-url]: https://linkedin.com/in/linkedin_username + +[product-screenshot]: images/library.png + +[Java]: https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white + +[Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white + +[Next-url]: https://nextjs.org/ + +[React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB + +[React-url]: https://reactjs.org/ + +[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D + +[Vue-url]: https://vuejs.org/ + +[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white + +[Angular-url]: https://angular.io/ + +[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00 + +[Svelte-url]: https://svelte.dev/ + +[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white + +[Laravel-url]: https://laravel.com + +[Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white + +[Bootstrap-url]: https://getbootstrap.com + +[JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white + +[JQuery-url]: https://jquery.com \ No newline at end of file diff --git a/images/library.png b/images/library.png new file mode 100644 index 0000000..e5132a2 Binary files /dev/null and b/images/library.png differ diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..8d937f4 --- /dev/null +++ b/mvnw @@ -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 +# +# http://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 "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..c4586b5 --- /dev/null +++ b/mvnw.cmd @@ -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 http://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% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c889797 --- /dev/null +++ b/pom.xml @@ -0,0 +1,230 @@ + + + 4.0.0 + fr.iut + library-borrowing-api + 1.0-SNAPSHOT + + 3.11.0 + 17 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.3.1 + true + 3.1.2 + 1.18.28 + 1.5.5.Final + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-mongodb-panache + + + io.quarkus + quarkus-resteasy-reactive + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-arc + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-junit5-mockito + test + + + io.quarkus + quarkus-jdbc-h2 + test + + + io.quarkus + quarkus-test-h2 + test + + + io.quarkus + quarkus-test-mongodb + test + + + + io.quarkus + quarkus-liquibase-mongodb + 3.2.2.Final + + + io.quarkus + quarkus-elytron-security-jdbc + + + io.quarkus + quarkus-flyway + + + + + + smallrye-open-api-maven-plugin + io.smallrye + + ${project.basedir}/swagger/ + swagger + + + + compile + + generate-schema + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + ${maven.compiler.parameters} + + + + io.quarkus + quarkus-panache-common + ${quarkus.platform.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native + + + native + + + + false + native + + + + diff --git a/src/main/java/fr/iut/cicd/App.java b/src/main/java/fr/iut/cicd/App.java new file mode 100644 index 0000000..a8963aa --- /dev/null +++ b/src/main/java/fr/iut/cicd/App.java @@ -0,0 +1,44 @@ +package fr.iut.cicd; + +import com.mongodb.client.MongoClient; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.event.Observes; +import jakarta.ws.rs.core.Application; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.info.Info; +import org.eclipse.microprofile.openapi.annotations.info.License; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +@OpenAPIDefinition( + tags = { + @Tag(name = "Borrowings", description = "Borrowings operations."), + @Tag(name = "Loans", description = "Loans operations."), + @Tag(name = "Security", description = "Security operations."), + }, + info = @Info( + title = "Library Java API", + version = "1.0.0", + license = @License( + name = "Apache 2.0", + url = "https://www.apache.org/licenses/LICENSE-2.0.html") + ) +) +@Slf4j +public class App extends Application { + + void onStart(@Observes StartupEvent ev) { + // For singleton injection is enough +// if (ConfigProvider.getConfig().getValue("database-mongodb.migrate-at-start", Boolean.class)) { +// var databaseName = ConfigProvider.getConfig().getValue("quarkus.mongodb.database", String.class); +// mongoClient.getDatabase(databaseName) +// .listCollectionNames() +// .forEach(elt-> log.info("{} collection found !", elt)); +// } else { +// log.info("Mongo migration is disable at startup"); +// } + // For @ApplicationScoped beans a client proxy is injected and so we need to invoke some method + // to force bean instantiation + } + +} \ No newline at end of file diff --git a/src/main/java/fr/iut/cicd/controller/BorrowingController.java b/src/main/java/fr/iut/cicd/controller/BorrowingController.java new file mode 100644 index 0000000..ee93f09 --- /dev/null +++ b/src/main/java/fr/iut/cicd/controller/BorrowingController.java @@ -0,0 +1,70 @@ +package fr.iut.cicd.controller; + + +import fr.iut.cicd.dto.BorrowingDTO; +import fr.iut.cicd.dto.CreateBorrowingDTO; +import fr.iut.cicd.dto.EndBorrowingDTO; +import fr.iut.cicd.exception.MandatoryParameterException; +import fr.iut.cicd.mapper.BookMapper; +import fr.iut.cicd.mapper.BorrowingMapper; +import fr.iut.cicd.mapper.ObjectIdMapper; +import fr.iut.cicd.services.BorrowingService; +import io.quarkus.security.Authenticated; +import io.quarkus.security.identity.SecurityIdentity; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import java.util.Date; +import java.util.List; + +@Tag(name = "Borrowings") +@Path("/borrowings") +@Authenticated +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class BorrowingController { + + @Inject + BorrowingMapper mapper; + + @Inject + BookMapper bookMapper; + + @Inject + ObjectIdMapper objectIdMapper; + + @Inject + BorrowingService service; + + @Inject + SecurityIdentity securityIdentity; + + @POST + public BorrowingDTO create(CreateBorrowingDTO dto) { + if (dto == null) { + throw new MandatoryParameterException("Request body"); + } + if (dto.getBook() == null || dto.getBook().getId() == null) { + throw new MandatoryParameterException("book"); + } + return mapper.toDTO(service.create(bookMapper.toDomain(dto.getBook()), dto.getAt(), getConnectedUsername())); + } + + @POST + @Path("/{id}/return") + public BorrowingDTO returned(@PathParam("id") String borrowingId, EndBorrowingDTO dto) { + var dateToUse = dto == null || dto.getReturnedAt() == null ? new Date() : dto.getReturnedAt(); + return mapper.toDTO(service.returned(objectIdMapper.toObjectId(borrowingId), dateToUse, getConnectedUsername())); + } + + @GET + public List list() { + return mapper.toDTO(service.list(securityIdentity.getPrincipal().getName())); + } + + private String getConnectedUsername() { + return securityIdentity.getPrincipal().getName(); + } +} diff --git a/src/main/java/fr/iut/cicd/controller/LibraryController.java b/src/main/java/fr/iut/cicd/controller/LibraryController.java new file mode 100644 index 0000000..525e520 --- /dev/null +++ b/src/main/java/fr/iut/cicd/controller/LibraryController.java @@ -0,0 +1,78 @@ +package fr.iut.cicd.controller; + + +import fr.iut.cicd.dto.CreateLibraryEntryDTO; +import fr.iut.cicd.dto.LibraryEntryDTO; +import fr.iut.cicd.exception.MandatoryParameterException; +import fr.iut.cicd.mapper.LibraryEntryMapper; +import fr.iut.cicd.mapper.ObjectIdMapper; +import fr.iut.cicd.services.LibraryService; +import io.quarkus.security.Authenticated; +import io.quarkus.security.identity.SecurityIdentity; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import java.util.List; + +@Tag(name = "Borrowings") +@Path("/library") +@Authenticated +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class LibraryController { + + @Inject + LibraryEntryMapper mapper; + + @Inject + ObjectIdMapper objectIdMapper; + + @Inject + LibraryService service; + + @Inject + SecurityIdentity securityIdentity; + + @POST + @Path("/books") + public LibraryEntryDTO create(CreateLibraryEntryDTO dto) { + if (dto == null) { + throw new MandatoryParameterException("Request body"); + } + if (dto.getBook() == null || dto.getBook().getId() == null) { + throw new MandatoryParameterException("book"); + } + return mapper.toDTO(service.create(mapper.toDomain(dto), getConnectedUsername())); + } + + @PUT + @Path("/books/{libraryEntryId}") + public LibraryEntryDTO create(@PathParam("libraryEntryId") String id, CreateLibraryEntryDTO dto) { + if (dto == null) { + throw new MandatoryParameterException("Request body"); + } + if (dto.getBook() == null || dto.getBook().getId() == null) { + throw new MandatoryParameterException("book"); + } + return mapper.toDTO(service.update(mapper.toDomain(dto).setId(objectIdMapper.toObjectId(id)), getConnectedUsername())); + } + + @DELETE + @Path("/books/{libraryEntryId}") + public void delete(@PathParam("libraryEntryId") String id) { + service.delete(objectIdMapper.toObjectId(id), getConnectedUsername()); + } + + + @GET + @Path("/books") + public List list() { + return mapper.toDTO(service.list(getConnectedUsername())); + } + + private String getConnectedUsername() { + return securityIdentity.getPrincipal().getName(); + } +} diff --git a/src/main/java/fr/iut/cicd/controller/LoanController.java b/src/main/java/fr/iut/cicd/controller/LoanController.java new file mode 100644 index 0000000..62cfe43 --- /dev/null +++ b/src/main/java/fr/iut/cicd/controller/LoanController.java @@ -0,0 +1,73 @@ +package fr.iut.cicd.controller; + + +import fr.iut.cicd.dto.CreateLoanDTO; +import fr.iut.cicd.dto.EndLoanDTO; +import fr.iut.cicd.dto.LoanDTO; +import fr.iut.cicd.exception.MandatoryParameterException; +import fr.iut.cicd.mapper.BookMapper; +import fr.iut.cicd.mapper.LoanMapper; +import fr.iut.cicd.mapper.ObjectIdMapper; +import fr.iut.cicd.services.LoanService; +import io.quarkus.security.Authenticated; +import io.quarkus.security.identity.SecurityIdentity; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import java.util.Date; +import java.util.List; + +@Tag(name = "Loans") +@Path("/loans") +@Authenticated +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class LoanController { + + @Inject + LoanMapper mapper; + + @Inject + BookMapper bookMapper; + + @Inject + ObjectIdMapper objectIdMapper; + + @Inject + LoanService service; + + @Inject + SecurityIdentity securityIdentity; + + @POST + public LoanDTO create(CreateLoanDTO dto) { + if (dto == null) { + throw new MandatoryParameterException("Request body"); + } + if (dto.getTo() == null) { + throw new MandatoryParameterException("to"); + } + if (dto.getBook() == null || dto.getBook().getId() == null) { + throw new MandatoryParameterException("book"); + } + return mapper.toDTO(service.create(bookMapper.toDomain(dto.getBook()), dto.getAt(), dto.getTo(), getConnectedUsername())); + } + + @POST + @Path("/{id}/return") + public LoanDTO returned(@PathParam("id") String borrowingId, EndLoanDTO dto) { + var dateToUse = dto == null || dto.getReturnedAt() == null ? new Date() : dto.getReturnedAt(); + return mapper.toDTO(service.returned(objectIdMapper.toObjectId(borrowingId), dateToUse, getConnectedUsername())); + } + + @GET + public List list() { + return mapper.toDTO(service.list(securityIdentity.getPrincipal().getName())); + } + + private String getConnectedUsername() { + return securityIdentity.getPrincipal().getName(); + } +} diff --git a/src/main/java/fr/iut/cicd/controller/SecurityController.java b/src/main/java/fr/iut/cicd/controller/SecurityController.java new file mode 100644 index 0000000..c97faba --- /dev/null +++ b/src/main/java/fr/iut/cicd/controller/SecurityController.java @@ -0,0 +1,38 @@ +package fr.iut.cicd.controller; + +import fr.iut.cicd.dto.FormAuthenticationDTO; +import fr.iut.cicd.services.UserService; +import jakarta.annotation.security.PermitAll; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +@Tag(name = "Security") +@PermitAll +@Path("/users") +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class SecurityController { + + + @Inject + UserService service; + + @POST + @Path("/register") + public void register(FormAuthenticationDTO dto) { + service.create(dto.getUsername(), dto.getPassword()); + } +// +// @POST +// @Path("/login") +// public TokenDTO login(FormAuthenticationDTO dto) { +// return new TokenDTO(); +// } +// + +} diff --git a/src/main/java/fr/iut/cicd/domain/Book.java b/src/main/java/fr/iut/cicd/domain/Book.java new file mode 100644 index 0000000..1a37e98 --- /dev/null +++ b/src/main/java/fr/iut/cicd/domain/Book.java @@ -0,0 +1,22 @@ +package fr.iut.cicd.domain; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.Date; +import java.util.List; + +@Data +@Accessors(chain = true) +public class Book { + private String id; + private String title; + private List publishers; + private Date publishDate; + private String isbn13; + private List series; + private Integer nbPages; + private String imageSmall; + private String imageMedium; + private String imageLarge; +} diff --git a/src/main/java/fr/iut/cicd/domain/Borrowing.java b/src/main/java/fr/iut/cicd/domain/Borrowing.java new file mode 100644 index 0000000..da67604 --- /dev/null +++ b/src/main/java/fr/iut/cicd/domain/Borrowing.java @@ -0,0 +1,22 @@ +package fr.iut.cicd.domain; + +import io.quarkus.mongodb.panache.common.MongoEntity; +import lombok.Data; +import lombok.experimental.Accessors; +import org.bson.codecs.pojo.annotations.BsonId; +import org.bson.types.ObjectId; + +import java.util.Date; + +@Data +@Accessors(chain = true) +@MongoEntity(collection = "borrowings") +public class Borrowing { + @BsonId + private ObjectId id; + private Date at; + private Date returnedAt; + private String userId; + private Book book; + +} diff --git a/src/main/java/fr/iut/cicd/domain/LibraryEntry.java b/src/main/java/fr/iut/cicd/domain/LibraryEntry.java new file mode 100644 index 0000000..ce1cf23 --- /dev/null +++ b/src/main/java/fr/iut/cicd/domain/LibraryEntry.java @@ -0,0 +1,18 @@ +package fr.iut.cicd.domain; + +import io.quarkus.mongodb.panache.common.MongoEntity; +import lombok.Data; +import lombok.experimental.Accessors; +import org.bson.codecs.pojo.annotations.BsonId; +import org.bson.types.ObjectId; + +@Data +@Accessors(chain = true) +@MongoEntity(collection = "library") +public class LibraryEntry { + @BsonId + private ObjectId id; + private String userId; + private Book book; + +} diff --git a/src/main/java/fr/iut/cicd/domain/Loan.java b/src/main/java/fr/iut/cicd/domain/Loan.java new file mode 100644 index 0000000..a2e5af3 --- /dev/null +++ b/src/main/java/fr/iut/cicd/domain/Loan.java @@ -0,0 +1,23 @@ +package fr.iut.cicd.domain; + +import io.quarkus.mongodb.panache.common.MongoEntity; +import lombok.Data; +import lombok.experimental.Accessors; +import org.bson.codecs.pojo.annotations.BsonId; +import org.bson.types.ObjectId; + +import java.util.Date; + +@Data +@Accessors(chain = true) +@MongoEntity(collection = "loans") +public class Loan { + @BsonId + private ObjectId id; + private Date at; + private Date returnedAt; + private String userId; + private String to; + private Book book; + +} diff --git a/src/main/java/fr/iut/cicd/domain/User.java b/src/main/java/fr/iut/cicd/domain/User.java new file mode 100644 index 0000000..72eb762 --- /dev/null +++ b/src/main/java/fr/iut/cicd/domain/User.java @@ -0,0 +1,17 @@ +package fr.iut.cicd.domain; + +import io.quarkus.hibernate.orm.panache.PanacheEntity; +import jakarta.persistence.Entity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@Entity(name = "users") +@EqualsAndHashCode(callSuper = true) +public class User extends PanacheEntity { + private String username; + private String password; + private String role; +} diff --git a/src/main/java/fr/iut/cicd/dto/BookDTO.java b/src/main/java/fr/iut/cicd/dto/BookDTO.java new file mode 100644 index 0000000..577fb5e --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/BookDTO.java @@ -0,0 +1,24 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.Date; +import java.util.List; + +@Data +@Accessors(chain = true) +public class BookDTO { + @Schema(required = true) + private String id; + private String title; + private List publishers; + private Date publishDate; + private String isbn13; + private List series; + private Integer nbPages; + private String imageSmall; + private String imageMedium; + private String imageLarge; +} diff --git a/src/main/java/fr/iut/cicd/dto/BorrowingDTO.java b/src/main/java/fr/iut/cicd/dto/BorrowingDTO.java new file mode 100644 index 0000000..040c321 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/BorrowingDTO.java @@ -0,0 +1,17 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class BorrowingDTO extends CreateBorrowingDTO { + private String id; + private String userId; + private Date returnedAt; + +} diff --git a/src/main/java/fr/iut/cicd/dto/CreateBorrowingDTO.java b/src/main/java/fr/iut/cicd/dto/CreateBorrowingDTO.java new file mode 100644 index 0000000..43e36c9 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/CreateBorrowingDTO.java @@ -0,0 +1,17 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.Date; + +@Data +@Accessors(chain = true) +public class CreateBorrowingDTO { + @Schema(required = true) + private BookDTO book; + + private Date at; + +} diff --git a/src/main/java/fr/iut/cicd/dto/CreateLibraryEntryDTO.java b/src/main/java/fr/iut/cicd/dto/CreateLibraryEntryDTO.java new file mode 100644 index 0000000..743c2ec --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/CreateLibraryEntryDTO.java @@ -0,0 +1,11 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class CreateLibraryEntryDTO { + private BookDTO book; + +} diff --git a/src/main/java/fr/iut/cicd/dto/CreateLoanDTO.java b/src/main/java/fr/iut/cicd/dto/CreateLoanDTO.java new file mode 100644 index 0000000..36b74bf --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/CreateLoanDTO.java @@ -0,0 +1,20 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.Date; + +@Data +@Accessors(chain = true) +public class CreateLoanDTO { + @Schema(required = true) + private BookDTO book; + + @Schema(required = true, description = "User name of the borrower.") + private String to; + + private Date at; + +} diff --git a/src/main/java/fr/iut/cicd/dto/EndBorrowingDTO.java b/src/main/java/fr/iut/cicd/dto/EndBorrowingDTO.java new file mode 100644 index 0000000..a4ef5d6 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/EndBorrowingDTO.java @@ -0,0 +1,15 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.Date; + +@Data +@Accessors(chain = true) +public class EndBorrowingDTO { + @Schema(description = "Today if not filled.") + private Date returnedAt; + +} diff --git a/src/main/java/fr/iut/cicd/dto/EndLoanDTO.java b/src/main/java/fr/iut/cicd/dto/EndLoanDTO.java new file mode 100644 index 0000000..7e94f35 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/EndLoanDTO.java @@ -0,0 +1,15 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.Date; + +@Data +@Accessors(chain = true) +public class EndLoanDTO { + @Schema(description = "Today if not filled.") + private Date returnedAt; + +} diff --git a/src/main/java/fr/iut/cicd/dto/FormAuthenticationDTO.java b/src/main/java/fr/iut/cicd/dto/FormAuthenticationDTO.java new file mode 100644 index 0000000..f89ee65 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/FormAuthenticationDTO.java @@ -0,0 +1,11 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class FormAuthenticationDTO { + private String password; + private String username; +} diff --git a/src/main/java/fr/iut/cicd/dto/LibraryEntryDTO.java b/src/main/java/fr/iut/cicd/dto/LibraryEntryDTO.java new file mode 100644 index 0000000..55aa3f4 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/LibraryEntryDTO.java @@ -0,0 +1,13 @@ +package fr.iut.cicd.dto; + +import fr.iut.cicd.domain.Book; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class LibraryEntryDTO { + private String id; + private BookDTO book; + +} diff --git a/src/main/java/fr/iut/cicd/dto/LoanDTO.java b/src/main/java/fr/iut/cicd/dto/LoanDTO.java new file mode 100644 index 0000000..609110e --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/LoanDTO.java @@ -0,0 +1,17 @@ +package fr.iut.cicd.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +public class LoanDTO extends CreateLoanDTO { + private String id; + private String userId; + private Date returnedAt; + +} diff --git a/src/main/java/fr/iut/cicd/dto/TokenDTO.java b/src/main/java/fr/iut/cicd/dto/TokenDTO.java new file mode 100644 index 0000000..e1c7669 --- /dev/null +++ b/src/main/java/fr/iut/cicd/dto/TokenDTO.java @@ -0,0 +1,10 @@ +//package fr.iut.cicd.dto; +// +//import lombok.Data; +//import lombok.experimental.Accessors; +// +//@Data +//@Accessors(chain = true) +//public class TokenDTO { +// private String token; +//} diff --git a/src/main/java/fr/iut/cicd/exception/BadParameterException.java b/src/main/java/fr/iut/cicd/exception/BadParameterException.java new file mode 100644 index 0000000..268c435 --- /dev/null +++ b/src/main/java/fr/iut/cicd/exception/BadParameterException.java @@ -0,0 +1,10 @@ +package fr.iut.cicd.exception; + +public class BadParameterException extends RuntimeException { + + private static final String MESSAGE_FORMAT = "Please check %s parameter's value !"; + + public BadParameterException(String paramName) { + super(String.format(MESSAGE_FORMAT, paramName)); + } +} diff --git a/src/main/java/fr/iut/cicd/exception/ExceptionHandler.java b/src/main/java/fr/iut/cicd/exception/ExceptionHandler.java new file mode 100644 index 0000000..dba9edc --- /dev/null +++ b/src/main/java/fr/iut/cicd/exception/ExceptionHandler.java @@ -0,0 +1,26 @@ +package fr.iut.cicd.exception; + +import fr.iut.cicd.exception.dto.ErrorDTO; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; +import lombok.extern.slf4j.Slf4j; + +@Provider +@Slf4j +public class ExceptionHandler implements ExceptionMapper { + + @Override + public Response toResponse(Exception e) { + if (e instanceof BadParameterException + | e instanceof MandatoryParameterException) { + return Response.status(Response.Status.BAD_REQUEST) + .entity(new ErrorDTO().setError(e.getMessage())) + .build(); + } else { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(new ErrorDTO().setError(e.getMessage())) + .build(); + } + } +} diff --git a/src/main/java/fr/iut/cicd/exception/MandatoryParameterException.java b/src/main/java/fr/iut/cicd/exception/MandatoryParameterException.java new file mode 100644 index 0000000..87c4c5a --- /dev/null +++ b/src/main/java/fr/iut/cicd/exception/MandatoryParameterException.java @@ -0,0 +1,10 @@ +package fr.iut.cicd.exception; + +public class MandatoryParameterException extends RuntimeException { + + private static final String MESSAGE_FORMAT = "%s parameter's value must not be empty !"; + + public MandatoryParameterException(String paramName) { + super(String.format(MESSAGE_FORMAT, paramName)); + } +} diff --git a/src/main/java/fr/iut/cicd/exception/dto/ErrorDTO.java b/src/main/java/fr/iut/cicd/exception/dto/ErrorDTO.java new file mode 100644 index 0000000..99df2cf --- /dev/null +++ b/src/main/java/fr/iut/cicd/exception/dto/ErrorDTO.java @@ -0,0 +1,10 @@ +package fr.iut.cicd.exception.dto; + +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class ErrorDTO { + private String error; +} diff --git a/src/main/java/fr/iut/cicd/mapper/BookMapper.java b/src/main/java/fr/iut/cicd/mapper/BookMapper.java new file mode 100644 index 0000000..572c6c9 --- /dev/null +++ b/src/main/java/fr/iut/cicd/mapper/BookMapper.java @@ -0,0 +1,20 @@ +package fr.iut.cicd.mapper; + +import fr.iut.cicd.domain.Book; +import fr.iut.cicd.dto.BookDTO; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +import java.util.List; + +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA, uses = ObjectIdMapper.class) +public interface BookMapper { + + BookDTO toDTO(Book domain); + + List toDTO(List domain); + + Book toDomain(BookDTO dto); + + List toDomain(List dto); +} diff --git a/src/main/java/fr/iut/cicd/mapper/BorrowingMapper.java b/src/main/java/fr/iut/cicd/mapper/BorrowingMapper.java new file mode 100644 index 0000000..c489cec --- /dev/null +++ b/src/main/java/fr/iut/cicd/mapper/BorrowingMapper.java @@ -0,0 +1,20 @@ +package fr.iut.cicd.mapper; + +import fr.iut.cicd.domain.Borrowing; +import fr.iut.cicd.dto.BorrowingDTO; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +import java.util.List; + +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA, uses = ObjectIdMapper.class) +public interface BorrowingMapper { + + BorrowingDTO toDTO(Borrowing domain); + + List toDTO(List domain); + + Borrowing toDomain(BorrowingDTO dto); + + List toDomain(List dto); +} diff --git a/src/main/java/fr/iut/cicd/mapper/LibraryEntryMapper.java b/src/main/java/fr/iut/cicd/mapper/LibraryEntryMapper.java new file mode 100644 index 0000000..759ab3b --- /dev/null +++ b/src/main/java/fr/iut/cicd/mapper/LibraryEntryMapper.java @@ -0,0 +1,22 @@ +package fr.iut.cicd.mapper; + +import fr.iut.cicd.domain.LibraryEntry; +import fr.iut.cicd.dto.CreateLibraryEntryDTO; +import fr.iut.cicd.dto.LibraryEntryDTO; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +import java.util.List; + +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA, uses = ObjectIdMapper.class) +public interface LibraryEntryMapper { + + LibraryEntryDTO toDTO(LibraryEntry domain); + + List toDTO(List domain); + + LibraryEntry toDomain(LibraryEntryDTO dto); + LibraryEntry toDomain(CreateLibraryEntryDTO dto); + + List toDomain(List dto); +} diff --git a/src/main/java/fr/iut/cicd/mapper/LoanMapper.java b/src/main/java/fr/iut/cicd/mapper/LoanMapper.java new file mode 100644 index 0000000..108b787 --- /dev/null +++ b/src/main/java/fr/iut/cicd/mapper/LoanMapper.java @@ -0,0 +1,20 @@ +package fr.iut.cicd.mapper; + +import fr.iut.cicd.domain.Loan; +import fr.iut.cicd.dto.LoanDTO; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +import java.util.List; + +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA, uses = ObjectIdMapper.class) +public interface LoanMapper { + + LoanDTO toDTO(Loan domain); + + List toDTO(List domain); + + Loan toDomain(LoanDTO dto); + + List toDomain(List dto); +} diff --git a/src/main/java/fr/iut/cicd/mapper/ObjectIdMapper.java b/src/main/java/fr/iut/cicd/mapper/ObjectIdMapper.java new file mode 100644 index 0000000..99fa7f6 --- /dev/null +++ b/src/main/java/fr/iut/cicd/mapper/ObjectIdMapper.java @@ -0,0 +1,17 @@ +package fr.iut.cicd.mapper; + +import org.bson.types.ObjectId; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +@Mapper(componentModel = MappingConstants.ComponentModel.JAKARTA) +public interface ObjectIdMapper { + + default String toString(ObjectId oid) { + return oid != null ? oid.toHexString() : null; + } + + default ObjectId toObjectId(String value) { + return (value != null && ObjectId.isValid(value)) ? new ObjectId(value) : null; + } +} diff --git a/src/main/java/fr/iut/cicd/repository/BorrowingRepository.java b/src/main/java/fr/iut/cicd/repository/BorrowingRepository.java new file mode 100644 index 0000000..714d686 --- /dev/null +++ b/src/main/java/fr/iut/cicd/repository/BorrowingRepository.java @@ -0,0 +1,19 @@ +package fr.iut.cicd.repository; + +import fr.iut.cicd.domain.Borrowing; +import io.quarkus.mongodb.panache.PanacheMongoRepository; +import io.quarkus.panache.common.Parameters; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.List; + +@ApplicationScoped +public class BorrowingRepository implements PanacheMongoRepository { + + public List findAllByUsername(String username) { + var params = Parameters.with("username", username); + + return find("userId = :username", params) + .list(); + } +} diff --git a/src/main/java/fr/iut/cicd/repository/LibraryEntryRepository.java b/src/main/java/fr/iut/cicd/repository/LibraryEntryRepository.java new file mode 100644 index 0000000..667c80c --- /dev/null +++ b/src/main/java/fr/iut/cicd/repository/LibraryEntryRepository.java @@ -0,0 +1,19 @@ +package fr.iut.cicd.repository; + +import fr.iut.cicd.domain.LibraryEntry; +import io.quarkus.mongodb.panache.PanacheMongoRepository; +import io.quarkus.panache.common.Parameters; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.List; + +@ApplicationScoped +public class LibraryEntryRepository implements PanacheMongoRepository { + + public List findAllByUsername(String username) { + var params = Parameters.with("username", username); + + return find("userId = :username", params) + .list(); + } +} diff --git a/src/main/java/fr/iut/cicd/repository/LoanRepository.java b/src/main/java/fr/iut/cicd/repository/LoanRepository.java new file mode 100644 index 0000000..a9f2fb5 --- /dev/null +++ b/src/main/java/fr/iut/cicd/repository/LoanRepository.java @@ -0,0 +1,19 @@ +package fr.iut.cicd.repository; + +import fr.iut.cicd.domain.Loan; +import io.quarkus.mongodb.panache.PanacheMongoRepository; +import io.quarkus.panache.common.Parameters; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.List; + +@ApplicationScoped +public class LoanRepository implements PanacheMongoRepository { + + public List findAllByUsername(String username) { + var params = Parameters.with("username", username); + + return find("userId = :username", params) + .list(); + } +} diff --git a/src/main/java/fr/iut/cicd/services/BorrowingService.java b/src/main/java/fr/iut/cicd/services/BorrowingService.java new file mode 100644 index 0000000..eb4c622 --- /dev/null +++ b/src/main/java/fr/iut/cicd/services/BorrowingService.java @@ -0,0 +1,42 @@ +package fr.iut.cicd.services; + +import fr.iut.cicd.domain.Book; +import fr.iut.cicd.domain.Borrowing; +import fr.iut.cicd.repository.BorrowingRepository; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.bson.types.ObjectId; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +@ApplicationScoped +public class BorrowingService { + @Inject + BorrowingRepository repository; + + public Borrowing create(Book book, Date at, String name) { + var borrowing = new Borrowing() + .setId(new ObjectId()) + .setBook(book) + .setUserId(name) + .setAt(at == null ? new Date() : at); + repository.persist(borrowing); + return repository.findById(borrowing.getId()); + } + + public List list(String username) { + return repository.findAllByUsername(username); + } + + public Borrowing returned(ObjectId borrowingId, Date dateToUse, String username) { + var currentBorrowing = repository.findById(borrowingId); + if (currentBorrowing != null && Objects.equals(currentBorrowing.getUserId(), username)) { + currentBorrowing.setReturnedAt(dateToUse); + repository.update(currentBorrowing); + currentBorrowing = repository.findById(borrowingId); + } + return currentBorrowing; + } +} diff --git a/src/main/java/fr/iut/cicd/services/LibraryService.java b/src/main/java/fr/iut/cicd/services/LibraryService.java new file mode 100644 index 0000000..aa73894 --- /dev/null +++ b/src/main/java/fr/iut/cicd/services/LibraryService.java @@ -0,0 +1,45 @@ +package fr.iut.cicd.services; + +import fr.iut.cicd.domain.LibraryEntry; +import fr.iut.cicd.repository.LibraryEntryRepository; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.bson.types.ObjectId; + +import java.util.List; +import java.util.Objects; + +@ApplicationScoped +public class LibraryService { + + @Inject + LibraryEntryRepository repository; + + public LibraryEntry create(LibraryEntry domain, String connectedUsername) { + domain.setId(new ObjectId()) + .setUserId(connectedUsername); + repository.persist(domain); + return domain; + } + + public LibraryEntry update(LibraryEntry domain, String connectedUsername) { + var existing = repository.findById(domain.getId()); + if (existing != null && Objects.equals(existing.getUserId(), connectedUsername)) { + repository.update(domain); + existing = repository.findById(domain.getId()); + } + return existing; + } + + + public List list(String connectedUsername) { + return repository.findAllByUsername(connectedUsername); + } + + public void delete(ObjectId oid, String connectedUsername) { + var existing = repository.findById(oid); + if (existing != null && Objects.equals(existing.getUserId(), connectedUsername)) { + repository.deleteById(oid); + } + } +} diff --git a/src/main/java/fr/iut/cicd/services/LoanService.java b/src/main/java/fr/iut/cicd/services/LoanService.java new file mode 100644 index 0000000..354984d --- /dev/null +++ b/src/main/java/fr/iut/cicd/services/LoanService.java @@ -0,0 +1,43 @@ +package fr.iut.cicd.services; + +import fr.iut.cicd.domain.Book; +import fr.iut.cicd.domain.Loan; +import fr.iut.cicd.repository.LoanRepository; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.bson.types.ObjectId; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +@ApplicationScoped +public class LoanService { + @Inject + LoanRepository repository; + + public Loan create(Book book, Date at, String to, String name) { + var loan = new Loan() + .setId(new ObjectId()) + .setBook(book) + .setTo(to) + .setUserId(name) + .setAt(at == null ? new Date() : at); + repository.persist(loan); + return repository.findById(loan.getId()); + } + + public List list(String username) { + return repository.findAllByUsername(username); + } + + public Loan returned(ObjectId loanId, Date dateToUse, String username) { + var currentLoan = repository.findById(loanId); + if (currentLoan != null && Objects.equals(currentLoan.getUserId(), username)) { + currentLoan.setReturnedAt(dateToUse); + repository.update(currentLoan); + currentLoan = repository.findById(loanId); + } + return currentLoan; + } +} diff --git a/src/main/java/fr/iut/cicd/services/UserService.java b/src/main/java/fr/iut/cicd/services/UserService.java new file mode 100644 index 0000000..3506f20 --- /dev/null +++ b/src/main/java/fr/iut/cicd/services/UserService.java @@ -0,0 +1,18 @@ +package fr.iut.cicd.services; + +import fr.iut.cicd.domain.User; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; + +@ApplicationScoped +public class UserService { + + @Transactional + public void create(String username, String password) { + var user = new User() + .setRole("user") + .setPassword(password) + .setUsername(username); + user.persistAndFlush(); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..c997f00 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,43 @@ +# configure the MongoDB client for a replica set of two nodes +quarkus.mongodb.connection-string=${MONGODB_CONNECTION_STRING} +# mandatory if you don't specify the name of the database using @MongoEntity +quarkus.mongodb.database=${MONGODB_DATABASE_NAME} +# Swagger configuration +quarkus.smallrye-openapi.path=/swagger +quarkus.swagger-ui.path=/swagger-ui +quarkus.swagger-ui.always-include=true + +database-mongodb.migrate-at-start=true + +quarkus.http.auth.basic=true +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.users.bob=bob +quarkus.security.users.embedded.roles.bob=user + +### Pg +quarkus.datasource.jdbc.url=jdbc:postgresql://${PG_HOST}:5432/library +quarkus.datasource.db-kind=postgresql +quarkus.datasource.username=${PG_USERNAME} +quarkus.datasource.password=${PG_PASSWORD} + +# Flyway +quarkus.flyway.migrate-at-start=true +quarkus.flyway.locations=classpath:db/migration +quarkus.flyway.sql-migration-prefix=V +quarkus.flyway.connect-retries=10 +quarkus.flyway.table=flyway_quarkus_history +# run sql: drop table flyway_quarkus_history +# run bash: ./mvnw clean ; ./mvnw quarkus:dev -Dquarkus.flyway.baseline-on-migrate=true -Dquarkus.flyway.baseline-version=20191117222000 +#quarkus.flyway.baseline-on-migrate=true +#quarkus.flyway.baseline-version=20191117222000 + +### security +quarkus.security.jdbc.enabled=true +quarkus.security.jdbc.principal-query.sql=SELECT u.password, u.role FROM users u WHERE u.username=? +quarkus.security.jdbc.principal-query.clear-password-mapper.enabled=true +quarkus.security.jdbc.principal-query.clear-password-mapper.password-index=1 +quarkus.security.jdbc.principal-query.attribute-mappings.0.index=2 +quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups + +### Others +quarkus.http.port=80 \ No newline at end of file diff --git a/src/main/resources/db/migration/V1__init.sql b/src/main/resources/db/migration/V1__init.sql new file mode 100644 index 0000000..834569f --- /dev/null +++ b/src/main/resources/db/migration/V1__init.sql @@ -0,0 +1,17 @@ +DROP TABLE IF EXISTS users; + +CREATE TABLE IF NOT EXISTS users ( + id SERIAL NOT NULL PRIMARY KEY, + username VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL, + role VARCHAR(255) NOT NULL +); + +INSERT INTO users (id, username, password, role) VALUES +(1, 'bob', 'bob', 'user'); + +alter table if exists users alter column id set data type bigint; + +DROP SEQUENCE IF EXISTS users_SEQ; + +create sequence users_SEQ start with 2 increment by 50; diff --git a/src/test/java/fr/iut/cicd/services/LibraryServiceTest.java b/src/test/java/fr/iut/cicd/services/LibraryServiceTest.java new file mode 100644 index 0000000..5720ed4 --- /dev/null +++ b/src/test/java/fr/iut/cicd/services/LibraryServiceTest.java @@ -0,0 +1,59 @@ +package fr.iut.cicd.services; + +import fr.iut.cicd.domain.LibraryEntry; +import fr.iut.cicd.repository.LibraryEntryRepository; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.h2.H2DatabaseTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.mockito.InjectSpy; +import jakarta.inject.Inject; +import org.bson.types.ObjectId; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.Arrays; + +import static fr.iut.cicd.services.TestData.BOOK_1; +import static fr.iut.cicd.services.TestData.BOOK_2; +import static io.smallrye.common.constraint.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@QuarkusTest +@QuarkusTestResource(H2DatabaseTestResource.class) +public class LibraryServiceTest { + + @Inject + LibraryService libraryService; + + @InjectSpy + LibraryEntryRepository repository; + + @BeforeEach + void setUp() { + Mockito.doReturn( + Arrays.asList( + new LibraryEntry().setId(new ObjectId()).setUserId("Toto").setBook(BOOK_1), + new LibraryEntry().setId(new ObjectId()).setUserId("Toto").setBook(BOOK_2) + )) + .when(repository).findAllByUsername("Toto"); + } + + @Test + void whenFindForUser_thenBooksShouldBeFound() { + assertEquals(2, libraryService.list("Toto").size()); + } + + @Test + void addABookToAnUserLibrary_thenShouldBeOk() { + var libEntry = new LibraryEntry() + .setBook(BOOK_1); + Mockito.doAnswer(invocationOnMock -> invocationOnMock.getArgument(0)) + .when(repository) + .persist(libEntry); + + var result = libraryService.create(libEntry, "Toto"); + assertEquals("Toto", result.getUserId()); + assertNotNull(result.getId()); + } +} diff --git a/src/test/java/fr/iut/cicd/services/TestData.java b/src/test/java/fr/iut/cicd/services/TestData.java new file mode 100644 index 0000000..46331b0 --- /dev/null +++ b/src/test/java/fr/iut/cicd/services/TestData.java @@ -0,0 +1,25 @@ +package fr.iut.cicd.services; + + +import fr.iut.cicd.domain.Book; + +import java.util.Arrays; +import java.util.List; + +public class TestData { + + public static Book BOOK_1 = new Book() + .setId("drklmjgkznbl") + .setNbPages(210) + .setPublishers(Arrays.asList("Jean", "Jacques")) + .setTitle("The book") + .setIsbn13("e45frze045e22"); + + + public static Book BOOK_2 = new Book() + .setId("gkenrlkaùbn") + .setNbPages(42) + .setPublishers(List.of("Michel")) + .setTitle("The book, the return") + .setIsbn13("45e04er5gh014e"); +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..a9bb5b8 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,43 @@ +# configure the MongoDB client for a replica set of two nodes +#quarkus.mongodb.connection-string=${MONGODB_CONNECTION_STRING} +# mandatory if you don't specify the name of the database using @MongoEntity +quarkus.mongodb.database=test +# Swagger configuration +quarkus.smallrye-openapi.path=/swagger +quarkus.swagger-ui.path=/swagger-ui +quarkus.swagger-ui.always-include=true + +database-mongodb.migrate-at-start=true + +quarkus.http.auth.basic=true +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.users.bob=bob +quarkus.security.users.embedded.roles.bob=user + +### Pg +%test.quarkus.datasource.username=username +%test.quarkus.datasource.password=password +%test.quarkus.datasource.db-kind=h2 +%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test + +# Flyway +quarkus.flyway.migrate-at-start=false +quarkus.flyway.locations=classpath:db/migration +quarkus.flyway.sql-migration-prefix=V +quarkus.flyway.connect-retries=10 +quarkus.flyway.table=flyway_quarkus_history +# run sql: drop table flyway_quarkus_history +# run bash: ./mvnw clean ; ./mvnw quarkus:dev -Dquarkus.flyway.baseline-on-migrate=true -Dquarkus.flyway.baseline-version=20191117222000 +#quarkus.flyway.baseline-on-migrate=true +#quarkus.flyway.baseline-version=20191117222000 + +### security +quarkus.security.jdbc.enabled=true +quarkus.security.jdbc.principal-query.sql=SELECT u.password, u.role FROM users u WHERE u.username=? +quarkus.security.jdbc.principal-query.clear-password-mapper.enabled=true +quarkus.security.jdbc.principal-query.clear-password-mapper.password-index=1 +quarkus.security.jdbc.principal-query.attribute-mappings.0.index=2 +quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups + +### Others +quarkus.http.port=80 \ No newline at end of file diff --git a/support/tests/requests.http b/support/tests/requests.http new file mode 100644 index 0000000..d79f182 --- /dev/null +++ b/support/tests/requests.http @@ -0,0 +1,73 @@ +POST http://localhost:8080/users/register +Content-Type: application/json + +{ + "username": "toto", + "password": "Leagumus" +} + +####### +POST https://codefirst.iut.uca.fr/containers/camillepetitalot-cicd-java-backend/users/register +Content-Type: application/json + +{ + "username": "toto", + "password": "Leagumus" +} + +####### + +GET https://codefirst.iut.uca.fr/containers/camillepetitalot-cicd-java-backend/borrowings +Authorization: Basic toto Leagumus + + +####### + +GET http://localhost:8080/borrowings +Authorization: Basic toto Leagumus + + +####### + +POST http://localhost:8080/borrowings +Authorization: Basic toto Leagumus +Content-Type: application/json + +{ + "book": { + "id": "voezbvjkezb" + } +} + +###### + +POST http://localhost:8080/borrowings/64f1dbab48ddf006eafdcc0b/return +Authorization: Basic toto Leagumus +Content-Type: application/json + + + +####### + +GET http://localhost:8080/loans +Authorization: Basic toto Leagumus + +####### + +POST http://localhost:8080/loans +Authorization: Basic toto Leagumus +Content-Type: application/json + +{ + "book": { + "id": "voezbvjkezb" + }, + "to": "Michel", + "at": "2023-10-01" +} + +###### + +POST http://localhost:8080/loans/64f1e6a0af0aaf0594d30c2f/return +Authorization: Basic toto Leagumus +Content-Type: application/json