├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── .validationconfig
├── Dockerfile
├── LICENSE.md
├── docker-compose.yml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── readme.md
└── src
├── main
├── java
│ └── com
│ │ └── bankofspring
│ │ ├── BankOfSpringApplication.java
│ │ ├── api
│ │ ├── .gitkeep
│ │ └── v1
│ │ │ ├── controller
│ │ │ ├── AccountController.java
│ │ │ └── CustomerController.java
│ │ │ └── request
│ │ │ ├── account
│ │ │ ├── CreateAccountRequest.java
│ │ │ ├── DepositRequest.java
│ │ │ ├── TransferFundRequest.java
│ │ │ └── WithdrawalRequest.java
│ │ │ └── customer
│ │ │ └── CreateCustomerRequest.java
│ │ ├── configuration
│ │ └── SwaggerConfig.java
│ │ ├── domain
│ │ ├── model
│ │ │ ├── Account.java
│ │ │ ├── BaseDomainObject.java
│ │ │ ├── Branch.java
│ │ │ └── Customer.java
│ │ └── repository
│ │ │ ├── AccountRepository.java
│ │ │ ├── BranchRepository.java
│ │ │ └── CustomerRepository.java
│ │ ├── dto
│ │ ├── AccountDto.java
│ │ └── CustomerDto.java
│ │ ├── exception
│ │ ├── ApiError.java
│ │ ├── BankException.java
│ │ ├── DuplicateEntityException.java
│ │ ├── EntityException.java
│ │ ├── EntityNotFoundException.java
│ │ └── handler
│ │ │ └── RestExceptionHandler.java
│ │ └── service
│ │ ├── account
│ │ ├── AccountService.java
│ │ ├── AccountServiceImpl.java
│ │ └── exception
│ │ │ ├── AccountException.java
│ │ │ └── InsufficientFundsException.java
│ │ └── customer
│ │ ├── CustomerService.java
│ │ └── CustomerServiceImpl.java
└── resources
│ ├── application.properties
│ ├── data-h2.sql
│ └── images
│ ├── BankOfSpring.png
│ └── logo.png
└── test
└── java
└── com
└── bankofspring
├── BankOfSpringApplicationTests.java
├── BankOfSpringApplicationUnitTests.java
├── integrationtest
├── .gitkeep
└── controller
│ ├── AccountControllerTest.java
│ └── CustomerControllerTest.java
└── unittest
├── .gitkeep
└── controller
└── CustomerControllerMockMvcStandaloneTest.java
/.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 |
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | nbproject/private/
20 | build/
21 | nbbuild/
22 | dist/
23 | nbdist/
24 | .nb-gradle/
25 |
26 | # User-specific stuff:
27 | .idea/**/workspace.xml
28 | .idea/**/tasks.xml
29 | .idea/dictionaries
30 |
31 | # Sensitive or high-churn files:
32 | .idea/**/dataSources/
33 | .idea/**/dataSources.ids
34 | .idea/**/dataSources.xml
35 | .idea/**/dataSources.local.xml
36 | .idea/**/sqlDataSources.xml
37 | .idea/**/dynamic.xml
38 | .idea/**/uiDesigner.xml
39 |
40 | # Gradle:
41 | .idea/**/gradle.xml
42 | .idea/**/libraries
43 |
44 | # CMake
45 | cmake-build-debug/
46 | cmake-build-release/
47 |
48 | # Mongo Explorer plugin:
49 | .idea/**/mongoSettings.xml
50 |
51 | ## File-based project format:
52 | *.iws
53 |
54 | ## Plugin-specific files:
55 |
56 | # IntelliJ
57 | out/
58 |
59 | # mpeltonen/sbt-idea plugin
60 | .idea_modules/
61 |
62 | # JIRA plugin
63 | atlassian-ide-plugin.xml
64 |
65 | # Cursive Clojure plugin
66 | .idea/replstate.xml
67 |
68 | # Crashlytics plugin (for Android Studio and IntelliJ)
69 | com_crashlytics_export_strings.xml
70 | crashlytics.properties
71 | crashlytics-build.properties
72 | fabric.properties
73 |
74 | # Mac OS
75 | .DS_Store
76 |
77 | # Logs
78 | logs/
79 | .log
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SystangoTechnologies/BankOfSpring/e09bc0312e726bec2d555e1f4593813312c68a8a/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.validationconfig:
--------------------------------------------------------------------------------
1 | PR_TITLE_REGEX=/((?:[a-z][a-z]+))(\d{3})(:)([a-z0-9])/i
2 | COMMIT_MESSAGE_REGEX=/(#)(#)((?:[a-z][a-z]+))(\d{3})(:)([a-z0-9])/i
3 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-jre-alpine
2 | WORKDIR /usr/spring/bank
3 | COPY ./target/bankofspring-0.0.1-SNAPSHOT.jar /usr/spring/bank
4 | EXPOSE 8080
5 | CMD ["java", "-jar", "bankofspring-0.0.1-SNAPSHOT.jar"]
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [2018] [Systango Technologies]
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.
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2.2'
2 | services:
3 | bankofspring:
4 | build:
5 | context: ./
6 | dockerfile: Dockerfile
7 | ports:
8 | - '8080:8080'
9 | image: bankofspring
10 | mem_limit: "256m"
11 | cpu_count: 1
12 | cpu_percent: 50
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.systango
7 | bankofspring
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | BankOfSpring
12 | Production ready SpringBoot application with Txn management.
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.0.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-actuator
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.springframework.boot
42 | spring-boot-starter-web
43 |
44 |
45 | org.springframework.data
46 | spring-data-rest-hal-browser
47 |
48 |
49 |
50 | com.h2database
51 | h2
52 | runtime
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-starter-test
57 | test
58 |
59 |
60 | org.springframework.restdocs
61 | spring-restdocs-mockmvc
62 | test
63 |
64 |
65 |
66 |
67 | org.apache.commons
68 | commons-lang3
69 | 3.5
70 |
71 |
72 | org.projectlombok
73 | lombok
74 | true
75 |
76 |
77 | org.modelmapper
78 | modelmapper
79 | 2.0.0
80 |
81 |
82 | io.springfox
83 | springfox-swagger2
84 | 2.9.2
85 |
86 |
87 | io.springfox
88 | springfox-swagger-ui
89 | 2.9.2
90 |
91 |
92 |
93 |
94 |
95 |
96 | org.springframework.boot
97 | spring-boot-maven-plugin
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## BankOfSpring
4 | Production ready maven based Spring Boot starter kit application with example cases of handling transactions with Spring.
5 |
6 | ## Description
7 | Starter kit for booting up the development of a API oriented and transaction based spring Java server. It contains the best practices and latest tools that a spring boot developer should opt for in a fresh development. Since JPA is used, developers are free to opt for any SQL based DB engine for persistence (H2 has been used as an example with this project). The preferred IDE for development is IntelliJ which comes with a plethora of useful JAVA tools to support Spring Boot development, but developers are free to opt for Eclipse or STS as well. The focus in this project is solely upon the SpringBoot development with business cases involving transactions and writting proper unit and integration tests for them.
8 |
9 | ## Technology
10 |
11 | - **Spring Boot** - Server side framework
12 | - **JPA** - Entity framework
13 | - **Lombok** - Provides automated getter/setters
14 | - **Actuator** - Application insights on the fly
15 | - **Spring Security** - Spring's security layer
16 | - **Thymeleaf** - Template Engine
17 | - **Devtools** - Support Hot-Code Swapping with live browser reload
18 | - **JJWT** - JWT tokens for API authentication
19 | - **Swagger** - In-built swagger2 documentation support
20 | - **Docker** - Docker containers
21 | - **Junit** - Unit testing framework
22 | - **H2** - H2 database embedded version
23 |
24 | ## Application Structure
25 |
26 | ## Running the server locally
27 | The BankOfSpring application can be started using your favourite IDE and its run configuration support. If you are a terminal savvy, please use the following command -
28 |
29 | ````
30 | mvn spring-boot:run
31 | ````
32 |
33 | ## Docker
34 | BankOfSpring supports docker container out of the box. This boilerplate is meant to cater to both web based applications as well as scalable micro services written in Java. Please select one of the following two ways to use docker to build and run the application -
35 |
36 | **Dockerfile**
37 |
38 | To build a fresh image, use -
39 | ````
40 | docker build -t bankofspring .
41 | ````
42 | To run the new image, use -
43 | ````
44 | docker run -p 8080:8080 bankofspring
45 | ````
46 |
47 | **Docker-Compose**
48 |
49 | To build a fresh image, use -
50 | ````
51 | docker-compose build
52 | ````
53 | To run the new image, use -
54 | ````
55 | docker-compose up
56 | ````
57 |
58 | ## Swagger Documentation
59 | Swagger documentation is in-built in this starter-kit and can be accessed at the following URL -
60 | ````
61 | http://:8080/swagger-ui.html
62 | ````
63 |
64 | ## Unit test cases
65 | There are multiple unit test cases written to cover the different components of the application. However there is a global application test suite file _**BankOfSpringApplicationUnitTests.java**_ that combines all the test cases in a logical manner to create a complete suite. It can be run from command prompt using the following command -
66 |
67 | ````
68 | mvn clean test -Dtest=BankOfSpringApplicationUnitTests
69 | ````
70 |
71 | ## Integration test cases
72 | There are multiple integration test cases written to cover the different components of the application. However there is a global application test suite file _**BankOfSpringApplicationTests.java**_ that combines all the test cases in a logical manner to create a complete suite. It can be run from command prompt using the following command -
73 |
74 | ````
75 | mvn clean test -Dtest=BankOfSpringApplicationTests
76 | ````
77 |
78 | ## Contributors
79 | [Arpit Khandelwal](https://www.linkedin.com/in/arpitkhandelwal1984/)
80 |
81 | ## License
82 | This project is licensed under the terms of the MIT license.
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/BankOfSpringApplication.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring;
2 |
3 | import org.modelmapper.ModelMapper;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.context.annotation.Bean;
7 |
8 | @SpringBootApplication
9 | public class BankOfSpringApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(BankOfSpringApplication.class, args);
13 | }
14 |
15 | @Bean
16 | public ModelMapper modelMapper() {
17 | return new ModelMapper();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SystangoTechnologies/BankOfSpring/e09bc0312e726bec2d555e1f4593813312c68a8a/src/main/java/com/bankofspring/api/.gitkeep
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/controller/AccountController.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.controller;
2 |
3 | import com.bankofspring.api.v1.request.account.CreateAccountRequest;
4 | import com.bankofspring.api.v1.request.account.DepositRequest;
5 | import com.bankofspring.api.v1.request.account.TransferFundRequest;
6 | import com.bankofspring.api.v1.request.account.WithdrawalRequest;
7 | import com.bankofspring.domain.model.Account;
8 | import com.bankofspring.dto.AccountDto;
9 | import com.bankofspring.exception.EntityException;
10 | import com.bankofspring.exception.EntityNotFoundException;
11 | import com.bankofspring.service.account.AccountService;
12 | import com.bankofspring.service.account.exception.InsufficientFundsException;
13 | import org.modelmapper.ModelMapper;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.web.bind.annotation.*;
16 |
17 | import javax.validation.Valid;
18 | import java.util.Collections;
19 | import java.util.List;
20 | import java.util.stream.Collectors;
21 |
22 | /**
23 | * Created by Arpit Khandelwal.
24 | */
25 | @RestController
26 | @RequestMapping("/v1/account")
27 | public class AccountController {
28 |
29 | @Autowired
30 | private AccountService accountService;
31 |
32 | @Autowired
33 | private ModelMapper modelMapper;
34 |
35 | @GetMapping(value = "/")
36 | public List getAccounts() {
37 | List accounts = accountService.getAllAccounts();
38 | if (accounts != null && !accounts.isEmpty()) {
39 | return accounts
40 | .stream()
41 | .map(account -> modelMapper.map(account, AccountDto.class))
42 | .collect(Collectors.toList());
43 | }
44 | return Collections.emptyList();
45 | }
46 |
47 | @GetMapping(value = "/{accountNumber}")
48 | public AccountDto getAccountByNumber(@PathVariable("accountNumber") Long accountNumber) throws EntityNotFoundException {
49 | Account account = accountService.getAccount(accountNumber);
50 | if (account != null) {
51 | return modelMapper.map(account, AccountDto.class);
52 | }
53 | return null;
54 | }
55 |
56 | @PostMapping("/create")
57 | public AccountDto createAccount(@RequestBody @Valid CreateAccountRequest createAccountRequest) throws EntityException {
58 | AccountDto accountDto = modelMapper.map(createAccountRequest, AccountDto.class);
59 | Account account = accountService.createAccount(accountDto);
60 | if (account != null) {
61 | return modelMapper.map(account, AccountDto.class);
62 | }
63 | return null;
64 | }
65 |
66 | @PostMapping("/deposit")
67 | public AccountDto depositMoney(@RequestBody @Valid DepositRequest depositRequest) throws EntityNotFoundException {
68 | AccountDto accountDto = modelMapper.map(depositRequest, AccountDto.class);
69 | Account account = accountService.creditAmount(accountDto, depositRequest.getDepositAmt());
70 | if (account != null) {
71 | return modelMapper.map(account, AccountDto.class);
72 | }
73 | return null;
74 | }
75 |
76 | @PostMapping("/withdraw")
77 | public AccountDto withdrawMoney(@RequestBody @Valid WithdrawalRequest withdrawalRequest) throws EntityNotFoundException, InsufficientFundsException {
78 | AccountDto accountDto = modelMapper.map(withdrawalRequest, AccountDto.class);
79 | Account account = accountService.debitAmount(accountDto, withdrawalRequest.getWithdrawlAmt());
80 | if (account != null) {
81 | return modelMapper.map(account, AccountDto.class);
82 | }
83 | return null;
84 | }
85 |
86 | @PostMapping("/transfer")
87 | public List transferMoney(@RequestBody @Valid TransferFundRequest transferFundRequest) throws InsufficientFundsException, EntityNotFoundException {
88 | AccountDto debitAccountDto = new AccountDto().setAccountNumber(transferFundRequest.getDebitAccountNumber());
89 | AccountDto creditAccountDto = new AccountDto().setAccountNumber(transferFundRequest.getCreditAccountNumber());
90 | List accounts = accountService.transferFunds(debitAccountDto,creditAccountDto,transferFundRequest.getAmount());
91 | return accounts
92 | .stream()
93 | .map(account -> modelMapper.map(account, AccountDto.class))
94 | .collect(Collectors.toList());
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/controller/CustomerController.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.controller;
2 |
3 | import com.bankofspring.api.v1.request.customer.CreateCustomerRequest;
4 | import com.bankofspring.domain.model.Customer;
5 | import com.bankofspring.dto.CustomerDto;
6 | import com.bankofspring.exception.EntityException;
7 | import com.bankofspring.exception.EntityNotFoundException;
8 | import com.bankofspring.service.customer.CustomerService;
9 | import org.modelmapper.ModelMapper;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.web.bind.annotation.*;
12 |
13 | import javax.validation.Valid;
14 | import java.util.Collections;
15 | import java.util.List;
16 | import java.util.stream.Collectors;
17 |
18 | /**
19 | * Created by Arpit Khandelwal.
20 | */
21 | @RestController
22 | @RequestMapping("/v1/customer")
23 | public class CustomerController {
24 | @Autowired
25 | private CustomerService customerService;
26 |
27 | @Autowired
28 | private ModelMapper mapper;
29 |
30 | @GetMapping(value = "/")
31 | public List getAllCustomers() {
32 | List customers = customerService.getAllCustomers();
33 | if (customers != null && !customers.isEmpty()) {
34 | return customers
35 | .stream()
36 | .map(customer -> mapper.map(customer, CustomerDto.class))
37 | .collect(Collectors.toList());
38 | }
39 | return Collections.emptyList();
40 | }
41 |
42 | @GetMapping(value = "/{ssn}")
43 | public CustomerDto getCustomer(@PathVariable("ssn") String ssn) throws EntityNotFoundException {
44 | Customer customer = customerService.getCustomer(ssn);
45 | if (customer != null) {
46 | return mapper.map(customer, CustomerDto.class);
47 | }
48 | return null;
49 | }
50 |
51 | @PostMapping("/create")
52 | public CustomerDto createCustomer(@RequestBody @Valid CreateCustomerRequest createCustomerRequest) throws EntityException {
53 | CustomerDto customerDto = mapper.map(createCustomerRequest, CustomerDto.class);
54 | Customer customer = customerService.createCustomer(customerDto);
55 | if (customer != null) {
56 | return mapper.map(customer, CustomerDto.class);
57 | }
58 | return null;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/request/account/CreateAccountRequest.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.request.account;
2 |
3 | import com.bankofspring.domain.model.Account;
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import lombok.Getter;
6 | import lombok.NoArgsConstructor;
7 | import lombok.Setter;
8 | import lombok.experimental.Accessors;
9 |
10 | import javax.validation.constraints.NotNull;
11 | import java.math.BigDecimal;
12 |
13 | /**
14 | * Created by Arpit Khandelwal.
15 | */
16 | @Getter
17 | @Setter
18 | @Accessors(chain = true)
19 | @NoArgsConstructor
20 | @JsonIgnoreProperties(ignoreUnknown = true)
21 | public class CreateAccountRequest {
22 |
23 | @NotNull(message = "{constraints.NotEmpty.message}")
24 | private Long customerId;
25 |
26 | @NotNull(message = "{constraints.NotEmpty.message}")
27 | private Account.AccountType type;
28 |
29 | @NotNull(message = "{constraints.NotEmpty.message}")
30 | private Long branchId;
31 |
32 | @NotNull(message = "{constraints.NotEmpty.message}")
33 | private BigDecimal balance;
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/request/account/DepositRequest.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.request.account;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import lombok.experimental.Accessors;
8 |
9 | import javax.validation.constraints.NotNull;
10 | import java.math.BigDecimal;
11 |
12 | /**
13 | * Created by Arpit Khandelwal.
14 | */
15 | @Getter
16 | @Setter
17 | @Accessors(chain = true)
18 | @NoArgsConstructor
19 | @JsonIgnoreProperties(ignoreUnknown = true)
20 | public class DepositRequest {
21 | @NotNull(message = "{constraints.NotEmpty.message}")
22 | private Long accountNumber;
23 |
24 | @NotNull(message = "{constraints.NotEmpty.message}")
25 | private BigDecimal depositAmt;
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/request/account/TransferFundRequest.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.request.account;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import lombok.experimental.Accessors;
8 |
9 | import javax.validation.constraints.NotNull;
10 | import java.math.BigDecimal;
11 |
12 | /**
13 | * Created by Arpit Khandelwal.
14 | */
15 | @Getter
16 | @Setter
17 | @Accessors(chain = true)
18 | @NoArgsConstructor
19 | @JsonIgnoreProperties(ignoreUnknown = true)
20 | public class TransferFundRequest {
21 |
22 | @NotNull(message = "{constraints.NotEmpty.message}")
23 | private Long debitAccountNumber;
24 |
25 | @NotNull(message = "{constraints.NotEmpty.message}")
26 | private Long creditAccountNumber;
27 |
28 | @NotNull(message = "{constraints.NotEmpty.message}")
29 | private BigDecimal amount;
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/request/account/WithdrawalRequest.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.request.account;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import lombok.experimental.Accessors;
8 |
9 | import javax.validation.constraints.NotNull;
10 | import java.math.BigDecimal;
11 |
12 | /**
13 | * Created by Arpit Khandelwal.
14 | */
15 | @Getter
16 | @Setter
17 | @Accessors(chain = true)
18 | @NoArgsConstructor
19 | @JsonIgnoreProperties(ignoreUnknown = true)
20 | public class WithdrawalRequest {
21 | @NotNull(message = "{constraints.NotEmpty.message}")
22 | private Long accountNumber;
23 |
24 | @NotNull(message = "{constraints.NotEmpty.message}")
25 | private BigDecimal withdrawlAmt;
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/api/v1/request/customer/CreateCustomerRequest.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.api.v1.request.customer;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import lombok.experimental.Accessors;
8 |
9 | import javax.validation.constraints.NotNull;
10 |
11 | /**
12 | * Created by Arpit Khandelwal.
13 | */
14 | @Getter
15 | @Setter
16 | @Accessors(chain = true)
17 | @NoArgsConstructor
18 | @JsonIgnoreProperties(ignoreUnknown = true)
19 | public class CreateCustomerRequest {
20 | @NotNull(message = "{constraints.NotEmpty.message}")
21 | private String name;
22 |
23 | @NotNull(message = "{constraints.NotEmpty.message}")
24 | private String ssn;
25 |
26 | private String address1;
27 | private String address2;
28 | private String city;
29 | private String contactNumber;
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/configuration/SwaggerConfig.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.configuration;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import springfox.documentation.builders.ApiInfoBuilder;
6 | import springfox.documentation.builders.PathSelectors;
7 | import springfox.documentation.builders.RequestHandlerSelectors;
8 | import springfox.documentation.service.ApiInfo;
9 | import springfox.documentation.service.Contact;
10 | import springfox.documentation.spi.DocumentationType;
11 | import springfox.documentation.spring.web.plugins.Docket;
12 | import springfox.documentation.swagger2.annotations.EnableSwagger2;
13 |
14 | /**
15 | * Created by Arpit Khandelwal.
16 | */
17 | @Configuration
18 | @EnableSwagger2
19 | public class SwaggerConfig {
20 | @Bean
21 | public Docket api() {
22 | return new Docket(DocumentationType.SWAGGER_2)
23 | .apiInfo(getApiInfo())
24 | .select()
25 | .apis(RequestHandlerSelectors.basePackage("com.bankofspring.api.v1.controller"))
26 | .paths(PathSelectors.any())
27 | .build();
28 | }
29 |
30 | private ApiInfo getApiInfo() {
31 | Contact contact = new Contact("Arpit Khandelwal", "http://www.systango.com", "arpit@systango.com");
32 | return new ApiInfoBuilder()
33 | .title("SpringBoot Bank")
34 | .description("Bank Api Definition")
35 | .version("1.0.0")
36 | .license("MIT")
37 | .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
38 | .contact(contact)
39 | .build();
40 | }
41 |
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/model/Account.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.model;
2 |
3 | import com.bankofspring.service.account.exception.InsufficientFundsException;
4 | import lombok.Getter;
5 | import lombok.NoArgsConstructor;
6 | import lombok.Setter;
7 | import lombok.experimental.Accessors;
8 | import org.hibernate.annotations.OnDelete;
9 | import org.hibernate.annotations.OnDeleteAction;
10 |
11 | import javax.persistence.*;
12 | import java.math.BigDecimal;
13 |
14 | /**
15 | * Created by Arpit Khandelwal.
16 | */
17 | @Setter
18 | @Getter
19 | @Accessors(chain = true)
20 | @NoArgsConstructor
21 | @Entity
22 | public class Account extends BaseDomainObject {
23 |
24 | @Id
25 | @GeneratedValue(strategy = GenerationType.IDENTITY)
26 | private Long accountNumber;
27 |
28 | @ManyToOne(fetch = FetchType.LAZY)
29 | @JoinColumn(name = "branchId", nullable = false)
30 | @OnDelete(action = OnDeleteAction.CASCADE)
31 | private Branch coreBranch;
32 |
33 | @ManyToOne(fetch = FetchType.LAZY)
34 | @JoinColumn(name = "customerId", nullable = false)
35 | @OnDelete(action = OnDeleteAction.CASCADE)
36 | private Customer accountOwner;
37 |
38 | private BigDecimal balance;
39 |
40 | private AccountType type;
41 |
42 | /**
43 | * Debits the given amount from current account balance.
44 | * @param debitAmount
45 | * @throws InsufficientFundsException
46 | */
47 | public void debit(BigDecimal debitAmount) throws InsufficientFundsException {
48 | if(this.balance.compareTo(debitAmount) >= 0){
49 | this.balance = this.balance.subtract(debitAmount);
50 | return;
51 | }
52 | throw new InsufficientFundsException(this,debitAmount);
53 | }
54 |
55 | /**
56 | * Credits the given amount to current account balance
57 | * @param creditAmount
58 | */
59 | public void credit(BigDecimal creditAmount) {
60 | this.balance = this.balance.add(creditAmount);
61 | }
62 |
63 | public enum AccountType {
64 | SAVINGS, CURRENT, LOAN;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/model/BaseDomainObject.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.model;
2 |
3 | import lombok.Data;
4 | import lombok.NoArgsConstructor;
5 | import org.hibernate.annotations.CreationTimestamp;
6 | import org.hibernate.annotations.UpdateTimestamp;
7 |
8 | import javax.persistence.MappedSuperclass;
9 | import java.io.Serializable;
10 | import java.util.Date;
11 |
12 | /**
13 | * Represents all common properties of standard domain objects
14 | *
15 | * @author Arpit Khandelwal
16 | */
17 | @MappedSuperclass
18 | @Data
19 | @NoArgsConstructor
20 | public abstract class BaseDomainObject implements Serializable {
21 |
22 | @CreationTimestamp
23 | protected Date createTimestamp = new Date();
24 |
25 | @UpdateTimestamp
26 | protected Date lastEditTimestamp;
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/model/Branch.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.model;
2 |
3 | import lombok.Getter;
4 | import lombok.NoArgsConstructor;
5 | import lombok.Setter;
6 | import lombok.experimental.Accessors;
7 |
8 | import javax.persistence.Entity;
9 | import javax.persistence.GeneratedValue;
10 | import javax.persistence.GenerationType;
11 | import javax.persistence.Id;
12 | import java.math.BigDecimal;
13 |
14 | /**
15 | * Created by Arpit Khandelwal.
16 | */
17 | @Getter
18 | @Setter
19 | @Accessors(chain = true)
20 | @NoArgsConstructor
21 | @Entity
22 | public class Branch extends BaseDomainObject {
23 | @Id
24 | @GeneratedValue(strategy = GenerationType.IDENTITY)
25 | private Long branchId;
26 |
27 | private String name;
28 | private String city;
29 | private BigDecimal assets;
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/model/Customer.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.model;
2 |
3 | import lombok.Getter;
4 | import lombok.Setter;
5 | import lombok.experimental.Accessors;
6 |
7 | import javax.persistence.*;
8 | import java.util.Set;
9 |
10 | /**
11 | * Created by Arpit Khandelwal.
12 | */
13 | @Getter
14 | @Setter
15 | @Accessors(chain = true)
16 | @Entity
17 | public class Customer extends BaseDomainObject {
18 |
19 | @Id
20 | @GeneratedValue(strategy = GenerationType.IDENTITY)
21 | protected Long customerId;
22 |
23 | @Column(unique = true)
24 | private String ssn;
25 |
26 | @OneToMany(cascade = CascadeType.ALL,
27 | fetch = FetchType.LAZY,
28 | mappedBy = "accountOwner")
29 | private Set accounts;
30 |
31 | private String name;
32 | private String address1;
33 | private String address2;
34 | private String city;
35 | private String contactNumber;
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/repository/AccountRepository.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.repository;
2 |
3 | import com.bankofspring.domain.model.Account;
4 | import org.springframework.data.repository.CrudRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | /**
8 | * Created by Arpit Khandelwal.
9 | */
10 | @Repository
11 | public interface AccountRepository extends CrudRepository {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/repository/BranchRepository.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.repository;
2 |
3 | import com.bankofspring.domain.model.Branch;
4 | import org.springframework.data.repository.CrudRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | /**
8 | * Created by Arpit Khandelwal.
9 | */
10 | @Repository
11 | public interface BranchRepository extends CrudRepository {
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/domain/repository/CustomerRepository.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.domain.repository;
2 |
3 | import com.bankofspring.domain.model.Customer;
4 | import org.springframework.data.repository.CrudRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | /**
8 | * Created by Arpit Khandelwal.
9 | */
10 | @Repository
11 | public interface CustomerRepository extends CrudRepository {
12 | Customer findBySsn(String ssn);
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/dto/AccountDto.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.dto;
2 |
3 | import com.bankofspring.domain.model.Account;
4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5 | import com.fasterxml.jackson.annotation.JsonInclude;
6 | import lombok.Getter;
7 | import lombok.NoArgsConstructor;
8 | import lombok.Setter;
9 | import lombok.ToString;
10 | import lombok.experimental.Accessors;
11 |
12 | import java.math.BigDecimal;
13 |
14 | /**
15 | * Created by Arpit Khandelwal.
16 | */
17 | @Getter
18 | @Setter
19 | @Accessors(chain = true)
20 | @NoArgsConstructor
21 | @ToString
22 | @JsonInclude(value = JsonInclude.Include.NON_NULL)
23 | @JsonIgnoreProperties(ignoreUnknown = true)
24 | public class AccountDto {
25 | private Long accountNumber;
26 | private Long customerId;
27 | private Long branchId;
28 | private Account.AccountType type;
29 | private BigDecimal balance;
30 | private CustomerDto accountOwner;
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/dto/CustomerDto.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import com.fasterxml.jackson.annotation.JsonInclude;
5 | import lombok.Getter;
6 | import lombok.NoArgsConstructor;
7 | import lombok.Setter;
8 | import lombok.ToString;
9 | import lombok.experimental.Accessors;
10 |
11 | /**
12 | * Created by Arpit Khandelwal.
13 | */
14 | @Getter
15 | @Setter
16 | @Accessors(chain = true)
17 | @NoArgsConstructor
18 | @ToString
19 | @JsonInclude(value = JsonInclude.Include.NON_NULL)
20 | @JsonIgnoreProperties(ignoreUnknown = true)
21 | public class CustomerDto {
22 | private Long customerId;
23 | private String ssn;
24 | private String name;
25 | private String address1;
26 | private String address2;
27 | private String city;
28 | private String contactNumber;
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/exception/ApiError.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.exception;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonFormat;
5 | import com.fasterxml.jackson.annotation.JsonTypeInfo;
6 | import com.fasterxml.jackson.databind.DatabindContext;
7 | import com.fasterxml.jackson.databind.JavaType;
8 | import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
9 | import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
10 | import com.fasterxml.jackson.databind.type.SimpleType;
11 | import com.fasterxml.jackson.databind.type.TypeFactory;
12 | import lombok.AllArgsConstructor;
13 | import lombok.Data;
14 | import lombok.EqualsAndHashCode;
15 | import org.hibernate.validator.internal.engine.path.PathImpl;
16 | import org.springframework.http.HttpStatus;
17 | import org.springframework.validation.FieldError;
18 | import org.springframework.validation.ObjectError;
19 |
20 | import javax.validation.ConstraintViolation;
21 | import java.io.IOException;
22 | import java.time.LocalDateTime;
23 | import java.util.ArrayList;
24 | import java.util.List;
25 | import java.util.Set;
26 |
27 | /**
28 | * Created by Arpit Khandelwal.
29 | */
30 | @Data
31 | @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.CUSTOM, property = "error", visible = true)
32 | @JsonTypeIdResolver(LowerCaseClassNameResolver.class)
33 | public class ApiError {
34 |
35 | private HttpStatus status;
36 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
37 | private LocalDateTime timestamp;
38 | private String message;
39 | private String debugMessage;
40 | private List subErrors;
41 |
42 | private ApiError() {
43 | timestamp = LocalDateTime.now();
44 | }
45 |
46 | public ApiError(HttpStatus status) {
47 | this();
48 | this.status = status;
49 | }
50 |
51 | public ApiError(HttpStatus status, Throwable ex) {
52 | this();
53 | this.status = status;
54 | this.message = "Unexpected error";
55 | this.debugMessage = ex.getLocalizedMessage();
56 | }
57 |
58 | public ApiError(HttpStatus status, String message, Throwable ex) {
59 | this();
60 | this.status = status;
61 | this.message = message;
62 | this.debugMessage = ex.getLocalizedMessage();
63 | }
64 |
65 | private void addSubError(ApiSubError subError) {
66 | if (subErrors == null) {
67 | subErrors = new ArrayList<>();
68 | }
69 | subErrors.add(subError);
70 | }
71 |
72 | private void addValidationError(String object, String field, Object rejectedValue, String message) {
73 | addSubError(new ApiValidationError(object, field, rejectedValue, message));
74 | }
75 |
76 | private void addValidationError(String object, String message) {
77 | addSubError(new ApiValidationError(object, message));
78 | }
79 |
80 | private void addValidationError(FieldError fieldError) {
81 | this.addValidationError(
82 | fieldError.getObjectName(),
83 | fieldError.getField(),
84 | fieldError.getRejectedValue(),
85 | fieldError.getDefaultMessage());
86 | }
87 |
88 | public void addValidationErrors(List fieldErrors) {
89 | fieldErrors.forEach(this::addValidationError);
90 | }
91 |
92 | private void addValidationError(ObjectError objectError) {
93 | this.addValidationError(
94 | objectError.getObjectName(),
95 | objectError.getDefaultMessage());
96 | }
97 |
98 | public void addValidationError(List globalErrors) {
99 | globalErrors.forEach(this::addValidationError);
100 | }
101 |
102 | /**
103 | * Utility method for adding error of ConstraintViolation. Usually when a @Validated validation fails.
104 | *
105 | * @param cv the ConstraintViolation
106 | */
107 | private void addValidationError(ConstraintViolation> cv) {
108 | this.addValidationError(
109 | cv.getRootBeanClass().getSimpleName(),
110 | ((PathImpl) cv.getPropertyPath()).getLeafNode().asString(),
111 | cv.getInvalidValue(),
112 | cv.getMessage());
113 | }
114 |
115 | public void addValidationErrors(Set> constraintViolations) {
116 | constraintViolations.forEach(this::addValidationError);
117 | }
118 |
119 |
120 | abstract class ApiSubError {
121 |
122 | }
123 |
124 | @Data
125 | @EqualsAndHashCode(callSuper = false)
126 | @AllArgsConstructor
127 | class ApiValidationError extends ApiSubError {
128 | private String object;
129 | private String field;
130 | private Object rejectedValue;
131 | private String message;
132 |
133 | ApiValidationError(String object, String message) {
134 | this.object = object;
135 | this.message = message;
136 | }
137 | }
138 | }
139 |
140 | class LowerCaseClassNameResolver extends TypeIdResolverBase {
141 |
142 | @Override
143 | public String idFromValue(Object value) {
144 | return value.getClass().getSimpleName().toLowerCase();
145 | }
146 |
147 | @Override
148 | public String idFromValueAndType(Object value, Class> suggestedType) {
149 | return idFromValue(value);
150 | }
151 |
152 | @Override
153 | public JsonTypeInfo.Id getMechanism() {
154 | return JsonTypeInfo.Id.CUSTOM;
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/exception/BankException.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.exception;
2 |
3 | /**
4 | * Created by Arpit Khandelwal.
5 | */
6 | public class BankException extends Exception {
7 | public BankException (String message){
8 | super(message);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/exception/DuplicateEntityException.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.exception;
2 |
3 | /**
4 | * Created by Arpit Khandelwal.
5 | */
6 | public class DuplicateEntityException extends EntityException {
7 | public DuplicateEntityException(Class clazz, String... searchParamsMap) {
8 | super(clazz, " was already found for parameters ", searchParamsMap);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/exception/EntityException.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.exception;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.stream.IntStream;
8 |
9 | /**
10 | * Created by Arpit Khandelwal.
11 | */
12 | public class EntityException extends Exception {
13 |
14 | public EntityException(Class clazz, String indicator, String... searchParamsMap) {
15 | super(EntityException.generateMessage(clazz.getSimpleName(), indicator, toMap(String.class, String.class, searchParamsMap)));
16 | }
17 |
18 | private static String generateMessage(String entity, String indicator, Map searchParams) {
19 | return StringUtils.capitalize(entity) + indicator + searchParams;
20 | }
21 |
22 | private static Map toMap(
23 | Class keyType, Class valueType, Object... entries) {
24 | if (entries.length % 2 == 1)
25 | throw new IllegalArgumentException("Invalid entries");
26 | return IntStream.range(0, entries.length / 2).map(i -> i * 2)
27 | .collect(HashMap::new,
28 | (m, i) -> m.put(keyType.cast(entries[i]), valueType.cast(entries[i + 1])),
29 | Map::putAll);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/exception/EntityNotFoundException.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.exception;
2 |
3 | /**
4 | * Created by Arpit Khandelwal.
5 | */
6 | public class EntityNotFoundException extends EntityException {
7 |
8 | public EntityNotFoundException(Class clazz, String... searchParamsMap) {
9 | super(clazz, " was not found for parameters ", searchParamsMap);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/com/bankofspring/exception/handler/RestExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.bankofspring.exception.handler;
2 |
3 |
4 | import com.bankofspring.exception.ApiError;
5 | import com.bankofspring.exception.BankException;
6 | import com.bankofspring.exception.DuplicateEntityException;
7 | import com.bankofspring.exception.EntityNotFoundException;
8 | import org.hibernate.exception.ConstraintViolationException;
9 | import org.springframework.core.Ordered;
10 | import org.springframework.core.annotation.Order;
11 | import org.springframework.dao.DataIntegrityViolationException;
12 | import org.springframework.http.HttpHeaders;
13 | import org.springframework.http.HttpStatus;
14 | import org.springframework.http.ResponseEntity;
15 | import org.springframework.http.converter.HttpMessageNotReadableException;
16 | import org.springframework.http.converter.HttpMessageNotWritableException;
17 | import org.springframework.web.HttpMediaTypeNotSupportedException;
18 | import org.springframework.web.bind.MethodArgumentNotValidException;
19 | import org.springframework.web.bind.MissingServletRequestParameterException;
20 | import org.springframework.web.bind.annotation.ControllerAdvice;
21 | import org.springframework.web.bind.annotation.ExceptionHandler;
22 | import org.springframework.web.context.request.ServletWebRequest;
23 | import org.springframework.web.context.request.WebRequest;
24 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
25 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
26 |
27 | import static org.springframework.http.HttpStatus.BAD_REQUEST;
28 | import static org.springframework.http.HttpStatus.NOT_FOUND;
29 |
30 | /**
31 | * Created by Arpit Khandelwal.
32 | */
33 | @Order(Ordered.HIGHEST_PRECEDENCE)
34 | @ControllerAdvice
35 | public class RestExceptionHandler extends ResponseEntityExceptionHandler {
36 |
37 | /**
38 | * Handle MissingServletRequestParameterException. Triggered when a 'required' request parameter is missing.
39 | *
40 | * @param ex MissingServletRequestParameterException
41 | * @param headers HttpHeaders
42 | * @param status HttpStatus
43 | * @param request WebRequest
44 | * @return the ApiError object
45 | */
46 | @Override
47 | protected ResponseEntity