diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..226effd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+*.autofetch
+*.sql
+*.orig
+.classpath
+.project
+.settings/
+target/
+logs/
+log/
+ebean-autotune.xml
+ebean-profiling*.xml
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+/test-azwarakbar.iml
diff --git a/HELP.md b/HELP.md
new file mode 100644
index 0000000..be7096b
--- /dev/null
+++ b/HELP.md
@@ -0,0 +1,9 @@
+# Getting Started
+
+### Reference Documentation
+For further reference, please consider the following sections:
+
+* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
+* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/maven-plugin/reference/html/#build-image)
+
diff --git a/README.md b/README.md
index c5786a0..6878b9f 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ The main challenge will be to build a simple user management tool that will perf
about look of the application. There are no business rules & guidelines other than to show us what you’re truly made of. It can be as simple or as complex as you want it to be.
### Prerequisites
-There are none :) Our main solutions stack include but not limited to Kotlin, Java, AngularJS, Flutter, Spring...
+There are none :) Our main solutions stack include but not limited to Kotlin, Java, AngularJS, Flutter, Spring...
Feel free to use any languages and technologies you are comfortable with.
## Mockups
@@ -43,7 +43,17 @@ Send your questions to [tech@chimaera.my](mailto:tech@chimaera.my).
Good luck!
-
-
-
+### Running project
+To run this project we have to setup our development environment.
+
+- Install Java JDK 8, you can download it from [here](https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html)
+- Instruction on JDK 8 installation setup: [setup JDK 8](https://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html)
+- Install Maven 3.6.3, you can download it from [here](https://maven.apache.org/download.cgi)
+- Intruction on Maven setup: [setup Maven](https://maven.apache.org/install.html)
+- After maven has already installed, go to terminal and go to this root project folder
+- And then from inside of this root project run this command: `mvn springboot:run`
+- After this project started, then you can check it using this URL from your browser http://localhost:8080/api/user
+### Testing
+For testing, I provided Postman file at this link:
+https://www.getpostman.com/collections/03aa0e218424844d949a
\ No newline at end of file
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..a16b543
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b0182f9
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,108 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.4.RELEASE
+
+
+ com.example
+ coding-challenge
+ 0.0.1-SNAPSHOT
+ coding-challenge
+ Coding test Azwar Akbar
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.5
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.10
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ io.ebean
+ ebean
+ 11.11.1
+
+
+
+ com.h2database
+ h2
+ runtime
+
+
+
+ org.springframework
+ spring-test
+ test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ io.repaint.maven
+ tiles-maven-plugin
+ 2.10
+ true
+
+
+ org.avaje.tile:java-compile:1.1
+ io.ebean.tile:enhancement:5.3
+
+
+
+
+
+
+
diff --git a/src/main/java/com/example/azwarakbar/TestAzwarakbarApplication.java b/src/main/java/com/example/azwarakbar/TestAzwarakbarApplication.java
new file mode 100644
index 0000000..047df93
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/TestAzwarakbarApplication.java
@@ -0,0 +1,13 @@
+package com.example.azwarakbar;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TestAzwarakbarApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestAzwarakbarApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/example/azwarakbar/components/UserProviderImp.java b/src/main/java/com/example/azwarakbar/components/UserProviderImp.java
new file mode 100644
index 0000000..feb552e
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/components/UserProviderImp.java
@@ -0,0 +1,13 @@
+package com.example.azwarakbar.components;
+
+import io.ebean.config.CurrentUserProvider;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserProviderImp implements CurrentUserProvider {
+
+ @Override
+ public Object currentUser() {
+ return "test";
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/components/factory/EbeanFactoryBean.java b/src/main/java/com/example/azwarakbar/components/factory/EbeanFactoryBean.java
new file mode 100644
index 0000000..f7f78fc
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/components/factory/EbeanFactoryBean.java
@@ -0,0 +1,40 @@
+package com.example.azwarakbar.components.factory;
+
+import com.example.azwarakbar.components.UserProviderImp;
+import io.ebean.EbeanServer;
+import io.ebean.EbeanServerFactory;
+import io.ebean.config.ServerConfig;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class EbeanFactoryBean implements FactoryBean {
+
+ @Autowired
+ UserProviderImp currentUser;
+
+ @Override
+ public EbeanServer getObject() throws Exception {
+ ServerConfig config = new ServerConfig();
+
+ config.setName("db");
+ config.setCurrentUserProvider(currentUser);
+ config.loadFromProperties();
+ config.loadTestProperties();
+ config.setDdlGenerate(true);
+ config.setDdlRun(true);
+
+ return EbeanServerFactory.create(config);
+ }
+
+ @Override
+ public Class> getObjectType() {
+ return EbeanServer.class;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/controller/ProductResourceController.java b/src/main/java/com/example/azwarakbar/controller/ProductResourceController.java
new file mode 100644
index 0000000..db6031f
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/controller/ProductResourceController.java
@@ -0,0 +1,55 @@
+package com.example.azwarakbar.controller;
+
+import com.example.azwarakbar.models.ProductResource;
+import com.example.azwarakbar.models.UserJob;
+import com.example.azwarakbar.response.ProductResourceFormat;
+import com.example.azwarakbar.response.UserFormat;
+import com.example.azwarakbar.services.ProductResourceService;
+import com.example.azwarakbar.services.UserJobService;
+import com.example.azwarakbar.utils.ResponseUtils;
+import com.example.azwarakbar.utils.ZJson;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Controller
+public class ProductResourceController {
+
+ @Autowired
+ private ProductResourceService service;
+
+ @RequestMapping(value = "/api/unknown", produces = "application/json")
+ @ResponseBody
+ public ResponseEntity list() {
+ return ResponseEntity.ok(service.getList(1));
+ }
+
+ @RequestMapping(value = "/api/unknown/{id}", produces = "application/json")
+ @ResponseBody
+ public ResponseEntity getSingle(@PathVariable Long id) {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+ ProductResourceFormat pr = service.getSingle(id);
+
+ if (pr != null) {
+ ObjectNode userNode = ZJson.toObjectNode(pr);
+ objNode.set("data", userNode);
+ return ResponseEntity.ok(objNode);
+ }
+
+ return new ResponseEntity<>(objNode, HttpStatus.NOT_FOUND);
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/controller/UserController.java b/src/main/java/com/example/azwarakbar/controller/UserController.java
new file mode 100644
index 0000000..a414d00
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/controller/UserController.java
@@ -0,0 +1,128 @@
+package com.example.azwarakbar.controller;
+
+import com.example.azwarakbar.models.User;
+import com.example.azwarakbar.request.LoginUser;
+import com.example.azwarakbar.response.UserFormat;
+import com.example.azwarakbar.response.RegistrationSuccess;
+import com.example.azwarakbar.services.UserService;
+import com.example.azwarakbar.utils.ResponseUtils;
+import com.example.azwarakbar.utils.ZJson;
+import com.example.azwarakbar.utils.ZString;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@Controller
+public class UserController {
+
+ @Autowired
+ private UserService service;
+
+ @PostMapping(value = "/api/register", consumes = "application/json")
+ @ResponseBody
+ public ResponseEntity register(@RequestBody User user) {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+ Set> constraintViolations = validator.validate(user);
+ List listError = new ArrayList<>();
+
+ if (constraintViolations.size() > 0 ) {
+ for (ConstraintViolation contraints : constraintViolations) {
+ String strError = contraints.getPropertyPath() + ": " + contraints.getMessage();
+ listError.add(strError);
+ }
+
+ ArrayNode arrayNode = ZJson.toArrayNode(listError);
+
+ return new ResponseEntity<>(ResponseUtils.getError(arrayNode), HttpStatus.BAD_REQUEST);
+ }
+
+ service.save(user);
+ String token = ZString.generateRandomStr();
+ RegistrationSuccess rs = new RegistrationSuccess(user.getId(), token);
+ ObjectNode objectNode = ZJson.toObjectNode(rs);
+
+ return ResponseEntity.ok(objectNode);
+ }
+
+ @PostMapping(value = "/api/login", consumes = "application/json")
+ @ResponseBody
+ public ResponseEntity login(@RequestBody LoginUser user) {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+ Set> constraintViolations = validator.validate(user);
+ List listError = new ArrayList<>();
+
+ if (constraintViolations.size() > 0 ) {
+ for (ConstraintViolation contraints : constraintViolations) {
+ String strError = contraints.getPropertyPath() + ": " + contraints.getMessage();
+ listError.add(strError);
+ }
+
+ ArrayNode arrayNode = ZJson.toArrayNode(listError);
+
+ return new ResponseEntity<>(ResponseUtils.getError(arrayNode), HttpStatus.BAD_REQUEST);
+ }
+
+ boolean isLoginOk = service.check(user);
+
+ if (! isLoginOk) {
+ ObjectNode objNode = ResponseUtils.getError("Wrong email or password");
+ return new ResponseEntity<>(objNode, HttpStatus.BAD_REQUEST);
+ }
+
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+ String token = ZString.generateRandomStr();
+
+ objNode.put("token", token);
+
+ return ResponseEntity.ok(objNode);
+ }
+
+ @RequestMapping(value = "/api/user", produces = "application/json")
+ @ResponseBody
+ public ResponseEntity listUser(@Nullable @RequestParam Integer page, @Nullable @RequestParam Integer delay) {
+ if (page == null) {
+ page = 0;
+ }
+
+ if (delay != null) {
+ try {
+ TimeUnit.SECONDS.sleep(delay);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ return ResponseEntity.ok(service.getListUser(page));
+ }
+
+ @RequestMapping(value = "/api/user/{id}", produces = "application/json")
+ @ResponseBody
+ public ResponseEntity getUser(@PathVariable Long id) {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+ UserFormat user = service.getUser(id);
+
+ if (user != null) {
+ ObjectNode userNode = ZJson.toObjectNode(user);
+ objNode.set("data", userNode);
+ return ResponseEntity.ok(objNode);
+ }
+
+ return new ResponseEntity<>(objNode, HttpStatus.NOT_FOUND);
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/controller/UserJobController.java b/src/main/java/com/example/azwarakbar/controller/UserJobController.java
new file mode 100644
index 0000000..c9d3056
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/controller/UserJobController.java
@@ -0,0 +1,119 @@
+package com.example.azwarakbar.controller;
+
+import com.example.azwarakbar.models.User;
+import com.example.azwarakbar.models.UserJob;
+import com.example.azwarakbar.request.LoginUser;
+import com.example.azwarakbar.response.RegistrationSuccess;
+import com.example.azwarakbar.response.UserFormat;
+import com.example.azwarakbar.services.UserJobService;
+import com.example.azwarakbar.services.UserService;
+import com.example.azwarakbar.utils.ResponseUtils;
+import com.example.azwarakbar.utils.ZJson;
+import com.example.azwarakbar.utils.ZString;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+@Controller
+public class UserJobController {
+
+ @Autowired
+ private UserJobService service;
+
+ @PostMapping(value = "/api/users", consumes = "application/json")
+ @ResponseBody
+ public ResponseEntity create(@RequestBody UserJob userJob) {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+ Set> constraintViolations = validator.validate(userJob);
+ List listError = new ArrayList<>();
+
+ if (constraintViolations.size() > 0 ) {
+ for (ConstraintViolation contraints : constraintViolations) {
+ String strError = contraints.getPropertyPath() + ": " + contraints.getMessage();
+ listError.add(strError);
+ }
+
+ ArrayNode arrayNode = ZJson.toArrayNode(listError);
+
+ return new ResponseEntity<>(ResponseUtils.getError(arrayNode), HttpStatus.BAD_REQUEST);
+ }
+
+ service.save(userJob);
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+
+ objNode.put("name", userJob.getName());
+ objNode.put("job", userJob.getJob());
+ objNode.put("id", userJob.getId());
+ objNode.put("createdAt", userJob.getWhenCreated().toString());
+
+ return ResponseEntity.ok(objNode);
+ }
+
+ @PutMapping(value = "/api/users/{id}", consumes = "application/json")
+ @PatchMapping(value = "/api/users/{id}", consumes = "application/json")
+ @ResponseBody
+ public ResponseEntity update(@RequestBody UserJob userJob, @PathVariable Long id) {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+ Set> constraintViolations = validator.validate(userJob);
+ List listError = new ArrayList<>();
+
+ if (constraintViolations.size() > 0 ) {
+ for (ConstraintViolation contraints : constraintViolations) {
+ String strError = contraints.getPropertyPath() + ": " + contraints.getMessage();
+ listError.add(strError);
+ }
+
+ ArrayNode arrayNode = ZJson.toArrayNode(listError);
+
+ return new ResponseEntity<>(ResponseUtils.getError(arrayNode), HttpStatus.BAD_REQUEST);
+ }
+
+ userJob.setId(id);
+
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+ UserJob uj = service.update(userJob);
+
+ if (uj == null) {
+ return new ResponseEntity<>(objNode, HttpStatus.NOT_FOUND);
+ }
+
+ objNode.put("name", uj.getName());
+ objNode.put("job", uj.getJob());
+ objNode.put("updatedAt", uj.getWhenModified().toString());
+
+ return ResponseEntity.ok(objNode);
+ }
+
+ @DeleteMapping(value = "/api/users/{id}")
+ @ResponseBody
+ public ResponseEntity delete(@PathVariable Long id) {
+ UserJob userJob = service.getUserJob(id);
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+
+ if (userJob == null) {
+ return new ResponseEntity<>(objNode, HttpStatus.NOT_FOUND);
+ }
+
+ userJob.delete();
+
+ return new ResponseEntity<>(objNode, HttpStatus.NO_CONTENT);
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/models/BaseModel.java b/src/main/java/com/example/azwarakbar/models/BaseModel.java
new file mode 100644
index 0000000..ac6cba8
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/models/BaseModel.java
@@ -0,0 +1,83 @@
+package com.example.azwarakbar.models;
+
+
+import io.ebean.Model;
+import io.ebean.annotation.WhenCreated;
+import io.ebean.annotation.WhenModified;
+import io.ebean.annotation.WhoCreated;
+import io.ebean.annotation.WhoModified;
+
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Version;
+import java.sql.Timestamp;
+
+@MappedSuperclass
+public class BaseModel extends Model {
+
+ @Id
+ Long id;
+
+ @Version
+ Long version;
+
+ @WhenCreated
+ Timestamp whenCreated;
+
+ @WhenModified
+ Timestamp whenModified;
+
+ @WhoCreated
+ String whoCreated;
+
+ @WhoModified
+ String whoModified;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getVersion() {
+ return version;
+ }
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public Timestamp getWhenCreated() {
+ return whenCreated;
+ }
+
+ public void setWhenCreated(Timestamp whenCreated) {
+ this.whenCreated = whenCreated;
+ }
+
+ public Timestamp getWhenModified() {
+ return whenModified;
+ }
+
+ public void setWhenModified(Timestamp whenModified) {
+ this.whenModified = whenModified;
+ }
+
+ public String getWhoCreated() {
+ return whoCreated;
+ }
+
+ public void setWhoCreated(String whoCreated) {
+ this.whoCreated = whoCreated;
+ }
+
+ public String getWhoModified() {
+ return whoModified;
+ }
+
+ public void setWhoModified(String whoModified) {
+ this.whoModified = whoModified;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/azwarakbar/models/ProductResource.java b/src/main/java/com/example/azwarakbar/models/ProductResource.java
new file mode 100644
index 0000000..fc0791e
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/models/ProductResource.java
@@ -0,0 +1,67 @@
+package com.example.azwarakbar.models;
+
+import io.ebean.annotation.NotNull;
+import org.hibernate.validator.constraints.Length;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.validation.constraints.NotEmpty;
+
+@Entity
+@Table(name="resource")
+public class ProductResource extends BaseModel {
+
+ @NotNull @NotEmpty(message = "Missing name")
+ String name;
+
+ @NotNull @NotEmpty(message = "Missing year")
+ @Length(min = 4, max = 4)
+ Integer year;
+
+ @NotNull @NotEmpty(message = "Missing color")
+ @Length(min = 4, max = 7)
+ String color;
+
+ @NotNull @NotEmpty(message = "Missing pantone_value")
+ @Length(min = 7, max = 7)
+ String pantoneValue;
+
+ public ProductResource(String name, int year, String color, String pantoneValue) {
+ this.name = name;
+ this.year = year;
+ this.color = color;
+ this.pantoneValue = pantoneValue;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getYear() {
+ return year;
+ }
+
+ public void setYear(Integer year) {
+ this.year = year;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public String getPantoneValue() {
+ return pantoneValue;
+ }
+
+ public void setPantoneValue(String pantoneValue) {
+ this.pantoneValue = pantoneValue;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/models/User.java b/src/main/java/com/example/azwarakbar/models/User.java
new file mode 100644
index 0000000..d8a01f2
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/models/User.java
@@ -0,0 +1,60 @@
+package com.example.azwarakbar.models;
+
+import io.ebean.annotation.NotNull;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.validation.constraints.NotEmpty;
+
+@Entity
+@Table(name="user")
+public class User extends BaseModel {
+
+ @NotNull @NotEmpty(message = "Missing email")
+ String email;
+ String firstName;
+ String lastName;
+ String avatar = "not_set";
+ @NotNull @NotEmpty(message = "Missing password")
+ String password;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/models/UserJob.java b/src/main/java/com/example/azwarakbar/models/UserJob.java
new file mode 100644
index 0000000..bf3d654
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/models/UserJob.java
@@ -0,0 +1,34 @@
+package com.example.azwarakbar.models;
+
+import io.ebean.annotation.NotNull;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.validation.constraints.NotEmpty;
+
+@Entity
+@Table(name="user_job")
+public class UserJob extends BaseModel {
+
+ @NotNull @NotEmpty(message = "Missing name")
+ String name;
+
+ @NotNull @NotEmpty(message = "Missing job")
+ String job;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getJob() {
+ return job;
+ }
+
+ public void setJob(String job) {
+ this.job = job;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/request/LoginUser.java b/src/main/java/com/example/azwarakbar/request/LoginUser.java
new file mode 100644
index 0000000..77bc4e6
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/request/LoginUser.java
@@ -0,0 +1,30 @@
+package com.example.azwarakbar.request;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotEmpty;
+
+public class LoginUser {
+
+ @NotEmpty(message = "Missing email")
+ @Email(message = "Email should be valid")
+ String email;
+
+ @NotEmpty(message = "Missing password")
+ String password;
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/response/ProductResourceFormat.java b/src/main/java/com/example/azwarakbar/response/ProductResourceFormat.java
new file mode 100644
index 0000000..9d24bca
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/response/ProductResourceFormat.java
@@ -0,0 +1,66 @@
+package com.example.azwarakbar.response;
+
+import com.example.azwarakbar.models.BaseModel;
+import io.ebean.annotation.NotNull;
+import org.hibernate.validator.constraints.Length;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.validation.constraints.NotEmpty;
+
+public class ProductResourceFormat {
+
+ Long id;
+ String name;
+ Integer year;
+ String color;
+ String pantoneValue;
+
+ public ProductResourceFormat(Long id, String name, int year, String color, String pantoneValue) {
+ this.id = id;
+ this.name = name;
+ this.year = year;
+ this.color = color;
+ this.pantoneValue = pantoneValue;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getYear() {
+ return year;
+ }
+
+ public void setYear(Integer year) {
+ this.year = year;
+ }
+
+ public String getColor() {
+ return color;
+ }
+
+ public void setColor(String color) {
+ this.color = color;
+ }
+
+ public String getPantoneValue() {
+ return pantoneValue;
+ }
+
+ public void setPantoneValue(String pantoneValue) {
+ this.pantoneValue = pantoneValue;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/response/RegistrationSuccess.java b/src/main/java/com/example/azwarakbar/response/RegistrationSuccess.java
new file mode 100644
index 0000000..681dcbd
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/response/RegistrationSuccess.java
@@ -0,0 +1,27 @@
+package com.example.azwarakbar.response;
+
+public class RegistrationSuccess {
+ Long id;
+ String token;
+
+ public RegistrationSuccess(Long id, String token) {
+ this.id = id;
+ this.token = token;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/response/UserFormat.java b/src/main/java/com/example/azwarakbar/response/UserFormat.java
new file mode 100644
index 0000000..371cf31
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/response/UserFormat.java
@@ -0,0 +1,66 @@
+package com.example.azwarakbar.response;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class UserFormat {
+
+ Long id;
+ String email;
+
+ @JsonAlias("first_name")
+ String firstName;
+
+ @JsonAlias("last_name")
+ String lastName;
+
+ String avatar;
+
+ public UserFormat(Long id, String email, String firstName, String lastName, String avatar) {
+ this.id = id;
+ this.email = email;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.avatar = avatar;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/services/ProductResourceService.java b/src/main/java/com/example/azwarakbar/services/ProductResourceService.java
new file mode 100644
index 0000000..03af898
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/services/ProductResourceService.java
@@ -0,0 +1,112 @@
+package com.example.azwarakbar.services;
+
+import com.example.azwarakbar.components.factory.EbeanFactoryBean;
+import com.example.azwarakbar.models.ProductResource;
+import com.example.azwarakbar.models.User;
+import com.example.azwarakbar.models.UserJob;
+import com.example.azwarakbar.response.ProductResourceFormat;
+import com.example.azwarakbar.response.UserFormat;
+import com.example.azwarakbar.utils.ZJson;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.ebean.EbeanServer;
+import io.ebean.PagedList;
+import io.ebean.annotation.Transactional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class ProductResourceService {
+
+ private static final int PAGING_SIZE = 6;
+
+ @Autowired
+ EbeanServer server;
+
+ @Transactional
+ public ProductResource save(ProductResource data) {
+ server.save(data);
+
+ return data;
+ }
+
+ public ObjectNode getList(int page) {
+ int indexPage = page > 0 ? page - 1 : page;
+ initDataResource();
+ PagedList pageUser = server.find(ProductResource.class)
+ .setFirstRow(PAGING_SIZE * indexPage)
+ .setMaxRows(PAGING_SIZE)
+ .findPagedList();
+
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+ ArrayNode arrayNodeUser = ZJson.toArrayNode(getFormattedList(pageUser.getList()));
+
+ objNode.put("page", page);
+ objNode.put("per_page", PAGING_SIZE);
+ objNode.put("total", pageUser.getTotalCount());
+ objNode.put("total_pages", pageUser.getTotalPageCount());
+ objNode.set("data", arrayNodeUser);
+
+ return objNode;
+ }
+
+ private List getFormattedList(List list) {
+ List listUserFormats = new ArrayList<>();
+
+ for (ProductResource pr : list) {
+ ProductResourceFormat prf = new ProductResourceFormat(pr.getId(), pr.getName(), pr.getYear(),
+ pr.getColor(), pr.getPantoneValue());
+
+ listUserFormats.add(prf);
+ }
+
+ return listUserFormats;
+ }
+
+ private void initDataResource() {
+ if (server.find(ProductResource.class).findCount() != 0) {
+ return;
+ }
+
+ ProductResource pr1 = new ProductResource("cerulean", 2000, "#C74375", "15-4020");
+ ProductResource pr2 = new ProductResource("fuchsia rose", 2001, "#C74375", "17-2031");
+ ProductResource pr3 = new ProductResource("true red", 2002, "#BF1932", "19-1664");
+ ProductResource pr4 = new ProductResource("aqua sky", 2003, "#7BC4C4", "14-4811");
+ ProductResource pr5 = new ProductResource("tigerlily", 2004, "#E2583E", "17-1456");
+ ProductResource pr6 = new ProductResource("blue turquoise", 2005, "#53B0AE", "15-5217");
+ ProductResource pr7 = new ProductResource("cerulean 2", 2000, "#C74375", "15-4020");
+ ProductResource pr8 = new ProductResource("fuchsia rose 2", 2001, "#C74375", "17-2031");
+ ProductResource pr9 = new ProductResource("true red 2", 2002, "#BF1932", "19-1664");
+
+ server.save(pr1);
+ server.save(pr2);
+ server.save(pr3);
+ server.save(pr4);
+ server.save(pr5);
+ server.save(pr6);
+ server.save(pr7);
+ server.save(pr8);
+ server.save(pr9);
+ }
+
+ public ProductResourceFormat getSingle(Long id) {
+ ProductResource pr = server.find(ProductResource.class)
+ .where()
+ .eq("id", id)
+ .findOne();
+
+ if (pr == null) {
+ return null;
+ }
+
+ ProductResourceFormat prf = new ProductResourceFormat(pr.getId(),
+ pr.getName(), pr.getYear(), pr.getColor(), pr.getPantoneValue());
+
+ return prf;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/services/UserJobService.java b/src/main/java/com/example/azwarakbar/services/UserJobService.java
new file mode 100644
index 0000000..1e1ea2e
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/services/UserJobService.java
@@ -0,0 +1,56 @@
+package com.example.azwarakbar.services;
+
+import com.example.azwarakbar.models.User;
+import com.example.azwarakbar.models.UserJob;
+import com.example.azwarakbar.request.LoginUser;
+import com.example.azwarakbar.response.UserFormat;
+import com.example.azwarakbar.utils.ZJson;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.ebean.EbeanServer;
+import io.ebean.PagedList;
+import io.ebean.annotation.Transactional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class UserJobService {
+
+ @Autowired
+ EbeanServer server;
+
+ @Transactional
+ public UserJob save(UserJob userJob) {
+ server.save(userJob);
+
+ return userJob;
+ }
+
+ public UserJob update(UserJob userJob) {
+ UserJob userJob1 = server.find(UserJob.class)
+ .where()
+ .eq("id", userJob.getId())
+ .findOne();
+
+ if (userJob1 != null) {
+ userJob1.setName(userJob.getName());
+ userJob1.setJob(userJob.getJob());
+ userJob1.update();
+
+ return userJob1;
+ }
+
+ return null;
+ }
+
+ public UserJob getUserJob(Long id) {
+ return server.find(UserJob.class)
+ .where()
+ .eq("id", id)
+ .findOne();
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/services/UserService.java b/src/main/java/com/example/azwarakbar/services/UserService.java
new file mode 100644
index 0000000..3beb0e4
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/services/UserService.java
@@ -0,0 +1,94 @@
+package com.example.azwarakbar.services;
+
+import com.example.azwarakbar.models.User;
+import com.example.azwarakbar.request.LoginUser;
+import com.example.azwarakbar.response.UserFormat;
+import com.example.azwarakbar.utils.ZJson;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.ebean.EbeanServer;
+import io.ebean.PagedList;
+import io.ebean.annotation.Transactional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class UserService {
+
+ private static final int PAGING_SIZE = 6;
+
+ @Autowired
+ EbeanServer server;
+
+ @Transactional
+ public User save(User user) {
+ server.save(user);
+
+ return user;
+ }
+
+ public boolean check(LoginUser user) {
+ User usr = server.find(User.class)
+ .where()
+ .eq("email", user.getEmail())
+ .eq("password", user.getPassword()).findOne();
+
+ if (usr != null) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public ObjectNode getListUser(int page) {
+ int indexPage = page > 0 ? page - 1 : page;
+ PagedList pageUser = server.find(User.class)
+ .setFirstRow(PAGING_SIZE * indexPage)
+ .setMaxRows(PAGING_SIZE)
+ .findPagedList();
+
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode objNode = mapper.createObjectNode();
+ ArrayNode arrayNodeUser = ZJson.toArrayNode(getFormattedList(pageUser.getList()));
+
+ objNode.put("page", page);
+ objNode.put("per_page", PAGING_SIZE);
+ objNode.put("total", pageUser.getTotalCount());
+ objNode.put("total_pages", pageUser.getTotalPageCount());
+ objNode.set("data", arrayNodeUser);
+
+ return objNode;
+ }
+
+ private List getFormattedList(List userList) {
+ List listUserFormats = new ArrayList<>();
+
+ for (User user : userList) {
+ UserFormat luf = new UserFormat(user.getId(), user.getEmail(), user.getFirstName(),
+ user.getLastName(), user.getAvatar());
+
+ listUserFormats.add(luf);
+ }
+
+ return listUserFormats;
+ }
+
+ public UserFormat getUser(Long id) {
+ User user = server.find(User.class)
+ .where()
+ .eq("id", id)
+ .findOne();
+
+ if (user == null)
+ return null;
+
+ UserFormat uf = new UserFormat(user.getId(), user.getEmail(),
+ user.getFirstName(), user.getLastName(), user.getAvatar());
+
+ return uf;
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/utils/ResponseUtils.java b/src/main/java/com/example/azwarakbar/utils/ResponseUtils.java
new file mode 100644
index 0000000..e98bcce
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/utils/ResponseUtils.java
@@ -0,0 +1,54 @@
+package com.example.azwarakbar.utils;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.gson.Gson;
+
+public class ResponseUtils {
+
+ public static ObjectNode getSuccess(String data) {
+ return JsonNodeFactory.instance.objectNode()
+ .put("status", "success")
+ .put("data", data);
+ }
+
+ public static ObjectNode getSuccess(ArrayNode arrayNode) {
+ ObjectNode result = JsonNodeFactory.instance.objectNode();
+
+ result.put("status", "success");
+ result.set("data", arrayNode);
+
+ return result;
+ }
+
+ public static ObjectNode getError(String data) {
+ return JsonNodeFactory.instance.objectNode()
+ .put("error", data);
+ }
+
+ public static ObjectNode getError(Object data) {
+ return JsonNodeFactory.instance.objectNode()
+ .put("status", "error")
+ .put("data", new Gson().toJson(data));
+ }
+
+ public static ObjectNode getError(ArrayNode data) {
+ return (ObjectNode) JsonNodeFactory.instance.objectNode()
+ .set("error", data);
+ }
+
+ public static ObjectNode getSuccess(JsonNode node) {
+ return (ObjectNode) JsonNodeFactory.instance.objectNode()
+ .put("status", "success")
+ .set("data", node);
+ }
+
+ public static ObjectNode getSuccessNode(ObjectNode on) {
+ return (ObjectNode) JsonNodeFactory.instance.objectNode()
+ .put("status", "success")
+ .set("data", on);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/azwarakbar/utils/ZJson.java b/src/main/java/com/example/azwarakbar/utils/ZJson.java
new file mode 100644
index 0000000..94a4922
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/utils/ZJson.java
@@ -0,0 +1,42 @@
+package com.example.azwarakbar.utils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+import java.util.List;
+
+public class ZJson {
+ public static ArrayNode toArrayNode(List list) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArrayNode arrayNode = mapper.valueToTree(list);
+
+ return arrayNode;
+ }
+
+ public static String toJsonString(Object object) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ return objectMapper.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static ObjectNode toObjectNode(Object object) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ String strJson = objectMapper.writeValueAsString(object);
+ return (ObjectNode) objectMapper.readTree(strJson);
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/com/example/azwarakbar/utils/ZString.java b/src/main/java/com/example/azwarakbar/utils/ZString.java
new file mode 100644
index 0000000..4ce1386
--- /dev/null
+++ b/src/main/java/com/example/azwarakbar/utils/ZString.java
@@ -0,0 +1,18 @@
+package com.example.azwarakbar.utils;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.nio.charset.Charset;
+import java.util.Random;
+
+public class ZString {
+
+ public static String generateRandomStr() {
+ int length = 24;
+ boolean useLetters = true;
+ boolean useNumbers = false;
+ String generatedString = RandomStringUtils.random(length, useLetters, useNumbers);
+
+ return generatedString;
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/src/main/resources/ebean.mf b/src/main/resources/ebean.mf
new file mode 100644
index 0000000..03a89a5
--- /dev/null
+++ b/src/main/resources/ebean.mf
@@ -0,0 +1,3 @@
+entity-packages: com.example.azwarakbar.models
+transactional-packages: com.example.azwarakbar
+querybean-packages: none
diff --git a/src/main/resources/ebean.properties b/src/main/resources/ebean.properties
new file mode 100644
index 0000000..152870c
--- /dev/null
+++ b/src/main/resources/ebean.properties
@@ -0,0 +1,7 @@
+logging.level.io.ebean.SQL=DEBUG
+logging.level.io.ebean.TXN=DEBUG
+
+datasource.db.username=sa
+datasource.db.password=
+datasource.db.databaseUrl=jdbc:h2:mem:tests
+datasource.db.databaseDriver=org.h2.Driver
\ No newline at end of file
diff --git a/src/test/java/com/example/azwarakbar/AbstractTest.java b/src/test/java/com/example/azwarakbar/AbstractTest.java
new file mode 100644
index 0000000..8b68fb2
--- /dev/null
+++ b/src/test/java/com/example/azwarakbar/AbstractTest.java
@@ -0,0 +1,40 @@
+package com.example.azwarakbar;
+
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = TestAzwarakbarApplication.class)
+@WebMvcTest
+public abstract class AbstractTest {
+ protected MockMvc mvc;
+ @Autowired
+ WebApplicationContext webApplicationContext;
+
+ protected void setUp() {
+ mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
+ }
+ protected String mapToJson(Object obj) throws JsonProcessingException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.writeValueAsString(obj);
+ }
+ protected T mapFromJson(String json, Class clazz)
+ throws JsonParseException, JsonMappingException, IOException {
+
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(json, clazz);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/example/azwarakbar/TestAzwarakbarApplicationTests.java b/src/test/java/com/example/azwarakbar/TestAzwarakbarApplicationTests.java
new file mode 100644
index 0000000..d939134
--- /dev/null
+++ b/src/test/java/com/example/azwarakbar/TestAzwarakbarApplicationTests.java
@@ -0,0 +1,35 @@
+package com.example.azwarakbar;
+
+import com.example.azwarakbar.models.User;
+import org.junit.Before;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@SpringBootTest
+class TestAzwarakbarApplicationTests extends AbstractTest {
+
+ @Override
+ @Before
+ public void setUp() {
+ super.setUp();
+ }
+
+ @Test
+ public void getProductsList() throws Exception {
+ String uri = "/api/user?page=1";
+ MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri)
+ .accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
+
+ int status = mvcResult.getResponse().getStatus();
+ assertEquals(200, status);
+ String content = mvcResult.getResponse().getContentAsString();
+ User[] productlist = super.mapFromJson(content, User[].class);
+ assertTrue(productlist.length > 0);
+ }
+
+}
diff --git a/src/test/java/com/example/azwarakbar/TestUserController.java b/src/test/java/com/example/azwarakbar/TestUserController.java
new file mode 100644
index 0000000..994c312
--- /dev/null
+++ b/src/test/java/com/example/azwarakbar/TestUserController.java
@@ -0,0 +1,45 @@
+package com.example.azwarakbar;
+
+import com.example.azwarakbar.controller.UserController;
+import com.example.azwarakbar.models.User;
+import com.example.azwarakbar.services.UserService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.CoreMatchers.is;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(UserController.class)
+public class TestUserController {
+
+ @Autowired
+ private MockMvc mvc;
+
+ @MockBean
+ private UserService service;
+
+ @Test
+ public void givenUsers_whenGetUsers_thenReturnJsonArray() throws Exception {
+ User user = new User();
+ user.setEmail("saya@example.com");
+ user.setPassword("rahasia");
+
+ service.save(user);
+
+ mvc.perform(get("/api/user", 1)
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.data", hasSize(1)))
+ .andExpect(jsonPath("$.data[0].email", is(user.getEmail())));
+ }
+}