├── Chapter01 ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── packt │ │ │ └── cardatabase │ │ │ └── CardatabaseApplication.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── packt │ └── cardatabase │ └── CardatabaseApplicationTests.java ├── Chapter02 ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── packt │ │ │ └── cardatabase │ │ │ ├── CardatabaseApplication.java │ │ │ └── domain │ │ │ ├── Car.java │ │ │ ├── CarRepository.java │ │ │ ├── Owner.java │ │ │ └── OwnerRepository.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── packt │ └── cardatabase │ └── CardatabaseApplicationTests.java ├── Chapter03 ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── packt │ │ │ └── cardatabase │ │ │ ├── CardatabaseApplication.java │ │ │ ├── domain │ │ │ ├── Car.java │ │ │ ├── CarRepository.java │ │ │ ├── Owner.java │ │ │ └── OwnerRepository.java │ │ │ └── web │ │ │ └── CarController.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── packt │ └── cardatabase │ └── CardatabaseApplicationTests.java ├── Chapter04 ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── packt │ │ │ └── cardatabase │ │ │ ├── AuthenticationFilter.java │ │ │ ├── CardatabaseApplication.java │ │ │ ├── LoginFilter.java │ │ │ ├── SecurityConfig.java │ │ │ ├── domain │ │ │ ├── AccountCredentials.java │ │ │ ├── Car.java │ │ │ ├── CarRepository.java │ │ │ ├── Owner.java │ │ │ ├── OwnerRepository.java │ │ │ ├── User.java │ │ │ └── UserRepository.java │ │ │ ├── service │ │ │ ├── AuthenticationService.java │ │ │ └── UserDetailServiceImpl.java │ │ │ └── web │ │ │ └── CarController.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── packt │ └── cardatabase │ ├── CarRepositoryTest.java │ ├── CarRestTest.java │ └── CardatabaseApplicationTests.java ├── Chapter07 ├── Readme.md ├── restgithub │ └── App.js └── weatherapp │ └── App.js ├── Chapter08 ├── ReactRouter │ ├── App.js │ ├── Contact.js │ └── Home.js ├── ReactTable │ └── App.js └── ShoppingList │ ├── AddItem.js │ └── App.js ├── Chapter10 ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── components │ │ ├── AddCar.js │ │ └── Carlist.js │ ├── constants.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js └── yarn.lock ├── Chapter11 ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── components │ ├── AddCar.js │ ├── Carlist.js │ └── Carlist.test.js │ ├── constants.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js ├── Chapter12 ├── .gitignore ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json └── src │ ├── AddCar.test.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── __snapshots__ │ └── App.test.js.snap │ ├── components │ ├── AddCar.js │ ├── Carlist.js │ └── getCars.js │ ├── constants.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js ├── Chapter13 ├── back end │ ├── .gitignore │ ├── README.MD │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── packt │ │ │ │ └── cardatabase │ │ │ │ ├── AuthenticationFilter.java │ │ │ │ ├── CardatabaseApplication.java │ │ │ │ ├── LoginFilter.java │ │ │ │ ├── SecurityConfig.java │ │ │ │ ├── domain │ │ │ │ ├── AccountCredentials.java │ │ │ │ ├── Car.java │ │ │ │ ├── CarRepository.java │ │ │ │ ├── Owner.java │ │ │ │ ├── OwnerRepository.java │ │ │ │ ├── User.java │ │ │ │ └── UserRepository.java │ │ │ │ ├── service │ │ │ │ ├── AuthenticationService.java │ │ │ │ └── UserDetailServiceImpl.java │ │ │ │ └── web │ │ │ │ └── CarController.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── packt │ │ └── cardatabase │ │ ├── CarRepositoryTest.java │ │ ├── CarRestTest.java │ │ └── CardatabaseApplicationTests.java └── front end │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── AddCar.test.js │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── __snapshots__ │ └── App.test.js.snap │ ├── components │ ├── AddCar.js │ ├── Carlist.js │ └── Login.js │ ├── constants.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── registerServiceWorker.js ├── LICENSE └── README.md /Chapter01/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /Chapter01/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter01/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /Chapter01/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /Chapter01/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /Chapter01/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /Chapter01/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.packt 7 | cardatabase 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | cardatabase 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-devtools 36 | runtime 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Chapter01/src/main/java/com/packt/cardatabase/CardatabaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CardatabaseApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CardatabaseApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter01/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter01/src/main/resources/application.properties -------------------------------------------------------------------------------- /Chapter01/src/test/java/com/packt/cardatabase/CardatabaseApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class CardatabaseApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter02/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter02/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /Chapter02/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /Chapter02/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /Chapter02/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.packt 7 | cardatabase 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | cardatabase 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jpa 35 | 36 | 37 | org.mariadb.jdbc 38 | mariadb-java-client 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-devtools 43 | runtime 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Chapter02/src/main/java/com/packt/cardatabase/CardatabaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | import com.packt.cardatabase.domain.Car; 10 | import com.packt.cardatabase.domain.CarRepository; 11 | import com.packt.cardatabase.domain.Owner; 12 | import com.packt.cardatabase.domain.OwnerRepository; 13 | 14 | @SpringBootApplication 15 | public class CardatabaseApplication { 16 | @Autowired 17 | private CarRepository repository; 18 | 19 | @Autowired 20 | private OwnerRepository orepository; 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(CardatabaseApplication.class, args); 24 | } 25 | 26 | @Bean 27 | CommandLineRunner runner() { 28 | return args -> { 29 | Owner owner1 = new Owner("John" , "Johnson"); 30 | Owner owner2 = new Owner("Mary" , "Robinson"); 31 | orepository.save(owner1); 32 | orepository.save(owner2); 33 | 34 | repository.save(new Car("Ford", "Mustang", "Red", "ADF-1121", 2017, 59000, owner1)); 35 | repository.save(new Car("Nissan", "Leaf", "White", "SSJ-3002", 2014, 29000, owner2)); 36 | repository.save(new Car("Toyota", "Prius", "Silver", "KKO-0212", 2018, 39000, owner2)); 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter02/src/main/java/com/packt/cardatabase/domain/Car.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | import javax.persistence.JoinColumn; 8 | import javax.persistence.ManyToOne; 9 | import javax.persistence.FetchType; 10 | 11 | @Entity 12 | public class Car { 13 | @Id 14 | @GeneratedValue(strategy=GenerationType.AUTO) 15 | private long id; 16 | private String brand, model, color, registerNumber; 17 | private int year, price; 18 | 19 | @ManyToOne(fetch=FetchType.LAZY) 20 | @JoinColumn(name = "owner") 21 | private Owner owner; 22 | 23 | public Car() {} 24 | 25 | public Car(String brand, String model, String color, String registerNumber, int year, int price, Owner owner) { 26 | super(); 27 | this.brand = brand; 28 | this.model = model; 29 | this.color = color; 30 | this.registerNumber = registerNumber; 31 | this.year = year; 32 | this.price = price; 33 | this.owner = owner; 34 | } 35 | 36 | public Owner getOwner() { 37 | return owner; 38 | } 39 | 40 | public void setOwner(Owner owner) { 41 | this.owner = owner; 42 | } 43 | 44 | public String getBrand() { 45 | return brand; 46 | } 47 | public void setBrand(String brand) { 48 | this.brand = brand; 49 | } 50 | public String getModel() { 51 | return model; 52 | } 53 | public void setModel(String model) { 54 | this.model = model; 55 | } 56 | public String getColor() { 57 | return color; 58 | } 59 | public void setColor(String color) { 60 | this.color = color; 61 | } 62 | public String getRegisterNumber() { 63 | return registerNumber; 64 | } 65 | public void setRegisterNumber(String registerNumber) { 66 | this.registerNumber = registerNumber; 67 | } 68 | public int getYear() { 69 | return year; 70 | } 71 | public void setYear(int year) { 72 | this.year = year; 73 | } 74 | public int getPrice() { 75 | return price; 76 | } 77 | public void setPrice(int price) { 78 | this.price = price; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Chapter02/src/main/java/com/packt/cardatabase/domain/CarRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | 7 | public interface CarRepository extends CrudRepository { 8 | // Fetch cars by brand and sort by year 9 | List findByBrandOrderByYearAsc(String brand); 10 | } 11 | -------------------------------------------------------------------------------- /Chapter02/src/main/java/com/packt/cardatabase/domain/Owner.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.OneToMany; 11 | 12 | @Entity 13 | public class Owner { 14 | @Id 15 | @GeneratedValue(strategy=GenerationType.AUTO) 16 | private long ownerid; 17 | private String firstname, lastname; 18 | 19 | @OneToMany(cascade = CascadeType.ALL, mappedBy="owner") 20 | private List cars; 21 | 22 | public Owner() {} 23 | 24 | public Owner(String firstname, String lastname) { 25 | super(); 26 | this.firstname = firstname; 27 | this.lastname = lastname; 28 | } 29 | 30 | public List getCars() { 31 | return cars; 32 | } 33 | 34 | public void setCars(List cars) { 35 | this.cars = cars; 36 | } 37 | 38 | public long getOwnerid() { 39 | return ownerid; 40 | } 41 | 42 | public void setOwnerid(long ownerid) { 43 | this.ownerid = ownerid; 44 | } 45 | 46 | public String getFirstname() { 47 | return firstname; 48 | } 49 | 50 | public void setFirstname(String firstname) { 51 | this.firstname = firstname; 52 | } 53 | 54 | public String getLastname() { 55 | return lastname; 56 | } 57 | 58 | public void setLastname(String lastname) { 59 | this.lastname = lastname; 60 | } 61 | 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Chapter02/src/main/java/com/packt/cardatabase/domain/OwnerRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface OwnerRepository extends CrudRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Chapter02/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mariadb://localhost:3306/cardb 2 | spring.datasource.username=root 3 | spring.datasource.password=sako09 4 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 5 | 6 | spring.jpa.generate-ddl=true 7 | spring.jpa.hibernate.ddl-auto=create-drop 8 | 9 | spring.jpa.show-sql=true -------------------------------------------------------------------------------- /Chapter02/src/test/java/com/packt/cardatabase/CardatabaseApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class CardatabaseApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter03/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /Chapter03/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter03/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /Chapter03/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /Chapter03/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /Chapter03/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.packt 7 | cardatabase 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | cardatabase 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jpa 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-data-rest 39 | 40 | 41 | org.mariadb.jdbc 42 | mariadb-java-client 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-devtools 47 | runtime 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Chapter03/src/main/java/com/packt/cardatabase/CardatabaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | import com.packt.cardatabase.domain.Car; 10 | import com.packt.cardatabase.domain.CarRepository; 11 | import com.packt.cardatabase.domain.Owner; 12 | import com.packt.cardatabase.domain.OwnerRepository; 13 | 14 | @SpringBootApplication 15 | public class CardatabaseApplication { 16 | @Autowired 17 | private CarRepository repository; 18 | 19 | @Autowired 20 | private OwnerRepository orepository; 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(CardatabaseApplication.class, args); 24 | } 25 | 26 | @Bean 27 | CommandLineRunner runner() { 28 | return args -> { 29 | Owner owner1 = new Owner("John" , "Johnson"); 30 | Owner owner2 = new Owner("Mary" , "Robinson"); 31 | orepository.save(owner1); 32 | orepository.save(owner2); 33 | 34 | repository.save(new Car("Ford", "Mustang", "Red", "ADF-1121", 2017, 59000, owner1)); 35 | repository.save(new Car("Nissan", "Leaf", "White", "SSJ-3002", 2014, 29000, owner2)); 36 | repository.save(new Car("Toyota", "Prius", "Silver", "KKO-0212", 2018, 39000, owner2)); 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Chapter03/src/main/java/com/packt/cardatabase/domain/Car.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.FetchType; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.JoinColumn; 9 | import javax.persistence.ManyToOne; 10 | 11 | @Entity 12 | public class Car { 13 | @Id 14 | @GeneratedValue(strategy=GenerationType.AUTO) 15 | private long id; 16 | private String brand, model, color, registerNumber; 17 | private int year, price; 18 | 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "owner") 21 | private Owner owner; 22 | 23 | public Car() {} 24 | 25 | public Car(String brand, String model, String color, String registerNumber, int year, int price, Owner owner) { 26 | super(); 27 | this.brand = brand; 28 | this.model = model; 29 | this.color = color; 30 | this.registerNumber = registerNumber; 31 | this.year = year; 32 | this.price = price; 33 | this.owner = owner; 34 | } 35 | 36 | public Owner getOwner() { 37 | return owner; 38 | } 39 | 40 | public void setOwner(Owner owner) { 41 | this.owner = owner; 42 | } 43 | 44 | public String getBrand() { 45 | return brand; 46 | } 47 | public void setBrand(String brand) { 48 | this.brand = brand; 49 | } 50 | public String getModel() { 51 | return model; 52 | } 53 | public void setModel(String model) { 54 | this.model = model; 55 | } 56 | public String getColor() { 57 | return color; 58 | } 59 | public void setColor(String color) { 60 | this.color = color; 61 | } 62 | public String getRegisterNumber() { 63 | return registerNumber; 64 | } 65 | public void setRegisterNumber(String registerNumber) { 66 | this.registerNumber = registerNumber; 67 | } 68 | public int getYear() { 69 | return year; 70 | } 71 | public void setYear(int year) { 72 | this.year = year; 73 | } 74 | public int getPrice() { 75 | return price; 76 | } 77 | public void setPrice(int price) { 78 | this.price = price; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Chapter03/src/main/java/com/packt/cardatabase/domain/CarRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.query.Param; 7 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 8 | 9 | @RepositoryRestResource 10 | public interface CarRepository extends CrudRepository { 11 | // Fetch cars by brand 12 | List findByBrand(@Param("brand") String brand); 13 | 14 | // Fetch cars by color 15 | List findByColor(@Param("color") String color); 16 | } 17 | -------------------------------------------------------------------------------- /Chapter03/src/main/java/com/packt/cardatabase/domain/Owner.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.OneToMany; 11 | 12 | import com.fasterxml.jackson.annotation.JsonIgnore; 13 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 14 | 15 | @Entity 16 | @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 17 | public class Owner { 18 | @Id 19 | @GeneratedValue(strategy=GenerationType.AUTO) 20 | private long ownerid; 21 | private String firstname, lastname; 22 | 23 | @OneToMany(cascade = CascadeType.ALL, mappedBy="owner") 24 | @JsonIgnore 25 | private List cars; 26 | 27 | public Owner() {} 28 | 29 | public Owner(String firstname, String lastname) { 30 | super(); 31 | this.firstname = firstname; 32 | this.lastname = lastname; 33 | } 34 | 35 | public List getCars() { 36 | return cars; 37 | } 38 | 39 | public void setCars(List cars) { 40 | this.cars = cars; 41 | } 42 | 43 | public long getOwnerid() { 44 | return ownerid; 45 | } 46 | 47 | public void setOwnerid(long ownerid) { 48 | this.ownerid = ownerid; 49 | } 50 | 51 | public String getFirstname() { 52 | return firstname; 53 | } 54 | 55 | public void setFirstname(String firstname) { 56 | this.firstname = firstname; 57 | } 58 | 59 | public String getLastname() { 60 | return lastname; 61 | } 62 | 63 | public void setLastname(String lastname) { 64 | this.lastname = lastname; 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Chapter03/src/main/java/com/packt/cardatabase/domain/OwnerRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface OwnerRepository extends CrudRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Chapter03/src/main/java/com/packt/cardatabase/web/CarController.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import com.packt.cardatabase.domain.Car; 8 | import com.packt.cardatabase.domain.CarRepository; 9 | 10 | @RestController 11 | public class CarController { 12 | @Autowired 13 | private CarRepository repository; 14 | 15 | @RequestMapping("/cars") 16 | public Iterable getCars() { 17 | return repository.findAll(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter03/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mariadb://localhost:3306/cardb 2 | spring.datasource.username=root 3 | spring.datasource.password=YOUR_MARIADB_ROOT_PASSWORD 4 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 5 | 6 | spring.jpa.generate-ddl=true 7 | spring.jpa.hibernate.ddl-auto=create-drop 8 | 9 | spring.jpa.show-sql=true 10 | 11 | spring.data.rest.basePath=/api 12 | -------------------------------------------------------------------------------- /Chapter03/src/test/java/com/packt/cardatabase/CardatabaseApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class CardatabaseApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /Chapter04/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter04/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /Chapter04/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /Chapter04/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /Chapter04/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.packt 7 | cardatabase 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | cardatabase 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.1.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jpa 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-data-rest 43 | 44 | 45 | org.mariadb.jdbc 46 | mariadb-java-client 47 | 48 | 49 | com.h2database 50 | h2 51 | test 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-devtools 56 | runtime 57 | 58 | 59 | io.jsonwebtoken 60 | jjwt 61 | 0.9.1 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/AuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.ServletRequest; 8 | import javax.servlet.ServletResponse; 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.core.context.SecurityContextHolder; 13 | import org.springframework.web.filter.GenericFilterBean; 14 | 15 | import com.packt.cardatabase.service.AuthenticationService; 16 | 17 | public class AuthenticationFilter extends GenericFilterBean { 18 | 19 | @Override 20 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 21 | throws IOException, ServletException { 22 | Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest)request); 23 | 24 | SecurityContextHolder.getContext().setAuthentication(authentication); 25 | filterChain.doFilter(request, response); 26 | } 27 | } -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/CardatabaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | import com.packt.cardatabase.domain.Car; 10 | import com.packt.cardatabase.domain.CarRepository; 11 | import com.packt.cardatabase.domain.Owner; 12 | import com.packt.cardatabase.domain.OwnerRepository; 13 | import com.packt.cardatabase.domain.User; 14 | import com.packt.cardatabase.domain.UserRepository; 15 | 16 | @SpringBootApplication 17 | public class CardatabaseApplication { 18 | @Autowired 19 | private CarRepository repository; 20 | 21 | @Autowired 22 | private OwnerRepository orepository; 23 | 24 | @Autowired 25 | private UserRepository urepository; 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(CardatabaseApplication.class, args); 29 | } 30 | 31 | @Bean 32 | CommandLineRunner runner() { 33 | return args -> { 34 | Owner owner1 = new Owner("John" , "Johnson"); 35 | Owner owner2 = new Owner("Mary" , "Robinson"); 36 | orepository.save(owner1); 37 | orepository.save(owner2); 38 | 39 | repository.save(new Car("Ford", "Mustang", "Red", "ADF-1121", 2017, 59000, owner1)); 40 | repository.save(new Car("Nissan", "Leaf", "White", "SSJ-3002", 2014, 29000, owner2)); 41 | repository.save(new Car("Toyota", "Prius", "Silver", "KKO-0212", 2018, 39000, owner2)); 42 | 43 | urepository.save(new User("user", "$2a$04$1.YhMIgNX/8TkCKGFUONWO1waedKhQ5KrnB30fl0Q01QKqmzLf.Zi", "USER")); 44 | urepository.save(new User("admin", "$2a$04$KNLUwOWHVQZVpXyMBNc7JOzbLiBjb9Tk9bP7KNcPI12ICuvzXQQKG", "ADMIN")); 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/LoginFilter.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; 16 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 17 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | import com.packt.cardatabase.domain.AccountCredentials; 20 | import com.packt.cardatabase.service.AuthenticationService; 21 | 22 | public class LoginFilter extends AbstractAuthenticationProcessingFilter { 23 | 24 | public LoginFilter(String url, AuthenticationManager authManager) { 25 | super(new AntPathRequestMatcher(url)); 26 | setAuthenticationManager(authManager); 27 | } 28 | 29 | @Override 30 | public Authentication attemptAuthentication( 31 | HttpServletRequest req, HttpServletResponse res) 32 | throws AuthenticationException, IOException, ServletException { 33 | AccountCredentials creds = new ObjectMapper() 34 | .readValue(req.getInputStream(), AccountCredentials.class); 35 | return getAuthenticationManager().authenticate( 36 | new UsernamePasswordAuthenticationToken( 37 | creds.getUsername(), 38 | creds.getPassword(), 39 | Collections.emptyList() 40 | ) 41 | ); 42 | } 43 | 44 | @Override 45 | protected void successfulAuthentication( 46 | HttpServletRequest req, 47 | HttpServletResponse res, FilterChain chain, 48 | Authentication auth) throws IOException, ServletException { 49 | AuthenticationService.addToken(res, auth.getName()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 15 | import org.springframework.web.cors.CorsConfiguration; 16 | import org.springframework.web.cors.CorsConfigurationSource; 17 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 18 | 19 | import com.packt.cardatabase.service.UserDetailServiceImpl; 20 | 21 | @Configuration 22 | @EnableWebSecurity 23 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 24 | @Autowired 25 | private UserDetailServiceImpl userDetailsService; 26 | 27 | @Override 28 | protected void configure(HttpSecurity http) throws Exception { 29 | http.csrf().disable().cors().and().authorizeRequests() 30 | .antMatchers(HttpMethod.POST, "/login").permitAll() 31 | .anyRequest().authenticated() 32 | .and() 33 | // Filter for the api/login requests 34 | .addFilterBefore(new LoginFilter("/login", authenticationManager()), 35 | UsernamePasswordAuthenticationFilter.class) 36 | // Filter for other requests to check JWT in header 37 | .addFilterBefore(new AuthenticationFilter(), 38 | UsernamePasswordAuthenticationFilter.class); 39 | } 40 | 41 | @Bean 42 | CorsConfigurationSource corsConfigurationSource() { 43 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 44 | CorsConfiguration config = new CorsConfiguration(); 45 | config.setAllowedOrigins(Arrays.asList("*")); 46 | config.setAllowedMethods(Arrays.asList("*")); 47 | config.setAllowedHeaders(Arrays.asList("*")); 48 | config.setAllowCredentials(true); 49 | config.applyPermitDefaultValues(); 50 | 51 | source.registerCorsConfiguration("/**", config); 52 | return source; 53 | } 54 | 55 | @Autowired 56 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 57 | auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/AccountCredentials.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | public class AccountCredentials { 4 | private String username; 5 | private String password; 6 | 7 | public String getUsername() { 8 | return username; 9 | } 10 | public void setUsername(String username) { 11 | this.username = username; 12 | } 13 | public String getPassword() { 14 | return password; 15 | } 16 | public void setPassword(String password) { 17 | this.password = password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/Car.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.FetchType; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.JoinColumn; 9 | import javax.persistence.ManyToOne; 10 | 11 | @Entity 12 | public class Car { 13 | @Id 14 | @GeneratedValue(strategy=GenerationType.AUTO) 15 | private long id; 16 | private String brand, model, color, registerNumber; 17 | private int year, price; 18 | 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "owner") 21 | private Owner owner; 22 | 23 | public Car() {} 24 | 25 | public Car(String brand, String model, String color, String registerNumber, int year, int price) { 26 | super(); 27 | this.brand = brand; 28 | this.model = model; 29 | this.color = color; 30 | this.registerNumber = registerNumber; 31 | this.year = year; 32 | this.price = price; 33 | } 34 | 35 | public Car(String brand, String model, String color, String registerNumber, int year, int price, Owner owner) { 36 | super(); 37 | this.brand = brand; 38 | this.model = model; 39 | this.color = color; 40 | this.registerNumber = registerNumber; 41 | this.year = year; 42 | this.price = price; 43 | this.owner = owner; 44 | } 45 | 46 | 47 | public long getId() { 48 | return id; 49 | } 50 | 51 | public void setId(long id) { 52 | this.id = id; 53 | } 54 | 55 | public Owner getOwner() { 56 | return owner; 57 | } 58 | 59 | public void setOwner(Owner owner) { 60 | this.owner = owner; 61 | } 62 | 63 | public String getBrand() { 64 | return brand; 65 | } 66 | public void setBrand(String brand) { 67 | this.brand = brand; 68 | } 69 | public String getModel() { 70 | return model; 71 | } 72 | public void setModel(String model) { 73 | this.model = model; 74 | } 75 | public String getColor() { 76 | return color; 77 | } 78 | public void setColor(String color) { 79 | this.color = color; 80 | } 81 | public String getRegisterNumber() { 82 | return registerNumber; 83 | } 84 | public void setRegisterNumber(String registerNumber) { 85 | this.registerNumber = registerNumber; 86 | } 87 | public int getYear() { 88 | return year; 89 | } 90 | public void setYear(int year) { 91 | this.year = year; 92 | } 93 | public int getPrice() { 94 | return price; 95 | } 96 | public void setPrice(int price) { 97 | this.price = price; 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/CarRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.query.Param; 7 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 8 | 9 | @RepositoryRestResource 10 | public interface CarRepository extends CrudRepository { 11 | // Fetch cars by brand 12 | List findByBrand(@Param("brand") String brand); 13 | 14 | // Fetch cars by color 15 | List findByColor(@Param("color") String color); 16 | } 17 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/Owner.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.OneToMany; 11 | 12 | import com.fasterxml.jackson.annotation.JsonIgnore; 13 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 14 | 15 | @Entity 16 | @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 17 | public class Owner { 18 | @Id 19 | @GeneratedValue(strategy=GenerationType.AUTO) 20 | private long ownerid; 21 | private String firstname, lastname; 22 | 23 | @OneToMany(cascade = CascadeType.ALL, mappedBy="owner") 24 | @JsonIgnore 25 | private List cars; 26 | 27 | public Owner() {} 28 | 29 | public Owner(String firstname, String lastname) { 30 | super(); 31 | this.firstname = firstname; 32 | this.lastname = lastname; 33 | } 34 | 35 | public List getCars() { 36 | return cars; 37 | } 38 | 39 | public void setCars(List cars) { 40 | this.cars = cars; 41 | } 42 | 43 | public long getOwnerid() { 44 | return ownerid; 45 | } 46 | 47 | public void setOwnerid(long ownerid) { 48 | this.ownerid = ownerid; 49 | } 50 | 51 | public String getFirstname() { 52 | return firstname; 53 | } 54 | 55 | public void setFirstname(String firstname) { 56 | this.firstname = firstname; 57 | } 58 | 59 | public String getLastname() { 60 | return lastname; 61 | } 62 | 63 | public void setLastname(String lastname) { 64 | this.lastname = lastname; 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/OwnerRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface OwnerRepository extends CrudRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | 9 | @Entity 10 | public class User { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | @Column(nullable = false, updatable = false) 14 | private Long id; 15 | 16 | @Column(nullable = false, unique = true) 17 | private String username; 18 | 19 | @Column(nullable = false) 20 | private String password; 21 | 22 | @Column(nullable = false) 23 | private String role; 24 | 25 | public User() { 26 | } 27 | 28 | public User(String username, String password, String role) { 29 | super(); 30 | this.username = username; 31 | this.password = password; 32 | this.role = role; 33 | } 34 | 35 | public Long getId() { 36 | return id; 37 | } 38 | 39 | public void setId(Long id) { 40 | this.id = id; 41 | } 42 | 43 | public String getUsername() { 44 | return username; 45 | } 46 | 47 | public void setUsername(String username) { 48 | this.username = username; 49 | } 50 | 51 | public String getPassword() { 52 | return password; 53 | } 54 | 55 | public void setPassword(String password) { 56 | this.password = password; 57 | } 58 | 59 | public String getRole() { 60 | return role; 61 | } 62 | 63 | public void setRole(String role) { 64 | this.role = role; 65 | } 66 | } -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/domain/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface UserRepository extends CrudRepository { 8 | User findByUsername(String username); 9 | } -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.service; 2 | 3 | import io.jsonwebtoken.Jwts; 4 | import io.jsonwebtoken.SignatureAlgorithm; 5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 | import org.springframework.security.core.Authentication; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.util.Date; 11 | 12 | import static java.util.Collections.emptyList; 13 | 14 | public class AuthenticationService { 15 | static final long EXPIRATIONTIME = 864_000_00; // 1 day in milliseconds 16 | static final String SIGNINGKEY = "SecretKey"; 17 | static final String PREFIX = "Bearer"; 18 | 19 | static public void addToken(HttpServletResponse res, String username) { 20 | String JwtToken = Jwts.builder().setSubject(username) 21 | .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME)) 22 | .signWith(SignatureAlgorithm.HS512, SIGNINGKEY) 23 | .compact(); 24 | res.addHeader("Authorization", PREFIX + " " + JwtToken); 25 | res.addHeader("Access-Control-Expose-Headers", "Authorization"); 26 | } 27 | 28 | static public Authentication getAuthentication(HttpServletRequest request) { 29 | String token = request.getHeader("Authorization"); 30 | if (token != null) { 31 | String user = Jwts.parser() 32 | .setSigningKey(SIGNINGKEY) 33 | .parseClaimsJws(token.replace(PREFIX, "")) 34 | .getBody() 35 | .getSubject(); 36 | 37 | if (user != null) 38 | return new UsernamePasswordAuthenticationToken(user, null, emptyList()); 39 | } 40 | return null; 41 | } 42 | } -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/service/UserDetailServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.service; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.core.authority.AuthorityUtils; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 8 | import org.springframework.stereotype.Service; 9 | 10 | import com.packt.cardatabase.domain.User; 11 | import com.packt.cardatabase.domain.UserRepository; 12 | 13 | @Service 14 | public class UserDetailServiceImpl implements UserDetailsService { 15 | @Autowired 16 | private UserRepository repository; 17 | 18 | 19 | @Override 20 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 21 | { 22 | User currentUser = repository.findByUsername(username); 23 | UserDetails user = new org.springframework.security.core.userdetails.User(username, currentUser.getPassword() 24 | , true, true, true, true, AuthorityUtils.createAuthorityList(currentUser.getRole())); 25 | return user; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /Chapter04/src/main/java/com/packt/cardatabase/web/CarController.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import com.packt.cardatabase.domain.Car; 8 | import com.packt.cardatabase.domain.CarRepository; 9 | 10 | @RestController 11 | public class CarController { 12 | @Autowired 13 | private CarRepository repository; 14 | 15 | @RequestMapping("/cars") 16 | public Iterable getCars() { 17 | return repository.findAll(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter04/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mariadb://localhost:3306/cardb 2 | spring.datasource.username=root 3 | spring.datasource.password=your_password 4 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 5 | 6 | spring.jpa.generate-ddl=true 7 | spring.jpa.hibernate.ddl-auto=create-drop 8 | 9 | spring.jpa.show-sql=true 10 | 11 | spring.data.rest.basePath=/api 12 | -------------------------------------------------------------------------------- /Chapter04/src/test/java/com/packt/cardatabase/CarRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 9 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import com.packt.cardatabase.domain.Car; 13 | import com.packt.cardatabase.domain.CarRepository; 14 | 15 | @RunWith(SpringRunner.class) 16 | @DataJpaTest 17 | public class CarRepositoryTest { 18 | @Autowired 19 | private TestEntityManager entityManager; 20 | 21 | @Autowired 22 | private CarRepository repository; 23 | 24 | @Test 25 | public void saveCar() { 26 | Car car = new Car("Tesla", "Model X", "White", "ABC-1234", 2017, 86000); 27 | entityManager.persistAndFlush(car); 28 | 29 | assertThat(car.getId()).isNotNull(); 30 | } 31 | 32 | @Test 33 | public void deleteCars() { 34 | entityManager.persistAndFlush(new Car("Tesla", "Model X", "White", "ABC-1234", 2017, 86000)); 35 | entityManager.persistAndFlush(new Car("Mini", "Cooper", "Yellow", "BWS-3007", 2015, 24500)); 36 | 37 | repository.deleteAll(); 38 | assertThat(repository.findAll()).isEmpty(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Chapter04/src/test/java/com/packt/cardatabase/CarRestTest.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 4 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | import org.springframework.test.web.servlet.MockMvc; 15 | 16 | @RunWith(SpringRunner.class) 17 | @SpringBootTest 18 | @AutoConfigureMockMvc 19 | public class CarRestTest { 20 | @Autowired 21 | private MockMvc mockMvc; 22 | 23 | @Test 24 | public void testAuthentication() throws Exception { 25 | // Testing authentication with correct credentials 26 | this.mockMvc.perform(post("/login").content("{\"username\":\"admin\", \"password\":\"admin\"}")). 27 | andDo(print()).andExpect(status().isOk()); 28 | 29 | // Testing authentication with wrong credentials 30 | this.mockMvc.perform(post("/login").content("{\"username\":\"admin\", \"password\":\"wrongpwd\"}")). 31 | andDo(print()).andExpect(status().is4xxClientError()); 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Chapter04/src/test/java/com/packt/cardatabase/CardatabaseApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import com.packt.cardatabase.web.CarController; 12 | 13 | @RunWith(SpringRunner.class) 14 | @SpringBootTest 15 | public class CardatabaseApplicationTests { 16 | @Autowired 17 | private CarController controller; 18 | 19 | @Test 20 | public void contextLoads() { 21 | assertThat(controller).isNotNull(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Chapter07/Readme.md: -------------------------------------------------------------------------------- 1 | Create React app: 2 | create-react-app weatherApp 3 | 4 | Replace App.js file with the file from Github 5 | 6 | Start your app: 7 | npm start -------------------------------------------------------------------------------- /Chapter07/restgithub/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | 4 | class App extends Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { keyword: '', data: [] }; 8 | } 9 | 10 | fetchData = () => { 11 | const url = `https://api.github.com/search/repositories?q=${this.state.keyword}`; 12 | fetch(url) 13 | .then(response => response.json()) 14 | .then(responseData => { 15 | this.setState({data : responseData.items }); 16 | }); 17 | } 18 | 19 | handleChange = (e) => { 20 | this.setState({keyword: e.target.value}); 21 | } 22 | 23 | render() { 24 | const tableRows = this.state.data.map((item, index) => 25 | {item.full_name} 26 | {item.html_url}); 27 | 28 | return ( 29 |
30 | 31 | 32 | {tableRows}
33 |
34 | ); 35 | } 36 | } 37 | 38 | export default App; 39 | -------------------------------------------------------------------------------- /Chapter07/weatherapp/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | 4 | class App extends Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = {temp: 0, desc: '', icon: '', loading: true} 8 | } 9 | 10 | componentDidMount() { 11 | fetch('http://api.openweathermap.org/data/2.5/weather?q=London&units=Metric&APIkey=YOUR_KEY') 12 | .then(response => response.json()) 13 | .then(responseData => { 14 | this.setState({ 15 | temp: responseData.main.temp, 16 | desc: responseData.weather[0].description, 17 | icon: responseData.weather[0].icon, 18 | loading: false 19 | }) 20 | }) 21 | .catch(err => console.error(err)); 22 | } 23 | 24 | render() { 25 | const imgSrc = `http://openweathermap.org/img/w/'${this.state.icon}.png`; 26 | 27 | if (this.state.loading) { 28 | return

Loading

; 29 | } 30 | else { 31 | return ( 32 |
33 |

Temperature: {this.state.temp} ℃

34 |

Description: {this.state.desc}

35 | Weather icon 36 |
37 | ); 38 | } 39 | } 40 | } 41 | 42 | export default App; 43 | -------------------------------------------------------------------------------- /Chapter08/ReactRouter/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import { BrowserRouter, Switch, Route, Link } from 'react-router-dom' 4 | import Contact from './Contact'; 5 | import Home from './Home'; 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
11 | 12 |
13 | Home{' '} 14 | Contact{' '} 15 | Links{' '} 16 | 17 | 18 | 19 |

Links

} /> 20 |

Page not found

} /> 21 |
22 |
23 |
24 |
25 | ); 26 | } 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /Chapter08/ReactRouter/Contact.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class Contact extends Component { 4 | render() { 5 | return ( 6 |
7 |

Contact.js

8 |
9 | ); 10 | } 11 | } 12 | 13 | export default Contact; -------------------------------------------------------------------------------- /Chapter08/ReactRouter/Home.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class Home extends Component { 4 | render() { 5 | return ( 6 |
7 |

Home.js

8 |
9 | ); 10 | } 11 | } 12 | 13 | export default Home; -------------------------------------------------------------------------------- /Chapter08/ReactTable/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import ReactTable from "react-table"; 4 | import 'react-table/react-table.css'; 5 | 6 | class App extends Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = { keyword: '', data: [] }; 10 | } 11 | 12 | fetchData = () => { 13 | const url = `https://api.github.com/search/repositories?q=${this.state.keyword}`; 14 | fetch(url) 15 | .then(response => response.json()) 16 | .then(responseData => { 17 | this.setState({data : responseData.items }); 18 | }); 19 | } 20 | 21 | handleChange = (e) => { 22 | this.setState({keyword: e.target.value}); 23 | } 24 | 25 | btnClick = (value) => { 26 | alert(value); 27 | } 28 | 29 | render() { 30 | const columns = [{ 31 | Header: 'Name', // Header of the column 32 | accessor: 'full_name' // Value accessor 33 | }, { 34 | Header: 'URL', 35 | accessor: 'html_url', 36 | }, { 37 | Header: 'Owner', 38 | accessor: 'owner.login', 39 | }, { 40 | id: 'button', 41 | sortable: false, 42 | filterable: false, 43 | width: 100, 44 | accessor: 'full_name', 45 | Cell: ({value}) => () 46 | }] 47 | 48 | return ( 49 |
50 | 51 | 52 | 58 |
59 | ); 60 | } 61 | } 62 | 63 | export default App; 64 | -------------------------------------------------------------------------------- /Chapter08/ShoppingList/AddItem.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import SkyLight from 'react-skylight'; 3 | import RaisedButton from 'material-ui/RaisedButton'; 4 | import TextField from 'material-ui/TextField'; 5 | 6 | class AddItem extends Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | addItem = () => { 12 | const item = {product: this.state.product, amount: this.state.amount}; 13 | this.props.additem(item); 14 | this.addform.hide(); 15 | } 16 | 17 | handleChange = (e) => { 18 | this.setState({[e.target.name]: e.target.value}); 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 |
25 | this.addform.show()} label="Add Item" primary={true} /> 26 |
27 | this.addform = ref} 30 | title="Add item"> 31 |
33 |
35 | 36 |
37 |
38 | ); 39 | } 40 | } 41 | 42 | export default AddItem; -------------------------------------------------------------------------------- /Chapter08/ShoppingList/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import {List, ListItem} from 'material-ui/List'; 3 | 4 | import './App.css'; 5 | import AddItem from './AddItem'; 6 | 7 | class App extends Component { 8 | constructor(props) { 9 | super(props); 10 | this.state={ items: [] }; 11 | } 12 | 13 | addItem = (item) => { 14 | this.setState({items: [item, ...this.state.items]}); 15 | } 16 | 17 | render() { 18 | const listItems = this.state.items.map((item, index) => 19 | {item.product}) 20 | return ( 21 |
22 |

Shopping list

23 | 24 | {listItems} 25 |
26 | ); 27 | } 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /Chapter10/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /Chapter10/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carfront", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.3.2", 7 | "react-confirm-alert": "^2.0.2", 8 | "react-csv": "^1.0.14", 9 | "react-dom": "^16.3.2", 10 | "react-scripts": "1.1.4", 11 | "react-skylight": "^0.5.1", 12 | "react-table": "^6.8.2", 13 | "react-toastify": "^4.0.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test --env=jsdom", 19 | "eject": "react-scripts eject" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Chapter10/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter10/public/favicon.ico -------------------------------------------------------------------------------- /Chapter10/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Chapter10/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /Chapter10/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color:lightblue; 12 | height: 50px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter10/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import Carlist from './components/Carlist'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 |

CarList

11 |
12 | 13 |
14 | ); 15 | } 16 | } 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /Chapter10/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter10/src/components/AddCar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SkyLight from 'react-skylight'; 3 | 4 | class AddCar extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = {brand: '', model: '', year: '', color: '', price: ''}; 8 | } 9 | 10 | handleChange = (event) => { 11 | this.setState( 12 | {[event.target.name]: event.target.value} 13 | ); 14 | } 15 | 16 | // Save car and close modal form 17 | handleSubmit = (event) => { 18 | event.preventDefault(); 19 | var newCar = {brand: this.state.brand, model: this.state.model, 20 | color: this.state.color, year: this.state.year, price: this.state.price}; 21 | this.props.addCar(newCar); 22 | this.refs.addDialog.hide(); 23 | } 24 | 25 | cancelSubmit = (event) => { 26 | event.preventDefault(); 27 | this.refs.addDialog.hide(); 28 | } 29 | 30 | render() { 31 | return ( 32 |
33 | 34 |

New car

35 |
36 |
37 |
38 |
39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 | ); 50 | } 51 | } 52 | 53 | export default AddCar; -------------------------------------------------------------------------------- /Chapter10/src/components/Carlist.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactTable from "react-table"; 3 | import 'react-table/react-table.css'; 4 | import { ToastContainer, toast } from 'react-toastify'; 5 | import 'react-toastify/dist/ReactToastify.css'; 6 | import {SERVER_URL} from '../constants.js'; 7 | import AddCar from './AddCar.js'; 8 | import { confirmAlert } from 'react-confirm-alert'; 9 | import 'react-confirm-alert/src/react-confirm-alert.css' 10 | import {CSVLink} from 'react-csv'; 11 | 12 | class Carlist extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.state = { cars: []}; 16 | } 17 | 18 | componentDidMount() { 19 | this.fetchCars(); 20 | } 21 | 22 | // Fetch all cars 23 | fetchCars = () => { 24 | fetch(SERVER_URL + 'api/cars') 25 | .then((response) => response.json()) 26 | .then((responseData) => { 27 | this.setState({ 28 | cars: responseData._embedded.cars, 29 | }); 30 | }) 31 | .catch(err => console.error(err)); 32 | } 33 | 34 | confirmDelete = (link) => { 35 | confirmAlert({ 36 | message: 'Are you sure to delete?', 37 | buttons: [ 38 | { 39 | label: 'Yes', 40 | onClick: () => this.onDelClick(link) 41 | }, 42 | { 43 | label: 'No', 44 | } 45 | ] 46 | }) 47 | } 48 | 49 | // Delete car 50 | onDelClick = (link) => { 51 | fetch(link, {method: 'DELETE'}) 52 | .then(res => { 53 | toast.success("Car deleted", { 54 | position: toast.POSITION.BOTTOM_LEFT 55 | }); 56 | this.fetchCars(); 57 | }) 58 | .catch(err => { 59 | toast.error("Error when deleting", { 60 | position: toast.POSITION.BOTTOM_LEFT 61 | }); 62 | console.error(err) 63 | }) 64 | } 65 | 66 | // Add new car 67 | addCar(car) { 68 | console.log(car); 69 | fetch(SERVER_URL + 'api/cars', 70 | { method: 'POST', 71 | headers: { 72 | 'Content-Type': 'application/json', 73 | }, 74 | body: JSON.stringify(car) 75 | }) 76 | .then(res => this.fetchCars()) 77 | .catch(err => console.error(err)) 78 | } 79 | 80 | // Update car 81 | updateCar(car, link) { 82 | fetch(link, 83 | { method: 'PUT', 84 | headers: { 85 | 'Content-Type': 'application/json', 86 | }, 87 | body: JSON.stringify(car) 88 | }) 89 | .then( res => 90 | toast.success("Changes saved", { 91 | position: toast.POSITION.BOTTOM_LEFT 92 | }) 93 | ) 94 | .catch( err => 95 | toast.error("Error when saving", { 96 | position: toast.POSITION.BOTTOM_LEFT 97 | }) 98 | ) 99 | } 100 | 101 | renderEditable = (cellInfo) => { 102 | return ( 103 |
{ 108 | const data = [...this.state.cars]; 109 | data[cellInfo.index][cellInfo.column.id] = e.target.innerHTML; 110 | this.setState({ cars: data }); 111 | }} 112 | dangerouslySetInnerHTML={{ 113 | __html: this.state.cars[cellInfo.index][cellInfo.column.id] 114 | }} 115 | /> 116 | ); 117 | } 118 | 119 | render() { 120 | const columns = [{ 121 | Header: 'Brand', 122 | accessor: 'brand', 123 | Cell: this.renderEditable 124 | }, { 125 | Header: 'Model', 126 | accessor: 'model', 127 | Cell: this.renderEditable 128 | }, { 129 | Header: 'Color', 130 | accessor: 'color', 131 | Cell: this.renderEditable 132 | }, { 133 | Header: 'Year', 134 | accessor: 'year', 135 | Cell: this.renderEditable 136 | }, { 137 | Header: 'Price €', 138 | accessor: 'price', 139 | Cell: this.renderEditable 140 | }, { 141 | id: 'savebutton', 142 | sortable: false, 143 | filterable: false, 144 | width: 100, 145 | accessor: '_links.self.href', 146 | Cell: ({value, row}) => () 147 | }, { 148 | id: 'delbutton', 149 | sortable: false, 150 | filterable: false, 151 | width: 100, 152 | accessor: '_links.self.href', 153 | Cell: ({value}) => () 154 | }] 155 | 156 | return ( 157 |
158 | Export CSV 159 | 160 | 162 | 163 |
164 | ); 165 | } 166 | } 167 | 168 | export default Carlist; -------------------------------------------------------------------------------- /Chapter10/src/constants.js: -------------------------------------------------------------------------------- 1 | export const SERVER_URL = 'http://localhost:8080/' -------------------------------------------------------------------------------- /Chapter10/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter10/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /Chapter10/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter10/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Chapter11/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /Chapter11/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carfront", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^1.0.0", 7 | "@material-ui/icons": "^1.0.0", 8 | "material-ui": "^0.20.1", 9 | "react": "^16.3.2", 10 | "react-confirm-alert": "^2.0.2", 11 | "react-csv": "^1.0.14", 12 | "react-dom": "^16.3.2", 13 | "react-scripts": "1.1.4", 14 | "react-skylight": "^0.5.1", 15 | "react-table": "^6.8.2" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom", 21 | "eject": "react-scripts eject" 22 | }, 23 | "devDependencies": { 24 | "enzyme": "^3.3.0", 25 | "jest-enzyme": "^6.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Chapter11/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter11/public/favicon.ico -------------------------------------------------------------------------------- /Chapter11/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Chapter11/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /Chapter11/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color:lightblue; 12 | height: 50px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter11/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import Carlist from './components/Carlist'; 4 | import AppBar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
11 | 12 | CarList 13 | 14 | 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /Chapter11/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /Chapter11/src/components/AddCar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SkyLight from 'react-skylight'; 3 | import Button from '@material-ui/core/Button'; 4 | import TextField from '@material-ui/core/TextField'; 5 | 6 | class AddCar extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {brand: '', model: '', year: '', color: '', price: ''}; 10 | } 11 | 12 | handleChange = (event) => { 13 | this.setState( 14 | {[event.target.name]: event.target.value} 15 | ); 16 | } 17 | 18 | // Save car and close modal form 19 | handleSubmit = (event) => { 20 | event.preventDefault(); 21 | var newCar = {brand: this.state.brand, model: this.state.model, 22 | color: this.state.color, year: this.state.year, price: this.state.price}; 23 | this.props.addCar(newCar); 24 | this.refs.addDialog.hide(); 25 | } 26 | 27 | cancelSubmit = (event) => { 28 | event.preventDefault(); 29 | this.refs.addDialog.hide(); 30 | } 31 | 32 | render() { 33 | return ( 34 |
35 | 36 |

New car

37 |
38 |
exit 39 |
40 |
41 |
42 |

43 | 44 | 45 | 46 |
47 |
48 | 49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | export default AddCar; -------------------------------------------------------------------------------- /Chapter11/src/components/Carlist.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactTable from "react-table"; 3 | import 'react-table/react-table.css'; 4 | import {SERVER_URL} from '../constants.js'; 5 | import AddCar from './AddCar.js'; 6 | import { confirmAlert } from 'react-confirm-alert'; 7 | import 'react-confirm-alert/src/react-confirm-alert.css' 8 | import {CSVLink} from 'react-csv'; 9 | import Button from '@material-ui/core/Button'; 10 | import Grid from '@material-ui/core/Grid'; 11 | import Snackbar from '@material-ui/core/Snackbar'; 12 | 13 | class Carlist extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { cars: [], open: false, message: ''}; 17 | } 18 | 19 | componentDidMount() { 20 | this.fetchCars(); 21 | } 22 | 23 | // Fetch all cars 24 | fetchCars = () => { 25 | fetch(SERVER_URL + 'api/cars') 26 | .then((response) => response.json()) 27 | .then((responseData) => { 28 | this.setState({ 29 | cars: responseData._embedded.cars, 30 | }); 31 | }) 32 | .catch(err => console.error(err)); 33 | } 34 | 35 | confirmDelete = (link) => { 36 | confirmAlert({ 37 | message: 'Are you sure to delete?', 38 | buttons: [ 39 | { 40 | label: 'Yes', 41 | onClick: () => this.onDelClick(link) 42 | }, 43 | { 44 | label: 'No', 45 | } 46 | ] 47 | }) 48 | } 49 | 50 | // Delete car 51 | onDelClick = (link) => { 52 | fetch(link, {method: 'DELETE'}) 53 | .then(res => { 54 | this.setState({open: true, message: 'Car deleted'}); 55 | this.fetchCars(); 56 | }) 57 | .catch(err => { 58 | this.setState({open: true, message: 'Error when deleting'}); 59 | console.error(err) 60 | }) 61 | } 62 | 63 | // Add new car 64 | addCar(car) { 65 | console.log(car); 66 | fetch(SERVER_URL + 'api/cars', 67 | { method: 'POST', 68 | headers: { 69 | 'Content-Type': 'application/json', 70 | }, 71 | body: JSON.stringify(car) 72 | }) 73 | .then(res => this.fetchCars()) 74 | .catch(err => console.error(err)) 75 | } 76 | 77 | // Update car 78 | updateCar(car, link) { 79 | fetch(link, 80 | { method: 'PUT', 81 | headers: { 82 | 'Content-Type': 'application/json', 83 | }, 84 | body: JSON.stringify(car) 85 | }) 86 | .then( res => 87 | this.setState({open: true, message: 'Changes saved'}) 88 | ) 89 | .catch( err => 90 | this.setState({open: true, message: 'Error when saving'}) 91 | ) 92 | } 93 | 94 | renderEditable = (cellInfo) => { 95 | return ( 96 |
{ 101 | const data = [...this.state.cars]; 102 | data[cellInfo.index][cellInfo.column.id] = e.target.innerHTML; 103 | this.setState({ cars: data }); 104 | }} 105 | dangerouslySetInnerHTML={{ 106 | __html: this.state.cars[cellInfo.index][cellInfo.column.id] 107 | }} 108 | /> 109 | ); 110 | } 111 | 112 | handleClose = (event, reason) => { 113 | this.setState({ open: false }); 114 | }; 115 | 116 | render() { 117 | const columns = [{ 118 | Header: 'Brand', 119 | accessor: 'brand', 120 | Cell: this.renderEditable 121 | }, { 122 | Header: 'Model', 123 | accessor: 'model', 124 | Cell: this.renderEditable 125 | }, { 126 | Header: 'Color', 127 | accessor: 'color', 128 | Cell: this.renderEditable 129 | }, { 130 | Header: 'Year', 131 | accessor: 'year', 132 | Cell: this.renderEditable 133 | }, { 134 | Header: 'Price €', 135 | accessor: 'price', 136 | Cell: this.renderEditable 137 | }, { 138 | id: 'savebutton', 139 | sortable: false, 140 | filterable: false, 141 | width: 100, 142 | accessor: '_links.self.href', 143 | Cell: ({value, row}) => () 144 | }, { 145 | id: 'delbutton', 146 | sortable: false, 147 | filterable: false, 148 | width: 100, 149 | accessor: '_links.self.href', 150 | Cell: ({value}) => () 151 | }] 152 | 153 | return ( 154 |
155 | 156 | 157 | 158 | 159 | 160 | Export CSV 161 | 162 | 163 | 164 | 166 | 170 |
171 | ); 172 | } 173 | } 174 | 175 | export default Carlist; -------------------------------------------------------------------------------- /Chapter11/src/components/Carlist.test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter11/src/components/Carlist.test.js -------------------------------------------------------------------------------- /Chapter11/src/constants.js: -------------------------------------------------------------------------------- 1 | export const SERVER_URL = 'http://localhost:8080/' -------------------------------------------------------------------------------- /Chapter11/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter11/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /Chapter11/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter11/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Chapter12/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /Chapter12/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carfront", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^1.0.0", 7 | "@material-ui/icons": "^1.0.0", 8 | "material-ui": "^0.20.1", 9 | "react": "^16.3.2", 10 | "react-confirm-alert": "^2.0.2", 11 | "react-csv": "^1.0.14", 12 | "react-dom": "^16.3.2", 13 | "react-scripts": "1.1.4", 14 | "react-skylight": "^0.5.1", 15 | "react-table": "^6.8.2" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom", 21 | "eject": "react-scripts eject" 22 | }, 23 | "devDependencies": { 24 | "enzyme": "^3.3.0", 25 | "enzyme-adapter-react-16": "^1.1.1", 26 | "jest-enzyme": "^6.0.0", 27 | "react-test-renderer": "^16.3.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Chapter12/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter12/public/favicon.ico -------------------------------------------------------------------------------- /Chapter12/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Chapter12/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/src/AddCar.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddCar from './components/AddCar'; 3 | import Enzyme, { shallow } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | 6 | Enzyme.configure({ adapter: new Adapter() }); 7 | 8 | describe('', () => { 9 | it('renders five components', () => { 10 | const wrapper = shallow(); 11 | expect(wrapper.find('TextField')).toHaveLength(5); 12 | }); 13 | }); -------------------------------------------------------------------------------- /Chapter12/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color:lightblue; 12 | height: 50px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter12/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import Carlist from './components/Carlist'; 4 | import AppBar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
11 | 12 | CarList 13 | 14 | 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /Chapter12/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import AddCar from './components/AddCar'; 5 | import renderer from 'react-test-renderer' 6 | 7 | it('renders without crashing', () => { 8 | const div = document.createElement('div'); 9 | ReactDOM.render(, div); 10 | ReactDOM.unmountComponentAtNode(div); 11 | }); 12 | 13 | it('renders a snapshot', () => { 14 | const tree = renderer.create().toJSON(); 15 | expect(tree).toMatchSnapshot(); 16 | }); -------------------------------------------------------------------------------- /Chapter12/src/components/AddCar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SkyLight from 'react-skylight'; 3 | import Button from '@material-ui/core/Button'; 4 | import TextField from '@material-ui/core/TextField'; 5 | 6 | class AddCar extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {brand: '', model: '', year: '', color: '', price: ''}; 10 | } 11 | 12 | handleChange = (event) => { 13 | this.setState( 14 | {[event.target.name]: event.target.value} 15 | ); 16 | } 17 | 18 | // Save car and close modal form 19 | handleSubmit = (event) => { 20 | event.preventDefault(); 21 | var newCar = {brand: this.state.brand, model: this.state.model, 22 | color: this.state.color, year: this.state.year, price: this.state.price}; 23 | this.props.addCar(newCar); 24 | this.refs.addDialog.hide(); 25 | } 26 | 27 | cancelSubmit = (event) => { 28 | event.preventDefault(); 29 | this.refs.addDialog.hide(); 30 | } 31 | 32 | render() { 33 | return ( 34 |
35 | 36 |

New car

37 |
38 |
exit 39 |
40 |
41 |
42 |

43 | 44 | 45 | 46 |
47 |
48 | 49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | export default AddCar; -------------------------------------------------------------------------------- /Chapter12/src/components/Carlist.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactTable from "react-table"; 3 | import 'react-table/react-table.css'; 4 | import {SERVER_URL} from '../constants.js'; 5 | import AddCar from './AddCar.js'; 6 | import { confirmAlert } from 'react-confirm-alert'; 7 | import 'react-confirm-alert/src/react-confirm-alert.css' 8 | import {CSVLink} from 'react-csv'; 9 | import Button from '@material-ui/core/Button'; 10 | import Grid from '@material-ui/core/Grid'; 11 | import Snackbar from '@material-ui/core/Snackbar'; 12 | 13 | class Carlist extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { cars: [], open: false, message: ''}; 17 | } 18 | 19 | componentDidMount() { 20 | this.fetchCars(); 21 | } 22 | 23 | // Fetch all cars 24 | fetchCars = () => { 25 | fetch(SERVER_URL + 'api/cars') 26 | .then((response) => response.json()) 27 | .then((responseData) => { 28 | this.setState({ 29 | cars: responseData._embedded.cars, 30 | }); 31 | }) 32 | .catch(err => console.error(err)); 33 | } 34 | 35 | confirmDelete = (link) => { 36 | confirmAlert({ 37 | message: 'Are you sure to delete?', 38 | buttons: [ 39 | { 40 | label: 'Yes', 41 | onClick: () => this.onDelClick(link) 42 | }, 43 | { 44 | label: 'No', 45 | } 46 | ] 47 | }) 48 | } 49 | 50 | // Delete car 51 | onDelClick = (link) => { 52 | fetch(link, {method: 'DELETE'}) 53 | .then(res => { 54 | this.setState({open: true, message: 'Car deleted'}); 55 | this.fetchCars(); 56 | }) 57 | .catch(err => { 58 | this.setState({open: true, message: 'Error when deleting'}); 59 | console.error(err) 60 | }) 61 | } 62 | 63 | // Add new car 64 | addCar(car) { 65 | console.log(car); 66 | fetch(SERVER_URL + 'api/cars', 67 | { method: 'POST', 68 | headers: { 69 | 'Content-Type': 'application/json', 70 | }, 71 | body: JSON.stringify(car) 72 | }) 73 | .then(res => this.fetchCars()) 74 | .catch(err => console.error(err)) 75 | } 76 | 77 | // Update car 78 | updateCar(car, link) { 79 | fetch(link, 80 | { method: 'PUT', 81 | headers: { 82 | 'Content-Type': 'application/json', 83 | }, 84 | body: JSON.stringify(car) 85 | }) 86 | .then( res => 87 | this.setState({open: true, message: 'Changes saved'}) 88 | ) 89 | .catch( err => 90 | this.setState({open: true, message: 'Error when saving'}) 91 | ) 92 | } 93 | 94 | renderEditable = (cellInfo) => { 95 | return ( 96 |
{ 101 | const data = [...this.state.cars]; 102 | data[cellInfo.index][cellInfo.column.id] = e.target.innerHTML; 103 | this.setState({ cars: data }); 104 | }} 105 | dangerouslySetInnerHTML={{ 106 | __html: this.state.cars[cellInfo.index][cellInfo.column.id] 107 | }} 108 | /> 109 | ); 110 | } 111 | 112 | handleClose = (event, reason) => { 113 | this.setState({ open: false }); 114 | }; 115 | 116 | render() { 117 | const columns = [{ 118 | Header: 'Brand', 119 | accessor: 'brand', 120 | Cell: this.renderEditable 121 | }, { 122 | Header: 'Model', 123 | accessor: 'model', 124 | Cell: this.renderEditable 125 | }, { 126 | Header: 'Color', 127 | accessor: 'color', 128 | Cell: this.renderEditable 129 | }, { 130 | Header: 'Year', 131 | accessor: 'year', 132 | Cell: this.renderEditable 133 | }, { 134 | Header: 'Price €', 135 | accessor: 'price', 136 | Cell: this.renderEditable 137 | }, { 138 | id: 'savebutton', 139 | sortable: false, 140 | filterable: false, 141 | width: 100, 142 | accessor: '_links.self.href', 143 | Cell: ({value, row}) => () 144 | }, { 145 | id: 'delbutton', 146 | sortable: false, 147 | filterable: false, 148 | width: 100, 149 | accessor: '_links.self.href', 150 | Cell: ({value}) => () 151 | }] 152 | 153 | return ( 154 |
155 | 156 | 157 | 158 | 159 | 160 | Export CSV 161 | 162 | 163 | 164 | 166 | 170 |
171 | ); 172 | } 173 | } 174 | 175 | export default Carlist; -------------------------------------------------------------------------------- /Chapter12/src/components/getCars.js: -------------------------------------------------------------------------------- 1 | // Fetch all cars 2 | const getCars = (url) => { 3 | fetch(url + 'api/cars') 4 | .then((response) => response.json()) 5 | .then((result) => {return result;}) 6 | } 7 | 8 | export {getCars} 9 | -------------------------------------------------------------------------------- /Chapter12/src/constants.js: -------------------------------------------------------------------------------- 1 | export const SERVER_URL = 'http://localhost:8080/' -------------------------------------------------------------------------------- /Chapter12/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter12/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /Chapter12/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter12/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Chapter13/back end/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /Chapter13/back end/README.MD: -------------------------------------------------------------------------------- 1 | 2 | Steps to set up the back end 3 | 1. Install MariaDB 4 | 2. Create a database called cardb 5 | 3. Clone the back end 6 | 4. Modify application.properties file and set your database password 7 | 5. Run the back end either with Elipse IDE or by typing mvn spring-boot:run (Needs maven installation) 8 | -------------------------------------------------------------------------------- /Chapter13/back end/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /Chapter13/back end/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.packt 7 | cardatabase 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | cardatabase 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.1.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jpa 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-security 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-data-rest 43 | 44 | 45 | org.mariadb.jdbc 46 | mariadb-java-client 47 | 48 | 49 | com.h2database 50 | h2 51 | test 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-devtools 56 | runtime 57 | 58 | 59 | io.jsonwebtoken 60 | jjwt 61 | 0.9.0 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/AuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.ServletRequest; 8 | import javax.servlet.ServletResponse; 9 | import javax.servlet.http.HttpServletRequest; 10 | 11 | import org.springframework.security.core.Authentication; 12 | import org.springframework.security.core.context.SecurityContextHolder; 13 | import org.springframework.web.filter.GenericFilterBean; 14 | 15 | import com.packt.cardatabase.service.AuthenticationService; 16 | 17 | public class AuthenticationFilter extends GenericFilterBean { 18 | 19 | @Override 20 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 21 | throws IOException, ServletException { 22 | Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest)request); 23 | 24 | SecurityContextHolder.getContext().setAuthentication(authentication); 25 | filterChain.doFilter(request, response); 26 | } 27 | } -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/CardatabaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | 9 | import com.packt.cardatabase.domain.Car; 10 | import com.packt.cardatabase.domain.CarRepository; 11 | import com.packt.cardatabase.domain.Owner; 12 | import com.packt.cardatabase.domain.OwnerRepository; 13 | import com.packt.cardatabase.domain.User; 14 | import com.packt.cardatabase.domain.UserRepository; 15 | 16 | @SpringBootApplication 17 | public class CardatabaseApplication { 18 | @Autowired 19 | private CarRepository repository; 20 | 21 | @Autowired 22 | private OwnerRepository orepository; 23 | 24 | @Autowired 25 | private UserRepository urepository; 26 | 27 | public static void main(String[] args) { 28 | SpringApplication.run(CardatabaseApplication.class, args); 29 | } 30 | 31 | @Bean 32 | CommandLineRunner runner() { 33 | return args -> { 34 | Owner owner1 = new Owner("John" , "Johnson"); 35 | Owner owner2 = new Owner("Mary" , "Robinson"); 36 | orepository.save(owner1); 37 | orepository.save(owner2); 38 | 39 | repository.save(new Car("Ford", "Mustang", "Red", "ADF-1121", 2017, 59000, owner1)); 40 | repository.save(new Car("Nissan", "Leaf", "White", "SSJ-3002", 2014, 29000, owner2)); 41 | repository.save(new Car("Toyota", "Prius", "Silver", "KKO-0212", 2018, 39000, owner2)); 42 | 43 | urepository.save(new User("user", "$2a$04$1.YhMIgNX/8TkCKGFUONWO1waedKhQ5KrnB30fl0Q01QKqmzLf.Zi", "USER")); 44 | urepository.save(new User("admin", "$2a$04$KNLUwOWHVQZVpXyMBNc7JOzbLiBjb9Tk9bP7KNcPI12ICuvzXQQKG", "ADMIN")); 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/LoginFilter.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 13 | import org.springframework.security.core.Authentication; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; 16 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 17 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; 19 | import com.packt.cardatabase.domain.AccountCredentials; 20 | import com.packt.cardatabase.service.AuthenticationService; 21 | 22 | public class LoginFilter extends AbstractAuthenticationProcessingFilter { 23 | 24 | public LoginFilter(String url, AuthenticationManager authManager) { 25 | super(new AntPathRequestMatcher(url)); 26 | setAuthenticationManager(authManager); 27 | } 28 | 29 | @Override 30 | public Authentication attemptAuthentication( 31 | HttpServletRequest req, HttpServletResponse res) 32 | throws AuthenticationException, IOException, ServletException { 33 | AccountCredentials creds = new ObjectMapper() 34 | .readValue(req.getInputStream(), AccountCredentials.class); 35 | return getAuthenticationManager().authenticate( 36 | new UsernamePasswordAuthenticationToken( 37 | creds.getUsername(), 38 | creds.getPassword(), 39 | Collections.emptyList() 40 | ) 41 | ); 42 | } 43 | 44 | @Override 45 | protected void successfulAuthentication( 46 | HttpServletRequest req, 47 | HttpServletResponse res, FilterChain chain, 48 | Authentication auth) throws IOException, ServletException { 49 | AuthenticationService.addToken(res, auth.getName()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 15 | import org.springframework.web.cors.CorsConfiguration; 16 | import org.springframework.web.cors.CorsConfigurationSource; 17 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 18 | 19 | import com.packt.cardatabase.service.UserDetailServiceImpl; 20 | 21 | @Configuration 22 | @EnableWebSecurity 23 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 24 | @Autowired 25 | private UserDetailServiceImpl userDetailsService; 26 | 27 | @Override 28 | protected void configure(HttpSecurity http) throws Exception { 29 | http.csrf().disable().cors().and().authorizeRequests() 30 | .antMatchers(HttpMethod.POST, "/login").permitAll() 31 | .anyRequest().authenticated() 32 | .and() 33 | // Filter for the api/login requests 34 | .addFilterBefore(new LoginFilter("/login", authenticationManager()), 35 | UsernamePasswordAuthenticationFilter.class) 36 | // Filter for other requests to check JWT in header 37 | .addFilterBefore(new AuthenticationFilter(), 38 | UsernamePasswordAuthenticationFilter.class); 39 | } 40 | 41 | @Bean 42 | CorsConfigurationSource corsConfigurationSource() { 43 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 44 | CorsConfiguration config = new CorsConfiguration(); 45 | config.setAllowedOrigins(Arrays.asList("*")); 46 | config.setAllowedMethods(Arrays.asList("*")); 47 | config.setAllowedHeaders(Arrays.asList("*")); 48 | config.setAllowCredentials(true); 49 | config.applyPermitDefaultValues(); 50 | 51 | source.registerCorsConfiguration("/**", config); 52 | return source; 53 | } 54 | 55 | @Autowired 56 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 57 | auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/AccountCredentials.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | public class AccountCredentials { 4 | private String username; 5 | private String password; 6 | 7 | public String getUsername() { 8 | return username; 9 | } 10 | public void setUsername(String username) { 11 | this.username = username; 12 | } 13 | public String getPassword() { 14 | return password; 15 | } 16 | public void setPassword(String password) { 17 | this.password = password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/Car.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.FetchType; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.JoinColumn; 9 | import javax.persistence.ManyToOne; 10 | 11 | @Entity 12 | public class Car { 13 | @Id 14 | @GeneratedValue(strategy=GenerationType.AUTO) 15 | private long id; 16 | private String brand, model, color, registerNumber; 17 | private int year, price; 18 | 19 | @ManyToOne(fetch = FetchType.LAZY) 20 | @JoinColumn(name = "owner") 21 | private Owner owner; 22 | 23 | public Car() {} 24 | 25 | public Car(String brand, String model, String color, String registerNumber, int year, int price) { 26 | super(); 27 | this.brand = brand; 28 | this.model = model; 29 | this.color = color; 30 | this.registerNumber = registerNumber; 31 | this.year = year; 32 | this.price = price; 33 | } 34 | 35 | public Car(String brand, String model, String color, String registerNumber, int year, int price, Owner owner) { 36 | super(); 37 | this.brand = brand; 38 | this.model = model; 39 | this.color = color; 40 | this.registerNumber = registerNumber; 41 | this.year = year; 42 | this.price = price; 43 | this.owner = owner; 44 | } 45 | 46 | 47 | public long getId() { 48 | return id; 49 | } 50 | 51 | public void setId(long id) { 52 | this.id = id; 53 | } 54 | 55 | public Owner getOwner() { 56 | return owner; 57 | } 58 | 59 | public void setOwner(Owner owner) { 60 | this.owner = owner; 61 | } 62 | 63 | public String getBrand() { 64 | return brand; 65 | } 66 | public void setBrand(String brand) { 67 | this.brand = brand; 68 | } 69 | public String getModel() { 70 | return model; 71 | } 72 | public void setModel(String model) { 73 | this.model = model; 74 | } 75 | public String getColor() { 76 | return color; 77 | } 78 | public void setColor(String color) { 79 | this.color = color; 80 | } 81 | public String getRegisterNumber() { 82 | return registerNumber; 83 | } 84 | public void setRegisterNumber(String registerNumber) { 85 | this.registerNumber = registerNumber; 86 | } 87 | public int getYear() { 88 | return year; 89 | } 90 | public void setYear(int year) { 91 | this.year = year; 92 | } 93 | public int getPrice() { 94 | return price; 95 | } 96 | public void setPrice(int price) { 97 | this.price = price; 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/CarRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.query.Param; 7 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 8 | 9 | @RepositoryRestResource 10 | public interface CarRepository extends CrudRepository { 11 | // Fetch cars by brand 12 | List findByBrand(@Param("brand") String brand); 13 | 14 | // Fetch cars by color 15 | List findByColor(@Param("color") String color); 16 | } 17 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/Owner.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import java.util.List; 4 | 5 | import javax.persistence.CascadeType; 6 | import javax.persistence.Entity; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.GenerationType; 9 | import javax.persistence.Id; 10 | import javax.persistence.OneToMany; 11 | 12 | import com.fasterxml.jackson.annotation.JsonIgnore; 13 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 14 | 15 | @Entity 16 | @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 17 | public class Owner { 18 | @Id 19 | @GeneratedValue(strategy=GenerationType.AUTO) 20 | private long ownerid; 21 | private String firstname, lastname; 22 | 23 | @OneToMany(cascade = CascadeType.ALL, mappedBy="owner") 24 | @JsonIgnore 25 | private List cars; 26 | 27 | public Owner() {} 28 | 29 | public Owner(String firstname, String lastname) { 30 | super(); 31 | this.firstname = firstname; 32 | this.lastname = lastname; 33 | } 34 | 35 | public List getCars() { 36 | return cars; 37 | } 38 | 39 | public void setCars(List cars) { 40 | this.cars = cars; 41 | } 42 | 43 | public long getOwnerid() { 44 | return ownerid; 45 | } 46 | 47 | public void setOwnerid(long ownerid) { 48 | this.ownerid = ownerid; 49 | } 50 | 51 | public String getFirstname() { 52 | return firstname; 53 | } 54 | 55 | public void setFirstname(String firstname) { 56 | this.firstname = firstname; 57 | } 58 | 59 | public String getLastname() { 60 | return lastname; 61 | } 62 | 63 | public void setLastname(String lastname) { 64 | this.lastname = lastname; 65 | } 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/OwnerRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | 5 | public interface OwnerRepository extends CrudRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/User.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | 9 | @Entity 10 | public class User { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY) 13 | @Column(nullable = false, updatable = false) 14 | private Long id; 15 | 16 | @Column(nullable = false, unique = true) 17 | private String username; 18 | 19 | @Column(nullable = false) 20 | private String password; 21 | 22 | @Column(nullable = false) 23 | private String role; 24 | 25 | public User() { 26 | } 27 | 28 | public User(String username, String password, String role) { 29 | super(); 30 | this.username = username; 31 | this.password = password; 32 | this.role = role; 33 | } 34 | 35 | public Long getId() { 36 | return id; 37 | } 38 | 39 | public void setId(Long id) { 40 | this.id = id; 41 | } 42 | 43 | public String getUsername() { 44 | return username; 45 | } 46 | 47 | public void setUsername(String username) { 48 | this.username = username; 49 | } 50 | 51 | public String getPassword() { 52 | return password; 53 | } 54 | 55 | public void setPassword(String password) { 56 | this.password = password; 57 | } 58 | 59 | public String getRole() { 60 | return role; 61 | } 62 | 63 | public void setRole(String role) { 64 | this.role = role; 65 | } 66 | } -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/domain/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.domain; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface UserRepository extends CrudRepository { 8 | User findByUsername(String username); 9 | } -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.service; 2 | 3 | import io.jsonwebtoken.Jwts; 4 | import io.jsonwebtoken.SignatureAlgorithm; 5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 | import org.springframework.security.core.Authentication; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.util.Date; 11 | 12 | import static java.util.Collections.emptyList; 13 | 14 | public class AuthenticationService { 15 | static final long EXPIRATIONTIME = 864_000_00; // 1 day in milliseconds 16 | static final String SIGNINGKEY = "SecretKey"; 17 | static final String PREFIX = "Bearer"; 18 | 19 | static public void addToken(HttpServletResponse res, String username) { 20 | String JwtToken = Jwts.builder().setSubject(username) 21 | .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME)) 22 | .signWith(SignatureAlgorithm.HS512, SIGNINGKEY) 23 | .compact(); 24 | res.addHeader("Authorization", PREFIX + " " + JwtToken); 25 | res.addHeader("Access-Control-Expose-Headers", "Authorization"); 26 | } 27 | 28 | static public Authentication getAuthentication(HttpServletRequest request) { 29 | String token = request.getHeader("Authorization"); 30 | if (token != null) { 31 | String user = Jwts.parser() 32 | .setSigningKey(SIGNINGKEY) 33 | .parseClaimsJws(token.replace(PREFIX, "")) 34 | .getBody() 35 | .getSubject(); 36 | 37 | if (user != null) 38 | return new UsernamePasswordAuthenticationToken(user, null, emptyList()); 39 | } 40 | return null; 41 | } 42 | } -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/service/UserDetailServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.service; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.core.authority.AuthorityUtils; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 8 | import org.springframework.stereotype.Service; 9 | 10 | import com.packt.cardatabase.domain.User; 11 | import com.packt.cardatabase.domain.UserRepository; 12 | 13 | @Service 14 | public class UserDetailServiceImpl implements UserDetailsService { 15 | @Autowired 16 | private UserRepository repository; 17 | 18 | 19 | @Override 20 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 21 | { 22 | User currentUser = repository.findByUsername(username); 23 | UserDetails user = new org.springframework.security.core.userdetails.User(username, currentUser.getPassword() 24 | , true, true, true, true, AuthorityUtils.createAuthorityList(currentUser.getRole())); 25 | return user; 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /Chapter13/back end/src/main/java/com/packt/cardatabase/web/CarController.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import com.packt.cardatabase.domain.Car; 8 | import com.packt.cardatabase.domain.CarRepository; 9 | 10 | @RestController 11 | public class CarController { 12 | @Autowired 13 | private CarRepository repository; 14 | 15 | @RequestMapping("/cars") 16 | public Iterable getCars() { 17 | return repository.findAll(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter13/back end/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mariadb://localhost:3306/cardb 2 | spring.datasource.username=root 3 | spring.datasource.password=YOUR_PASSWORD 4 | 5 | spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 6 | 7 | spring.jpa.generate-ddl=true 8 | spring.jpa.hibernate.ddl-auto=create-drop 9 | 10 | spring.jpa.show-sql=true 11 | 12 | spring.data.rest.basePath=/api 13 | -------------------------------------------------------------------------------- /Chapter13/back end/src/test/java/com/packt/cardatabase/CarRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 9 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import com.packt.cardatabase.domain.Car; 13 | import com.packt.cardatabase.domain.CarRepository; 14 | 15 | @RunWith(SpringRunner.class) 16 | @DataJpaTest 17 | public class CarRepositoryTest { 18 | @Autowired 19 | private TestEntityManager entityManager; 20 | 21 | @Autowired 22 | private CarRepository repository; 23 | 24 | @Test 25 | public void saveCar() { 26 | Car car = new Car("Tesla", "Model X", "White", "ABC-1234", 2017, 86000); 27 | entityManager.persistAndFlush(car); 28 | 29 | assertThat(car.getId()).isNotNull(); 30 | } 31 | 32 | @Test 33 | public void deleteCars() { 34 | entityManager.persistAndFlush(new Car("Tesla", "Model X", "White", "ABC-1234", 2017, 86000)); 35 | entityManager.persistAndFlush(new Car("Mini", "Cooper", "Yellow", "BWS-3007", 2015, 24500)); 36 | 37 | repository.deleteAll(); 38 | assertThat(repository.findAll()).isEmpty(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Chapter13/back end/src/test/java/com/packt/cardatabase/CarRestTest.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 4 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | import org.springframework.test.web.servlet.MockMvc; 15 | 16 | @RunWith(SpringRunner.class) 17 | @SpringBootTest 18 | @AutoConfigureMockMvc 19 | public class CarRestTest { 20 | @Autowired 21 | private MockMvc mockMvc; 22 | 23 | @Test 24 | public void testAuthentication() throws Exception { 25 | // Testing authentication with correct credentials 26 | this.mockMvc.perform(post("/login").content("{\"username\":\"admin\", \"password\":\"admin\"}")). 27 | andDo(print()).andExpect(status().isOk()); 28 | 29 | // Testing authentication with wrong credentials 30 | this.mockMvc.perform(post("/login").content("{\"username\":\"admin\", \"password\":\"wrongpwd\"}")). 31 | andDo(print()).andExpect(status().is4xxClientError()); 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Chapter13/back end/src/test/java/com/packt/cardatabase/CardatabaseApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.packt.cardatabase; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import com.packt.cardatabase.web.CarController; 12 | 13 | @RunWith(SpringRunner.class) 14 | @SpringBootTest 15 | public class CardatabaseApplicationTests { 16 | @Autowired 17 | private CarController controller; 18 | 19 | @Test 20 | public void contextLoads() { 21 | assertThat(controller).isNotNull(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Chapter13/front end/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /Chapter13/front end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carfront", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^1.0.0", 7 | "@material-ui/icons": "^1.0.0", 8 | "material-ui": "^0.20.1", 9 | "react": "^16.3.2", 10 | "react-confirm-alert": "^2.0.2", 11 | "react-csv": "^1.0.14", 12 | "react-dom": "^16.3.2", 13 | "react-scripts": "1.1.4", 14 | "react-skylight": "^0.5.1", 15 | "react-table": "^6.8.2" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom", 21 | "eject": "react-scripts eject" 22 | }, 23 | "devDependencies": { 24 | "enzyme": "^3.3.0", 25 | "enzyme-adapter-react-16": "^1.1.1", 26 | "jest-enzyme": "^6.0.0", 27 | "react-test-renderer": "^16.3.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Chapter13/front end/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-Full-Stack-Development-with-Spring-Boot-2.0-and-React/e160462dc6dfd9a86d710e7d18a5ccc5061decf2/Chapter13/front end/public/favicon.ico -------------------------------------------------------------------------------- /Chapter13/front end/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Chapter13/front end/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /Chapter13/front end/src/AddCar.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AddCar from './components/AddCar'; 3 | import Enzyme, { shallow } from 'enzyme'; 4 | import Adapter from 'enzyme-adapter-react-16'; 5 | 6 | Enzyme.configure({ adapter: new Adapter() }); 7 | 8 | describe('', () => { 9 | it('renders five components', () => { 10 | const wrapper = shallow(); 11 | expect(wrapper.find('TextField')).toHaveLength(5); 12 | }); 13 | }); -------------------------------------------------------------------------------- /Chapter13/front end/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color:lightblue; 12 | height: 50px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-title { 18 | font-size: 1.5em; 19 | } 20 | 21 | .App-intro { 22 | font-size: large; 23 | } 24 | 25 | @keyframes App-logo-spin { 26 | from { transform: rotate(0deg); } 27 | to { transform: rotate(360deg); } 28 | } 29 | -------------------------------------------------------------------------------- /Chapter13/front end/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import Login from './components/Login'; 4 | import AppBar from '@material-ui/core/AppBar'; 5 | import Toolbar from '@material-ui/core/Toolbar'; 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
11 | 12 | CarList 13 | 14 | 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /Chapter13/front end/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import AddCar from './components/AddCar'; 5 | import renderer from 'react-test-renderer' 6 | 7 | it('renders without crashing', () => { 8 | const div = document.createElement('div'); 9 | ReactDOM.render(, div); 10 | ReactDOM.unmountComponentAtNode(div); 11 | }); 12 | 13 | it('renders a snapshot', () => { 14 | const tree = renderer.create().toJSON(); 15 | expect(tree).toMatchSnapshot(); 16 | }); -------------------------------------------------------------------------------- /Chapter13/front end/src/components/AddCar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SkyLight from 'react-skylight'; 3 | import Button from '@material-ui/core/Button'; 4 | import TextField from '@material-ui/core/TextField'; 5 | 6 | class AddCar extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | this.state = {brand: '', model: '', year: '', color: '', price: ''}; 10 | } 11 | 12 | handleChange = (event) => { 13 | this.setState( 14 | {[event.target.name]: event.target.value} 15 | ); 16 | } 17 | 18 | // Save car and close modal form 19 | handleSubmit = (event) => { 20 | event.preventDefault(); 21 | var newCar = {brand: this.state.brand, model: this.state.model, 22 | color: this.state.color, year: this.state.year, price: this.state.price}; 23 | this.props.addCar(newCar); 24 | this.refs.addDialog.hide(); 25 | } 26 | 27 | cancelSubmit = (event) => { 28 | event.preventDefault(); 29 | this.refs.addDialog.hide(); 30 | } 31 | 32 | render() { 33 | return ( 34 |
35 | 36 |

New car

37 |
38 |
39 |
40 |
41 |
42 |

43 | 44 | 45 | 46 |
47 |
48 | 49 |
50 |
51 | ); 52 | } 53 | } 54 | 55 | export default AddCar; -------------------------------------------------------------------------------- /Chapter13/front end/src/components/Carlist.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactTable from "react-table"; 3 | import 'react-table/react-table.css'; 4 | import {SERVER_URL} from '../constants.js'; 5 | import AddCar from './AddCar.js'; 6 | import { confirmAlert } from 'react-confirm-alert'; 7 | import 'react-confirm-alert/src/react-confirm-alert.css' 8 | import {CSVLink} from 'react-csv'; 9 | import Button from '@material-ui/core/Button'; 10 | import Grid from '@material-ui/core/Grid'; 11 | import Snackbar from '@material-ui/core/Snackbar'; 12 | 13 | class Carlist extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { cars: [], open: false, message: ''}; 17 | } 18 | 19 | componentDidMount() { 20 | this.fetchCars(); 21 | } 22 | 23 | // Fetch all cars 24 | fetchCars = () => { 25 | const token = sessionStorage.getItem("jwt"); 26 | fetch(SERVER_URL + 'api/cars', 27 | { 28 | headers: {'Authorization': token} 29 | }) 30 | .then((response) => response.json()) 31 | .then((responseData) => { 32 | this.setState({ 33 | cars: responseData._embedded.cars, 34 | }); 35 | }) 36 | .catch(err => console.error(err)); 37 | } 38 | 39 | confirmDelete = (link) => { 40 | confirmAlert({ 41 | message: 'Are you sure to delete?', 42 | buttons: [ 43 | { 44 | label: 'Yes', 45 | onClick: () => this.onDelClick(link) 46 | }, 47 | { 48 | label: 'No', 49 | } 50 | ] 51 | }) 52 | } 53 | 54 | // Delete car 55 | onDelClick = (link) => { 56 | const token = sessionStorage.getItem("jwt"); 57 | fetch(link, 58 | { 59 | method: 'DELETE', 60 | headers: {'Authorization': token} 61 | } 62 | ) 63 | .then(res => { 64 | this.setState({open: true, message: 'Car deleted'}); 65 | this.fetchCars(); 66 | }) 67 | .catch(err => { 68 | this.setState({open: true, message: 'Error when deleting'}); 69 | console.error(err) 70 | }) 71 | } 72 | 73 | // Add new car 74 | addCar(car) { 75 | const token = sessionStorage.getItem("jwt"); 76 | fetch(SERVER_URL + 'api/cars', 77 | { method: 'POST', 78 | headers: { 79 | 'Content-Type': 'application/json', 80 | 'Authorization': token 81 | }, 82 | body: JSON.stringify(car) 83 | }) 84 | .then(res => this.fetchCars()) 85 | .catch(err => console.error(err)) 86 | } 87 | 88 | // Update car 89 | updateCar(car, link) { 90 | const token = sessionStorage.getItem("jwt"); 91 | fetch(link, 92 | { method: 'PUT', 93 | headers: { 94 | 'Content-Type': 'application/json', 95 | 'Authorization': token 96 | }, 97 | body: JSON.stringify(car) 98 | }) 99 | .then( res => 100 | this.setState({open: true, message: 'Changes saved'}) 101 | ) 102 | .catch( err => 103 | this.setState({open: true, message: 'Error when saving'}) 104 | ) 105 | } 106 | 107 | renderEditable = (cellInfo) => { 108 | return ( 109 |
{ 114 | const data = [...this.state.cars]; 115 | data[cellInfo.index][cellInfo.column.id] = e.target.innerHTML; 116 | this.setState({ cars: data }); 117 | }} 118 | dangerouslySetInnerHTML={{ 119 | __html: this.state.cars[cellInfo.index][cellInfo.column.id] 120 | }} 121 | /> 122 | ); 123 | } 124 | 125 | handleClose = (event, reason) => { 126 | this.setState({ open: false }); 127 | }; 128 | 129 | render() { 130 | const columns = [{ 131 | Header: 'Brand', 132 | accessor: 'brand', 133 | Cell: this.renderEditable 134 | }, { 135 | Header: 'Model', 136 | accessor: 'model', 137 | Cell: this.renderEditable 138 | }, { 139 | Header: 'Color', 140 | accessor: 'color', 141 | Cell: this.renderEditable 142 | }, { 143 | Header: 'Year', 144 | accessor: 'year', 145 | Cell: this.renderEditable 146 | }, { 147 | Header: 'Price €', 148 | accessor: 'price', 149 | Cell: this.renderEditable 150 | }, { 151 | id: 'savebutton', 152 | sortable: false, 153 | filterable: false, 154 | width: 100, 155 | accessor: '_links.self.href', 156 | Cell: ({value, row}) => () 157 | }, { 158 | id: 'delbutton', 159 | sortable: false, 160 | filterable: false, 161 | width: 100, 162 | accessor: '_links.self.href', 163 | Cell: ({value}) => () 164 | }] 165 | 166 | return ( 167 |
168 | 169 | 170 | 171 | 172 | 173 | Export CSV 174 | 175 | 176 | 177 | 179 | 182 |
183 | ); 184 | } 185 | } 186 | 187 | export default Carlist; -------------------------------------------------------------------------------- /Chapter13/front end/src/components/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import TextField from '@material-ui/core/TextField'; 3 | import Button from '@material-ui/core/Button'; 4 | import Snackbar from '@material-ui/core/Snackbar'; 5 | import Carlist from './Carlist'; 6 | import {SERVER_URL} from '../constants.js'; 7 | 8 | class Login extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = {username: '', password: '', isAuthenticated: false, open: false}; 12 | } 13 | 14 | login = () => { 15 | const user = {username: this.state.username, password: this.state.password}; 16 | fetch(SERVER_URL + 'login', { 17 | method: 'POST', 18 | body: JSON.stringify(user) 19 | }) 20 | .then(res => { 21 | const jwtToken = res.headers.get('Authorization'); 22 | if (jwtToken !== null) { 23 | sessionStorage.setItem("jwt", jwtToken); 24 | this.setState({isAuthenticated: true}); 25 | } 26 | else { 27 | this.setState({open: true}); 28 | } 29 | }) 30 | .catch(err => console.error(err)) 31 | } 32 | 33 | handleChange = (event) => { 34 | this.setState({[event.target.name] : event.target.value}); 35 | } 36 | 37 | handleClose = (event) => { 38 | this.setState({ open: false }); 39 | } 40 | 41 | render() { 42 | if (this.state.isAuthenticated === true) { 43 | return () 44 | } 45 | else { 46 | return ( 47 |
48 |
49 |
51 |

53 | 54 | 57 |
58 | ); 59 | } 60 | } 61 | } 62 | 63 | export default Login; 64 | -------------------------------------------------------------------------------- /Chapter13/front end/src/constants.js: -------------------------------------------------------------------------------- 1 | export const SERVER_URL = 'http://localhost:8080/' -------------------------------------------------------------------------------- /Chapter13/front end/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter13/front end/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /Chapter13/front end/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Chapter13/front end/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Hands-On Full Stack Development with Spring Boot 2.0 and React 5 | 6 | Hands-On Full Stack Development with Spring Boot 2.0 and React 7 | 8 | This is the code repository for [Hands-On Full Stack Development with Spring Boot 2.0 and React](https://www.packtpub.com/application-development/hands-full-stack-development-spring-boot-20-and-react?utm_source=github&utm_medium=repository&utm_campaign=9781789138085), published by Packt. 9 | 10 | **Build modern and scalable full stack applications using the Java-based Spring Framework 5.0 and React** 11 | 12 | ## What is this book about? 13 | Apart from knowing how to write frontend and backend code, a full-stack engineer has to tackle all the problems that are encountered in the application development life cycle, starting from a simple idea to UI design, the technical design, and all the way to implementing, testing, production, deployment, and monitoring. This book covers the full set of technologies that you need to know to become a full-stack web developer with Spring Boot for the backend and React for the frontend. 14 | 15 | This book covers the following exciting features: 16 | * Create a RESTful web service with Spring Boot 17 | * Understand how to use React for frontend programming 18 | * Gain knowledge of how to create unit tests using JUnit 19 | * Discover the techniques that go into securing the backend using Spring Security 20 | * Learn how to use Material UI in the user interface to make it more user-friendly 21 | 22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1789138086) today! 23 | 24 | https://www.packtpub.com/ 26 | 27 | 28 | ## Instructions and Navigations 29 | All of the code is organized into folders. For example, Chapter03. 30 | 31 | The code will look like the following: 32 | ``` 33 | @Entity 34 | public class Car { 35 | } 36 | ``` 37 | 38 | **Following is what you need for this book:** 39 | Java developers who are familiar with Spring, but have not yet built full-stack applications 40 | 41 | With the following software and hardware list you can run all code files present in the book (Chapter 1-15). 42 | 43 | ### Software and Hardware List 44 | 45 | | Chapter | OS required | Hardware required | 46 | | -------- | ------------------------------------| -----------------------------------| 47 | | 1 | Windows, Mac OS X, and Linux (Any) | Common modern workstation | 48 | | 5 | Windows, Mac OS X, and Linux (Any) | Common modern workstation | 49 | | 14 | Windows, Mac OS X, and Linux (Any) | Common modern workstation | 50 | 51 | 52 | 53 | ### Related products 54 | * Hands-On High Performance with Spring 5 [[Packt]](https://www.packtpub.com/application-development/hands-high-performance-spring-5?utm_source=github&utm_medium=repository&utm_campaign=9781788838382) [[Amazon]](https://www.amazon.com/dp/1788838386) 55 | 56 | * Building RESTful Web Services with Spring 5 - Second Edition [[Packt]](https://www.packtpub.com/application-development/building-restful-web-services-spring-5-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781788475891) [[Amazon]](https://www.amazon.com/dp/1788475895) 57 | 58 | ## Get to Know the Author 59 | **Juha Hinkula** 60 | is a software development lecturer at Haaga-Helia University of Applied Sciences in Finland. He received an MSc degree in computer science from the University of Helsinki. He has over 15 years of industry experience in software development. Over the past few years, he has focused on modern full stack development. He is also a passionate mobile developer with Android native technology and nowadays also uses React Native. 61 | 62 | ### Suggestions and Feedback 63 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 64 | ### Download a free PDF 65 | 66 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
67 |

https://packt.link/free-ebook/9781789138085

--------------------------------------------------------------------------------