diff --git a/README.md b/README.md
index 6398b5c09..aac84b893 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,105 @@
-### Requirements
+# Architecture
+
+The project's design is based on microservice architecture and communicate via Restful
-- Implement one single API which accepts a todo task and records it
- - There is a maximum **limit of N tasks per user** that can be added **per day**.
- - Different users can have **different** maximum daily limit.
-- Write integration (functional) tests
-- Write unit tests
-- Choose a suitable architecture to make your code simple, organizable, and maintainable
-- Using Docker to run locally
- - Using Docker for database (if used) is mandatory.
-- Write a concise README
- - How to run your code locally?
- - A sample “curl” command to call your API
- - How to run your unit tests locally?
- - What do you love about your solution?
- - What else do you want us to know about however you do not have enough time to complete?
+The system have 6 component:
+- Reverse proxy & Load balancer (`Nginx`)
+- User Service: manage user account and all setting
+- Task Service:
+ + task counter: `rate limiter` is based on `leaky bucket algorithm` with no `weight`
+ + manage task by user
+- User Database: is a `Postgres` instance for User Service
+- Task Database: is a `Postgres` instance for Task Service
+- Distributed Lock: is a `Redis` instance for caching & concurrent lock in distributed system
-### Notes
+# Source structures
+```sh
+└── com
+ └── manabie
+ └──todo
+ ├── config : infrastructure config
+ ├── constant : All constants values that can be used in the services
+ ├── controller : It is the public face of the application layer. It routes incoming requests and returns responses.
+ ├── entity : Object was mapped with database
+ ├── exception: Common exception
+ ├── model: Object was mapped with request/response and business model
+ ├── repository: interact with infrastructure to get resources for service layer
+ ├── service: The domain layer is responsible for encapsulating complex business logic, or simple business logic that is reused by multiple Controller
+```
+# Software usage
+- Spring Boot
+- Spring Reactive Webflux
+>Spring Framework uses Project Reactor as the base implementation of its reactive support, and also comes with a new web framework, Spring WebFlux, which supports the development of reactive, that is, non-blocking, HTTP clients and services.
+- Docker
+>Deploying Our Microservices Using Docker
-- We're using Golang at Manabie. **However**, we encourage you to use the programming language that you are most comfortable with because we want you to **shine** with all your skills and knowledge.
+# Running the microservices
+1. Run `mvn clean package -Dmaven.test.skip` to build the applications.
+2. Run `docker-compose up -d` to create the docker image locally and start the applications.
+# How to use
-### How to submit your solution?
+1. First create new user to test
+- Sample curl
+```bash
+curl --location 'localhost:8080/api/user' --header 'Content-Type: application/json' --data '{"username":"trungnguyen.tech","maxTaskPerDay":3}'
+```
-- Fork this repo and show us your development progress via a PR
+Which will respond with something like:
-### Interesting facts about Manabie
+```json
+{
+ "status": {
+ "message": null,
+ "code": 200,
+ "success": false
+ },
+ "data": {
+ "id": 7,
+ "username": "trungnguyen.tech1",
+ "maxTaskPerDay": 3,
+ "createdAt": "2023-04-22T16:05:55.162259378",
+ "createdBy": null,
+ "updatedAt": null,
+ "updatedBy": null
+ }
+}
+```
+2. Call the create task with owner id (user was created in step 1)
+> The endpoint http://localhost:8080/api/user used as gateway to `hide` the services behind the outside.
-- Monthly there are about 2 million lines of code changes (inserted/updated/deleted) committed into our GitHub repositories. To avoid **regression bugs**, we write different kinds of **automated tests** (unit/integration (functionality)/end2end) as parts of the definition of done of our assigned tasks.
-- We nurture the cultural values: **knowledge sharing** and **good communication**, therefore good written documents and readable, organizable, and maintainable code are in our blood when we build any features to grow our products.
-- We have **collaborative** culture at Manabie. Feel free to ask trieu@manabie.com any questions. We are very happy to answer all of them.
+- Sample curl
+```bash
+curl --location 'http://localhost:8080/api/task' --header 'Content-Type: application/json' --data '{"title":"join daily meeting","description":"join daily meeting","owner":3}'
+```
-Thank you for spending time to read and attempt our take-home assessment. We are looking forward to your submission.
+- Sample Response
+```json
+{
+ "status": {
+ "message": null,
+ "code": 200,
+ "success": false
+ },
+ "data": {
+ "id": 1,
+ "title": "join daily meeting",
+ "description": "join daily meeting",
+ "owner": 7,
+ "status": "NEW",
+ "createdAt": "2023-04-22T16:01:59.052947339",
+ "createdBy": null,
+ "updatedAt": null,
+ "updatedBy": null
+ }
+}
+```
+
+## TODO
+- Apply Authentication & Authorization service to protect system
+- Apply *Kubernetes & Istio* as alternative deployment
+- Apply Grafana, Prometheus and Kiali ... for tracing and monitoring to easilier debug and scale.
+- Apply Kafka for internal call to get highly performant and scalable.
+- Convert from Spring boot to Quarkus to enhance the performace
+ (CPU & Memory usage).
+- Do more testing & integration test on user-service ,task-service.
+- Store user info in Redis cached for better perfomance and reduce cost.
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 000000000..5dfeda7b6
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,78 @@
+version: "3.7"
+services:
+ redis:
+ image: redis:latest
+ restart: always
+ ports:
+ - '6379:6379'
+
+ ##PostgresDB for user-service
+ postgres-user:
+ container_name: postgres-user
+ image: postgres:latest
+ environment:
+ POSTGRES_DB: user
+ POSTGRES_USER: manabie
+ POSTGRES_PASSWORD: manabie@123
+ PGDATA: /data/postgres
+ volumes:
+ - ./docker/postgres/user:/data/postgres
+ expose:
+ - "5431"
+ ports:
+ - "5431:5431"
+ command: -p 5431
+ restart: always
+
+ ##PostgresDB for task-service
+ postgres-task:
+ container_name: postgres-task
+ image: postgres:latest
+ environment:
+ POSTGRES_DB: task
+ POSTGRES_USER: manabie
+ POSTGRES_PASSWORD: manabie@123
+ PGDATA: /data/postgres
+ volumes:
+ - ./docker/postgres/task:/data/postgres
+ expose:
+ - "5431"
+ ports:
+ - "5432:5431"
+ restart: always
+
+ postgres-admin:
+ image: dpage/pgadmin4:latest
+ environment:
+ PGADMIN_DEFAULT_EMAIL: admin@gmail.com
+ PGADMIN_DEFAULT_PASSWORD: admin
+ ports:
+ - '5433:80'
+
+ load-balancer:
+ platform: linux/arm64/v8
+ build: ./docker/nginx
+ container_name: load-balancer
+ image: manabie-challenge/load-balancer:latest
+ ports:
+ - '8080:8080'
+
+ ## User-Service Docker Compose Config
+ user-service:
+ platform: linux/arm64/v8
+ build: ./user-service
+ container_name: user-service
+ image: manabie-challenge/user-service:latest
+ environment:
+ - SPRING_PROFILES_ACTIVE=docker
+
+ ## User-Service Docker Compose Config
+ task-service:
+ platform: linux/arm64/v8
+ build: ./task-service
+ container_name: task-service
+ image: manabie-challenge/task-service:latest
+ environment:
+ - SPRING_PROFILES_ACTIVE=docker
+networks:
+ common:
\ No newline at end of file
diff --git a/docs/images/todo_context_diagram.png b/docs/images/todo_context_diagram.png
new file mode 100644
index 000000000..acffba35c
Binary files /dev/null and b/docs/images/todo_context_diagram.png differ
diff --git a/mvnw b/mvnw
new file mode 100755
index 000000000..8a8fb2282
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`\\unset -f command; \\command -v java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 000000000..1d8ab018e
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..a1c26a628
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,56 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.0.6
+
+
+ com.manabie.challenge
+ todo
+ 0.0.1-SNAPSHOT
+ todo
+ manabie challenge
+ pom
+
+
+ 17
+ 2022.0.1
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+ pom
+ import
+
+
+
+
+
+ user-service
+ task-service
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
diff --git a/task-service/Dockerfile b/task-service/Dockerfile
new file mode 100644
index 000000000..fea31b964
--- /dev/null
+++ b/task-service/Dockerfile
@@ -0,0 +1,5 @@
+FROM openjdk:17-slim-buster
+
+COPY target/*.jar app.jar
+
+ENTRYPOINT ["java","-jar","/app.jar"]
diff --git a/task-service/HELP.md b/task-service/HELP.md
new file mode 100644
index 000000000..42dc22d80
--- /dev/null
+++ b/task-service/HELP.md
@@ -0,0 +1,25 @@
+# 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/3.0.5/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.0.5/maven-plugin/reference/html/#build-image)
+* [Spring Web](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#web)
+* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#data.sql.jpa-and-spring-data)
+* [Liquibase Migration](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#howto.data-initialization.migration-tool.liquibase)
+* [Spring Data Redis (Access+Driver)](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#data.nosql.redis)
+* [Spring for Apache Kafka](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#messaging.kafka)
+* [Validation](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#io.validation)
+
+### Guides
+The following guides illustrate how to use some features concretely:
+
+* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
+* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
+* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
+* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
+* [Messaging with Redis](https://spring.io/guides/gs/messaging-redis/)
+* [Validation](https://spring.io/guides/gs/validating-form-input/)
+
diff --git a/task-service/mvnw b/task-service/mvnw
new file mode 100755
index 000000000..8a8fb2282
--- /dev/null
+++ b/task-service/mvnw
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`\\unset -f command; \\command -v java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/task-service/mvnw.cmd b/task-service/mvnw.cmd
new file mode 100644
index 000000000..1d8ab018e
--- /dev/null
+++ b/task-service/mvnw.cmd
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/task-service/pom.xml b/task-service/pom.xml
new file mode 100644
index 000000000..ed7853d31
--- /dev/null
+++ b/task-service/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+
+ com.manabie.challenge
+ todo
+ 0.0.1-SNAPSHOT
+
+ com.manabie.challenge
+ task-service
+ 0.0.1-SNAPSHOT
+ task-service
+ task service
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ org.springframework.integration
+ spring-integration-redis
+
+
+ org.springframework.data
+ spring-data-redis
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.modelmapper
+ modelmapper
+ 3.1.1
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/task-service/src/main/java/com/manabie/todo/TaskApplication.java b/task-service/src/main/java/com/manabie/todo/TaskApplication.java
new file mode 100644
index 000000000..0ee4fbca8
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/TaskApplication.java
@@ -0,0 +1,13 @@
+package com.manabie.todo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TaskApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TaskApplication.class, args);
+ }
+
+}
diff --git a/task-service/src/main/java/com/manabie/todo/config/BeanConfig.java b/task-service/src/main/java/com/manabie/todo/config/BeanConfig.java
new file mode 100644
index 000000000..fe680feff
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/config/BeanConfig.java
@@ -0,0 +1,15 @@
+package com.manabie.todo.config;
+
+import org.modelmapper.ModelMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class BeanConfig {
+ @Bean
+ ModelMapper modelMapper() {
+ ModelMapper modelMapper = new ModelMapper();
+ modelMapper.getConfiguration().setSkipNullEnabled(true);
+ return modelMapper;
+ }
+}
diff --git a/task-service/src/main/java/com/manabie/todo/config/RedisConfig.java b/task-service/src/main/java/com/manabie/todo/config/RedisConfig.java
new file mode 100644
index 000000000..a1b727ece
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/config/RedisConfig.java
@@ -0,0 +1,15 @@
+package com.manabie.todo.config;
+
+import com.manabie.todo.constant.CacheKey;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+
+@Configuration
+public class RedisConfig {
+ @Bean(destroyMethod = "destroy")
+ public RedisLockRegistry userLockRegistry(RedisConnectionFactory redisConnectionFactory) {
+ return new RedisLockRegistry(redisConnectionFactory, CacheKey.USER_LOCK);
+ }
+}
diff --git a/task-service/src/main/java/com/manabie/todo/config/WebClientConfig.java b/task-service/src/main/java/com/manabie/todo/config/WebClientConfig.java
new file mode 100644
index 000000000..e3fa5cf65
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/config/WebClientConfig.java
@@ -0,0 +1,14 @@
+package com.manabie.todo.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+public class WebClientConfig {
+
+ @Bean
+ public WebClient.Builder webClientBuilder() {
+ return WebClient.builder();
+ }
+}
diff --git a/task-service/src/main/java/com/manabie/todo/constant/CacheKey.java b/task-service/src/main/java/com/manabie/todo/constant/CacheKey.java
new file mode 100644
index 000000000..b027fd845
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/constant/CacheKey.java
@@ -0,0 +1,8 @@
+package com.manabie.todo.constant;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class CacheKey {
+ public final String USER_LOCK = "lock:user";
+}
diff --git a/task-service/src/main/java/com/manabie/todo/constant/TaskStatus.java b/task-service/src/main/java/com/manabie/todo/constant/TaskStatus.java
new file mode 100644
index 000000000..b29b27a6a
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/constant/TaskStatus.java
@@ -0,0 +1,7 @@
+package com.manabie.todo.constant;
+
+public enum TaskStatus {
+ NEW,
+ PROCESSING,
+ COMPLETED
+}
diff --git a/task-service/src/main/java/com/manabie/todo/controller/TaskController.java b/task-service/src/main/java/com/manabie/todo/controller/TaskController.java
new file mode 100644
index 000000000..5aefa35a3
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/controller/TaskController.java
@@ -0,0 +1,37 @@
+package com.manabie.todo.controller;
+
+import com.manabie.todo.model.BaseResponse;
+import com.manabie.todo.model.CreateTaskRequest;
+import com.manabie.todo.model.TaskModel;
+import com.manabie.todo.service.TaskService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/task")
+@RequiredArgsConstructor
+public class TaskController {
+ final TaskService taskService;
+
+ @PostMapping()
+ BaseResponse createTask(@RequestBody CreateTaskRequest request) {
+ return BaseResponse.ofSucceeded(taskService.createTask(request));
+ }
+
+ @GetMapping
+ BaseResponse> findAllTask() {
+ return BaseResponse.ofSucceeded(taskService.findAllTask());
+ }
+
+ @GetMapping("/owner/{userId}")
+ BaseResponse> findAllTaskByOwner(@PathVariable Long userId) {
+ return BaseResponse.ofSucceeded(taskService.findAllByOwner(userId));
+ }
+
+ @GetMapping("/{id}")
+ TaskModel getTaskById(@PathVariable Long id) {
+ return BaseResponse.ofSucceeded(taskService.getTaskById(id)).getData();
+ }
+}
\ No newline at end of file
diff --git a/task-service/src/main/java/com/manabie/todo/entity/TaskCounterEntity.java b/task-service/src/main/java/com/manabie/todo/entity/TaskCounterEntity.java
new file mode 100644
index 000000000..df40238e4
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/entity/TaskCounterEntity.java
@@ -0,0 +1,23 @@
+package com.manabie.todo.entity;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "task_counter")
+@Getter
+@Setter
+public class TaskCounterEntity {
+ @Id
+ private Long userId;
+ private Long counter;
+ private LocalDateTime createdAt;
+ private String createdBy;
+ private LocalDateTime updatedAt;
+ private String updatedBy;
+}
\ No newline at end of file
diff --git a/task-service/src/main/java/com/manabie/todo/entity/TaskEntity.java b/task-service/src/main/java/com/manabie/todo/entity/TaskEntity.java
new file mode 100644
index 000000000..ec8d0d53f
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/entity/TaskEntity.java
@@ -0,0 +1,27 @@
+package com.manabie.todo.entity;
+
+import com.manabie.todo.constant.TaskStatus;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "task")
+@Getter
+@Setter
+public class TaskEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "task_id_seq")
+ @SequenceGenerator(name = "task_id_seq", sequenceName = "task_id_seq", allocationSize = 1)
+ private Long id;
+ private String title;
+ private String description;
+ private Long owner;
+ private TaskStatus status;
+ private LocalDateTime createdAt;
+ private String createdBy;
+ private LocalDateTime updatedAt;
+ private String updatedBy;
+}
diff --git a/task-service/src/main/java/com/manabie/todo/exception/ManabieException.java b/task-service/src/main/java/com/manabie/todo/exception/ManabieException.java
new file mode 100644
index 000000000..3b9e477b0
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/exception/ManabieException.java
@@ -0,0 +1,15 @@
+package com.manabie.todo.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import org.springframework.http.HttpStatus;
+
+@Builder
+@Data
+@AllArgsConstructor
+public class ManabieException extends RuntimeException {
+ private int code;
+ private String message;
+ private HttpStatus httpStatus;
+}
diff --git a/task-service/src/main/java/com/manabie/todo/exception/ManabieExceptionHandler.java b/task-service/src/main/java/com/manabie/todo/exception/ManabieExceptionHandler.java
new file mode 100644
index 000000000..d1de81706
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/exception/ManabieExceptionHandler.java
@@ -0,0 +1,24 @@
+package com.manabie.todo.exception;
+
+import com.manabie.todo.model.BaseResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class ManabieExceptionHandler {
+ @ExceptionHandler({ManabieException.class})
+ public ResponseEntity vuiException(ManabieException ex) {
+ BaseResponse body = BaseResponse.ofFailed(ex.getCode(), ex.getMessage());
+ return ResponseEntity.status(ex.getHttpStatus()).body(body);
+ }
+
+ @ExceptionHandler({Exception.class})
+ @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+ public ResponseEntity exception(Exception ex) {
+ BaseResponse body = BaseResponse.ofFailed(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
+ }
+}
diff --git a/task-service/src/main/java/com/manabie/todo/model/BaseResponse.java b/task-service/src/main/java/com/manabie/todo/model/BaseResponse.java
new file mode 100644
index 000000000..d4526f4df
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/model/BaseResponse.java
@@ -0,0 +1,52 @@
+package com.manabie.todo.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import org.springframework.http.HttpStatus;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+
+@Data
+public class BaseResponse {
+ private Status status;
+ private T data;
+
+ public static BaseResponse ofSucceeded(T data) {
+ BaseResponse response = new BaseResponse<>();
+ response.data = data;
+
+ Status responseStatus=new Status();
+ responseStatus.setCode(HttpStatus.OK.value());
+ response.status=responseStatus;
+ return response;
+ }
+
+ public static BaseResponse ofSucceeded() {
+ BaseResponse response = new BaseResponse<>();
+ Status responseStatus=new Status();
+ responseStatus.setCode(HttpStatus.OK.value());
+ response.status=responseStatus;
+ return response;
+ }
+
+ public static BaseResponse ofFailed(Integer errorCode) {
+ return ofFailed(errorCode, null);
+ }
+
+ public static BaseResponse ofFailed(Integer errorCode, String message) {
+ BaseResponse response = new BaseResponse<>();
+
+ Status responseStatus=new Status();
+ responseStatus.setCode(errorCode);
+ responseStatus.setMessage(message);
+ response.status=responseStatus;
+ return response;
+ }
+
+ @Data
+ public static class Status {
+ private String message;
+ Integer code;
+ boolean success;
+ }
+}
diff --git a/task-service/src/main/java/com/manabie/todo/model/CreateTaskRequest.java b/task-service/src/main/java/com/manabie/todo/model/CreateTaskRequest.java
new file mode 100644
index 000000000..7d9907b77
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/model/CreateTaskRequest.java
@@ -0,0 +1,17 @@
+package com.manabie.todo.model;
+
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CreateTaskRequest {
+ @NotBlank(message = "title is mandatory")
+ private String title;
+
+ private String description;
+
+ @NotBlank(message = "owner is mandatory")
+ private Long owner;
+}
diff --git a/task-service/src/main/java/com/manabie/todo/model/TaskModel.java b/task-service/src/main/java/com/manabie/todo/model/TaskModel.java
new file mode 100644
index 000000000..3c6547242
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/model/TaskModel.java
@@ -0,0 +1,21 @@
+package com.manabie.todo.model;
+
+import com.manabie.todo.constant.TaskStatus;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class TaskModel {
+ private Long id;
+ private String title;
+ private String description;
+ private Long owner;
+ private TaskStatus status;
+ private LocalDateTime createdAt;
+ private String createdBy;
+ private LocalDateTime updatedAt;
+ private String updatedBy;
+}
diff --git a/task-service/src/main/java/com/manabie/todo/model/UserInfo.java b/task-service/src/main/java/com/manabie/todo/model/UserInfo.java
new file mode 100644
index 000000000..2b50882f2
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/model/UserInfo.java
@@ -0,0 +1,18 @@
+package com.manabie.todo.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class UserInfo {
+ private Long id;
+ private String username;
+ private Integer maxTaskPerDay;
+ private LocalDateTime createdAt;
+ private String createdBy;
+ private LocalDateTime updatedAt;
+ private String updatedBy;
+}
diff --git a/task-service/src/main/java/com/manabie/todo/repository/TaskCounterRepository.java b/task-service/src/main/java/com/manabie/todo/repository/TaskCounterRepository.java
new file mode 100644
index 000000000..77edc56fe
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/repository/TaskCounterRepository.java
@@ -0,0 +1,10 @@
+package com.manabie.todo.repository;
+
+import com.manabie.todo.entity.TaskCounterEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface TaskCounterRepository extends JpaRepository {
+ TaskCounterEntity findByUserId(Long userId);
+}
diff --git a/task-service/src/main/java/com/manabie/todo/repository/TaskRepository.java b/task-service/src/main/java/com/manabie/todo/repository/TaskRepository.java
new file mode 100644
index 000000000..cef231ec8
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/repository/TaskRepository.java
@@ -0,0 +1,12 @@
+package com.manabie.todo.repository;
+
+import com.manabie.todo.entity.TaskEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface TaskRepository extends JpaRepository {
+ List findAllByOwner(Long userId);
+}
diff --git a/task-service/src/main/java/com/manabie/todo/service/TaskLimitService.java b/task-service/src/main/java/com/manabie/todo/service/TaskLimitService.java
new file mode 100644
index 000000000..2f5cd9cd3
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/service/TaskLimitService.java
@@ -0,0 +1,5 @@
+package com.manabie.todo.service;
+
+public interface TaskLimitService {
+ boolean checkLimitAndIncreaseCounter(Long userId, Integer maxTaxPerDay);
+}
diff --git a/task-service/src/main/java/com/manabie/todo/service/TaskService.java b/task-service/src/main/java/com/manabie/todo/service/TaskService.java
new file mode 100644
index 000000000..d5d50fa9c
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/service/TaskService.java
@@ -0,0 +1,20 @@
+package com.manabie.todo.service;
+
+import com.manabie.todo.model.CreateTaskRequest;
+import com.manabie.todo.model.TaskModel;
+import com.manabie.todo.model.UserInfo;
+
+import java.util.List;
+
+public interface TaskService {
+ TaskModel createTask(CreateTaskRequest request);
+
+ List findAllTask();
+
+ List findAllByOwner(final Long userId);
+
+ TaskModel getTaskById(Long id);
+
+ UserInfo getUserInfo(Long userId);
+
+}
diff --git a/task-service/src/main/java/com/manabie/todo/service/impl/TaskLimitServiceImpl.java b/task-service/src/main/java/com/manabie/todo/service/impl/TaskLimitServiceImpl.java
new file mode 100644
index 000000000..8e186fc10
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/service/impl/TaskLimitServiceImpl.java
@@ -0,0 +1,32 @@
+package com.manabie.todo.service.impl;
+
+import com.manabie.todo.entity.TaskCounterEntity;
+import com.manabie.todo.repository.TaskCounterRepository;
+import com.manabie.todo.service.TaskLimitService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Service
+@RequiredArgsConstructor
+public class TaskLimitServiceImpl implements TaskLimitService {
+ final TaskCounterRepository taskCounterRepository;
+
+ @Override
+ public boolean checkLimitAndIncreaseCounter(Long userId, Integer maxTaxPerDay) {
+ TaskCounterEntity taskCounter = taskCounterRepository.findByUserId(userId);
+ if (null == taskCounter) {
+ taskCounter = new TaskCounterEntity();
+ taskCounter.setUserId(userId);
+ taskCounter.setCounter(0L);
+ taskCounter.setCreatedAt(LocalDateTime.now());
+ }
+ if (taskCounter.getCounter() < maxTaxPerDay) {
+ taskCounter.setCounter(taskCounter.getCounter() + 1);
+ taskCounterRepository.save(taskCounter);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/task-service/src/main/java/com/manabie/todo/service/impl/TaskServiceImpl.java b/task-service/src/main/java/com/manabie/todo/service/impl/TaskServiceImpl.java
new file mode 100644
index 000000000..13dd4c065
--- /dev/null
+++ b/task-service/src/main/java/com/manabie/todo/service/impl/TaskServiceImpl.java
@@ -0,0 +1,87 @@
+package com.manabie.todo.service.impl;
+
+import com.manabie.todo.constant.TaskStatus;
+import com.manabie.todo.entity.TaskEntity;
+import com.manabie.todo.exception.ManabieException;
+import com.manabie.todo.model.CreateTaskRequest;
+import com.manabie.todo.model.TaskModel;
+import com.manabie.todo.model.UserInfo;
+import com.manabie.todo.repository.TaskRepository;
+import com.manabie.todo.service.TaskLimitService;
+import com.manabie.todo.service.TaskService;
+import lombok.RequiredArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.http.HttpStatus;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+@Service
+@RequiredArgsConstructor
+public class TaskServiceImpl implements TaskService {
+ final ModelMapper modelMapper;
+ final TaskRepository taskRepository;
+ final TaskLimitService taskLimitService;
+ final RedisLockRegistry userLockRegistry;
+ final WebClient.Builder webClientBuilder;
+
+ @Override
+ public TaskModel createTask(CreateTaskRequest request) {
+ UserInfo userInfo = getUserInfo(request.getOwner());
+ if (null == userInfo) {
+ throw new ManabieException(HttpStatus.NOT_FOUND.ordinal(), "User not found", HttpStatus.NOT_FOUND);
+ }
+
+ TaskModel taskModel = null;
+ Lock lock = userLockRegistry.obtain(request.getOwner() + "");
+ boolean isLock = false;
+ try {
+ isLock = lock.tryLock(100, TimeUnit.MILLISECONDS);
+ if (isLock) {
+ if (!taskLimitService.checkLimitAndIncreaseCounter(request.getOwner(),userInfo.getMaxTaskPerDay())) {
+ TaskEntity taskEntity = modelMapper.map(request, TaskEntity.class);
+ taskEntity.setStatus(TaskStatus.NEW);
+ taskEntity.setCreatedAt(LocalDateTime.now());
+
+ taskModel = modelMapper.map(taskRepository.save(taskEntity), TaskModel.class);
+ }
+ lock.unlock();
+ }
+ } catch (Exception e) {
+ if (isLock) {
+ lock.unlock();
+ }
+ }
+ return taskModel;
+ }
+
+ @Override
+ public List findAllTask() {
+ return taskRepository.findAll().stream().map(t -> modelMapper.map(t, TaskModel.class)).toList();
+ }
+
+ @Override
+ public List findAllByOwner(final Long userId) {
+ return taskRepository.findAllByOwner(userId).stream().map(t -> modelMapper.map(t, TaskModel.class)).toList();
+ }
+
+ @Override
+ public TaskModel getTaskById(Long id) {
+ return modelMapper.map(taskRepository.getReferenceById(id), TaskModel.class);
+ }
+
+ @Override
+ public UserInfo getUserInfo(Long userId) {
+ return webClientBuilder.build().get()
+ .uri("http://user-service:8080",
+ uriBuilder -> uriBuilder.pathSegment("user",userId + "").build())
+ .retrieve()
+ .bodyToMono(UserInfo.class)
+ .block();
+ }
+}
diff --git a/task-service/src/main/resources/application-docker.properties b/task-service/src/main/resources/application-docker.properties
new file mode 100644
index 000000000..1d9b8f772
--- /dev/null
+++ b/task-service/src/main/resources/application-docker.properties
@@ -0,0 +1,17 @@
+spring.application.name=task-service
+server.port=8080
+
+spring.data.redis.host=redis
+spring.data.redis.port=6379
+
+spring.datasource.hikari.connectionTimeout=20000
+spring.datasource.hikari.maximumPoolSize=5
+
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.datasource.url=jdbc:postgresql://postgres-task:5432/task
+spring.datasource.username=manabie
+spring.datasource.password=manabie@123
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.generate-ddl=true
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
\ No newline at end of file
diff --git a/task-service/src/main/resources/application.properties b/task-service/src/main/resources/application.properties
new file mode 100644
index 000000000..c9493c485
--- /dev/null
+++ b/task-service/src/main/resources/application.properties
@@ -0,0 +1,17 @@
+spring.application.name=task-service
+server.port=8082
+
+spring.data.redis.host=localhost
+spring.data.redis.port=6379
+
+spring.datasource.hikari.connectionTimeout=20000
+spring.datasource.hikari.maximumPoolSize=5
+
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.datasource.url=jdbc:postgresql://localhost:5432/task
+spring.datasource.username=manabie
+spring.datasource.password=manabie@123
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.generate-ddl=true
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
\ No newline at end of file
diff --git a/task-service/src/test/java/com/manabie/todo/controller/TaskServiceTest.java b/task-service/src/test/java/com/manabie/todo/controller/TaskServiceTest.java
new file mode 100644
index 000000000..d009d2b4a
--- /dev/null
+++ b/task-service/src/test/java/com/manabie/todo/controller/TaskServiceTest.java
@@ -0,0 +1,67 @@
+package com.manabie.todo.controller;
+
+import com.manabie.todo.model.CreateTaskRequest;
+import com.manabie.todo.model.TaskModel;
+import com.manabie.todo.model.UserInfo;
+import com.manabie.todo.repository.TaskRepository;
+import com.manabie.todo.service.TaskLimitService;
+import com.manabie.todo.service.TaskService;
+import com.manabie.todo.service.impl.TaskServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+
+import org.mockito.MockitoAnnotations;
+import org.modelmapper.ModelMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+@Slf4j
+public class TaskServiceTest {
+
+ @Mock
+ private ModelMapper modelMapper;
+ @Mock
+ private TaskRepository taskRepository;
+ @Mock
+ private TaskLimitService taskLimitService;
+ @Autowired
+ private RedisLockRegistry userLockRegistry;
+ @Mock
+ private WebClient.Builder webClientBuilder;
+ @Mock
+ private TaskService taskService;
+
+
+ @BeforeEach
+ public void setUp() {
+ MockitoAnnotations.openMocks(this);
+ taskService = new TaskServiceImpl(modelMapper,taskRepository,taskLimitService,userLockRegistry,webClientBuilder);
+ }
+
+
+ @Test
+ public void createTask_returnSuccess() throws Exception {
+ CreateTaskRequest request=new CreateTaskRequest();
+ request.setTitle("Task 1");
+ request.setDescription("This is Task 1");
+ request.setOwner(1000L);
+
+ UserInfo fakedUserInfo=new UserInfo();
+ fakedUserInfo.setId(1000L);
+ fakedUserInfo.setUsername("trungnguyen.tech");
+ fakedUserInfo.setId(1000L);
+ fakedUserInfo.setMaxTaskPerDay(3);
+
+ when(taskService.getUserInfo(any())).thenReturn(fakedUserInfo);
+
+ TaskModel taskModel = taskService.createTask(request);
+ assertNotNull(taskModel);
+ }
+}
diff --git a/user-service/Dockerfile b/user-service/Dockerfile
new file mode 100644
index 000000000..fea31b964
--- /dev/null
+++ b/user-service/Dockerfile
@@ -0,0 +1,5 @@
+FROM openjdk:17-slim-buster
+
+COPY target/*.jar app.jar
+
+ENTRYPOINT ["java","-jar","/app.jar"]
diff --git a/user-service/HELP.md b/user-service/HELP.md
new file mode 100644
index 000000000..42dc22d80
--- /dev/null
+++ b/user-service/HELP.md
@@ -0,0 +1,25 @@
+# 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/3.0.5/maven-plugin/reference/html/)
+* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.0.5/maven-plugin/reference/html/#build-image)
+* [Spring Web](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#web)
+* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#data.sql.jpa-and-spring-data)
+* [Liquibase Migration](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#howto.data-initialization.migration-tool.liquibase)
+* [Spring Data Redis (Access+Driver)](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#data.nosql.redis)
+* [Spring for Apache Kafka](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#messaging.kafka)
+* [Validation](https://docs.spring.io/spring-boot/docs/3.0.5/reference/htmlsingle/#io.validation)
+
+### Guides
+The following guides illustrate how to use some features concretely:
+
+* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
+* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
+* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
+* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
+* [Messaging with Redis](https://spring.io/guides/gs/messaging-redis/)
+* [Validation](https://spring.io/guides/gs/validating-form-input/)
+
diff --git a/user-service/mvnw b/user-service/mvnw
new file mode 100755
index 000000000..8a8fb2282
--- /dev/null
+++ b/user-service/mvnw
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`\\unset -f command; \\command -v java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/user-service/mvnw.cmd b/user-service/mvnw.cmd
new file mode 100644
index 000000000..1d8ab018e
--- /dev/null
+++ b/user-service/mvnw.cmd
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/user-service/pom.xml b/user-service/pom.xml
new file mode 100644
index 000000000..b5f7021f2
--- /dev/null
+++ b/user-service/pom.xml
@@ -0,0 +1,93 @@
+
+
+ 4.0.0
+
+ com.manabie.challenge
+ todo
+ 0.0.1-SNAPSHOT
+
+ com.manabie.challenge
+ user-service
+ 0.0.1-SNAPSHOT
+ user-service
+ user service
+
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ org.springframework.integration
+ spring-integration-redis
+
+
+ org.springframework.data
+ spring-data-redis
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.modelmapper
+ modelmapper
+ 3.1.1
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/user-service/src/main/java/com/manabie/todo/UserApplication.java b/user-service/src/main/java/com/manabie/todo/UserApplication.java
new file mode 100644
index 000000000..570d88b23
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/UserApplication.java
@@ -0,0 +1,13 @@
+package com.manabie.todo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class UserApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(UserApplication.class, args);
+ }
+
+}
diff --git a/user-service/src/main/java/com/manabie/todo/config/BeanConfig.java b/user-service/src/main/java/com/manabie/todo/config/BeanConfig.java
new file mode 100644
index 000000000..fe680feff
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/config/BeanConfig.java
@@ -0,0 +1,15 @@
+package com.manabie.todo.config;
+
+import org.modelmapper.ModelMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class BeanConfig {
+ @Bean
+ ModelMapper modelMapper() {
+ ModelMapper modelMapper = new ModelMapper();
+ modelMapper.getConfiguration().setSkipNullEnabled(true);
+ return modelMapper;
+ }
+}
diff --git a/user-service/src/main/java/com/manabie/todo/config/RedisConfig.java b/user-service/src/main/java/com/manabie/todo/config/RedisConfig.java
new file mode 100644
index 000000000..a1b727ece
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/config/RedisConfig.java
@@ -0,0 +1,15 @@
+package com.manabie.todo.config;
+
+import com.manabie.todo.constant.CacheKey;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.integration.redis.util.RedisLockRegistry;
+
+@Configuration
+public class RedisConfig {
+ @Bean(destroyMethod = "destroy")
+ public RedisLockRegistry userLockRegistry(RedisConnectionFactory redisConnectionFactory) {
+ return new RedisLockRegistry(redisConnectionFactory, CacheKey.USER_LOCK);
+ }
+}
diff --git a/user-service/src/main/java/com/manabie/todo/config/WebClientConfig.java b/user-service/src/main/java/com/manabie/todo/config/WebClientConfig.java
new file mode 100644
index 000000000..e3fa5cf65
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/config/WebClientConfig.java
@@ -0,0 +1,14 @@
+package com.manabie.todo.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+public class WebClientConfig {
+
+ @Bean
+ public WebClient.Builder webClientBuilder() {
+ return WebClient.builder();
+ }
+}
diff --git a/user-service/src/main/java/com/manabie/todo/constant/CacheKey.java b/user-service/src/main/java/com/manabie/todo/constant/CacheKey.java
new file mode 100644
index 000000000..41568a30c
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/constant/CacheKey.java
@@ -0,0 +1,8 @@
+package com.manabie.todo.constant;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class CacheKey {
+ public final String USER_LOCK = "lock::user::";
+}
diff --git a/user-service/src/main/java/com/manabie/todo/constant/TaskStatus.java b/user-service/src/main/java/com/manabie/todo/constant/TaskStatus.java
new file mode 100644
index 000000000..b29b27a6a
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/constant/TaskStatus.java
@@ -0,0 +1,7 @@
+package com.manabie.todo.constant;
+
+public enum TaskStatus {
+ NEW,
+ PROCESSING,
+ COMPLETED
+}
diff --git a/user-service/src/main/java/com/manabie/todo/controller/UserController.java b/user-service/src/main/java/com/manabie/todo/controller/UserController.java
new file mode 100644
index 000000000..e893de253
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/controller/UserController.java
@@ -0,0 +1,32 @@
+package com.manabie.todo.controller;
+
+import com.manabie.todo.model.BaseResponse;
+import com.manabie.todo.model.CreateUserRequest;
+import com.manabie.todo.model.UserInfo;
+import com.manabie.todo.service.UserService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/user")
+public class UserController {
+ final UserService userService;
+
+ @PostMapping
+ BaseResponse createUser(@RequestBody CreateUserRequest request) {
+ return BaseResponse.ofSucceeded(userService.create(request));
+ }
+
+ @GetMapping("/{userId}")
+ BaseResponse getUserTaskById(@PathVariable Long userId) {
+ return BaseResponse.ofSucceeded(userService.getById(userId));
+ }
+
+ @GetMapping()
+ BaseResponse> getAllUser() {
+ return BaseResponse.ofSucceeded(userService.findAll());
+ }
+}
\ No newline at end of file
diff --git a/user-service/src/main/java/com/manabie/todo/entity/UserEntity.java b/user-service/src/main/java/com/manabie/todo/entity/UserEntity.java
new file mode 100644
index 000000000..aac036456
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/entity/UserEntity.java
@@ -0,0 +1,24 @@
+package com.manabie.todo.entity;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "users")
+@Getter
+@Setter
+public class UserEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id_seq")
+ @SequenceGenerator(name = "user_id_seq", sequenceName = "user_id_seq", allocationSize = 1)
+ private Long id;
+ private String username;
+ private Integer maxTaskPerDay;
+ private LocalDateTime createdAt;
+ private String createdBy;
+ private LocalDateTime updatedAt;
+ private String updatedBy;
+}
diff --git a/user-service/src/main/java/com/manabie/todo/exception/BaseResponse.java b/user-service/src/main/java/com/manabie/todo/exception/BaseResponse.java
new file mode 100644
index 000000000..17cfa0985
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/exception/BaseResponse.java
@@ -0,0 +1,44 @@
+package com.manabie.todo.exception;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import org.springframework.http.HttpStatus;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+
+@Data
+public class BaseResponse {
+ private Status status;
+ private T data;
+
+ public static BaseResponse ofSucceeded(T data) {
+ BaseResponse response = new BaseResponse<>();
+ response.data = data;
+ response.status.code = HttpStatus.OK.value();
+ return response;
+ }
+
+ public static BaseResponse ofSucceeded() {
+ BaseResponse response = new BaseResponse<>();
+ response.status.code = HttpStatus.OK.value();
+ return response;
+ }
+
+ public static BaseResponse ofFailed(Integer errorCode) {
+ return ofFailed(errorCode, null);
+ }
+
+ public static BaseResponse ofFailed(Integer errorCode, String message) {
+ BaseResponse response = new BaseResponse<>();
+ response.status.code = errorCode;
+ response.status.message = message;
+ return response;
+ }
+
+ @Data
+ public static class Status {
+ private String message;
+ Integer code;
+ boolean success;
+ }
+}
diff --git a/user-service/src/main/java/com/manabie/todo/exception/ManabieException.java b/user-service/src/main/java/com/manabie/todo/exception/ManabieException.java
new file mode 100644
index 000000000..3b9e477b0
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/exception/ManabieException.java
@@ -0,0 +1,15 @@
+package com.manabie.todo.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import org.springframework.http.HttpStatus;
+
+@Builder
+@Data
+@AllArgsConstructor
+public class ManabieException extends RuntimeException {
+ private int code;
+ private String message;
+ private HttpStatus httpStatus;
+}
diff --git a/user-service/src/main/java/com/manabie/todo/exception/ManabieExceptionHandler.java b/user-service/src/main/java/com/manabie/todo/exception/ManabieExceptionHandler.java
new file mode 100644
index 000000000..d1de81706
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/exception/ManabieExceptionHandler.java
@@ -0,0 +1,24 @@
+package com.manabie.todo.exception;
+
+import com.manabie.todo.model.BaseResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class ManabieExceptionHandler {
+ @ExceptionHandler({ManabieException.class})
+ public ResponseEntity vuiException(ManabieException ex) {
+ BaseResponse body = BaseResponse.ofFailed(ex.getCode(), ex.getMessage());
+ return ResponseEntity.status(ex.getHttpStatus()).body(body);
+ }
+
+ @ExceptionHandler({Exception.class})
+ @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
+ public ResponseEntity exception(Exception ex) {
+ BaseResponse body = BaseResponse.ofFailed(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
+ }
+}
diff --git a/user-service/src/main/java/com/manabie/todo/model/BaseResponse.java b/user-service/src/main/java/com/manabie/todo/model/BaseResponse.java
new file mode 100644
index 000000000..d4526f4df
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/model/BaseResponse.java
@@ -0,0 +1,52 @@
+package com.manabie.todo.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import org.springframework.http.HttpStatus;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+
+@Data
+public class BaseResponse {
+ private Status status;
+ private T data;
+
+ public static BaseResponse ofSucceeded(T data) {
+ BaseResponse response = new BaseResponse<>();
+ response.data = data;
+
+ Status responseStatus=new Status();
+ responseStatus.setCode(HttpStatus.OK.value());
+ response.status=responseStatus;
+ return response;
+ }
+
+ public static BaseResponse ofSucceeded() {
+ BaseResponse response = new BaseResponse<>();
+ Status responseStatus=new Status();
+ responseStatus.setCode(HttpStatus.OK.value());
+ response.status=responseStatus;
+ return response;
+ }
+
+ public static BaseResponse ofFailed(Integer errorCode) {
+ return ofFailed(errorCode, null);
+ }
+
+ public static BaseResponse ofFailed(Integer errorCode, String message) {
+ BaseResponse response = new BaseResponse<>();
+
+ Status responseStatus=new Status();
+ responseStatus.setCode(errorCode);
+ responseStatus.setMessage(message);
+ response.status=responseStatus;
+ return response;
+ }
+
+ @Data
+ public static class Status {
+ private String message;
+ Integer code;
+ boolean success;
+ }
+}
diff --git a/user-service/src/main/java/com/manabie/todo/model/CreateUserRequest.java b/user-service/src/main/java/com/manabie/todo/model/CreateUserRequest.java
new file mode 100644
index 000000000..78f6c561b
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/model/CreateUserRequest.java
@@ -0,0 +1,18 @@
+package com.manabie.todo.model;
+
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class CreateUserRequest {
+ @NotBlank(message = "username is mandatory")
+ private String username;
+
+ @Min(value=1, message="maxTaskPerDay must be equal or greater than 1")
+ @Max(value=45, message="maxTaskPerDay must be equal or less than 10")
+ private Integer maxTaskPerDay;
+}
diff --git a/user-service/src/main/java/com/manabie/todo/model/UserInfo.java b/user-service/src/main/java/com/manabie/todo/model/UserInfo.java
new file mode 100644
index 000000000..2b50882f2
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/model/UserInfo.java
@@ -0,0 +1,18 @@
+package com.manabie.todo.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+public class UserInfo {
+ private Long id;
+ private String username;
+ private Integer maxTaskPerDay;
+ private LocalDateTime createdAt;
+ private String createdBy;
+ private LocalDateTime updatedAt;
+ private String updatedBy;
+}
diff --git a/user-service/src/main/java/com/manabie/todo/repository/UserRepository.java b/user-service/src/main/java/com/manabie/todo/repository/UserRepository.java
new file mode 100644
index 000000000..c38940926
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/repository/UserRepository.java
@@ -0,0 +1,9 @@
+package com.manabie.todo.repository;
+
+import com.manabie.todo.entity.UserEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+}
diff --git a/user-service/src/main/java/com/manabie/todo/service/UserService.java b/user-service/src/main/java/com/manabie/todo/service/UserService.java
new file mode 100644
index 000000000..dc9c73231
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/service/UserService.java
@@ -0,0 +1,12 @@
+package com.manabie.todo.service;
+
+import com.manabie.todo.model.CreateUserRequest;
+import com.manabie.todo.model.UserInfo;
+
+import java.util.List;
+
+public interface UserService {
+ UserInfo create(CreateUserRequest request);
+ UserInfo getById(Long userId);
+ List findAll();
+}
diff --git a/user-service/src/main/java/com/manabie/todo/service/impl/UserServiceImpl.java b/user-service/src/main/java/com/manabie/todo/service/impl/UserServiceImpl.java
new file mode 100644
index 000000000..2608f7163
--- /dev/null
+++ b/user-service/src/main/java/com/manabie/todo/service/impl/UserServiceImpl.java
@@ -0,0 +1,37 @@
+package com.manabie.todo.service.impl;
+
+import com.manabie.todo.entity.UserEntity;
+import com.manabie.todo.model.CreateUserRequest;
+import com.manabie.todo.model.UserInfo;
+import com.manabie.todo.repository.UserRepository;
+import com.manabie.todo.service.UserService;
+import lombok.RequiredArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class UserServiceImpl implements UserService {
+ final UserRepository userRepository;
+ final ModelMapper modelMapper;
+
+ @Override
+ public UserInfo create(CreateUserRequest request) {
+ UserEntity userEntity = modelMapper.map(request, UserEntity.class);
+ userEntity.setCreatedAt(LocalDateTime.now());
+ return modelMapper.map(userRepository.save(userEntity), UserInfo.class);
+ }
+
+ @Override
+ public UserInfo getById(Long userId) {
+ return modelMapper.map(userRepository.getReferenceById(userId), UserInfo.class);
+ }
+
+ @Override
+ public List findAll() {
+ return userRepository.findAll().stream().map(u -> modelMapper.map(u, UserInfo.class)).toList();
+ }
+}
diff --git a/user-service/src/main/resources/application-docker.properties b/user-service/src/main/resources/application-docker.properties
new file mode 100644
index 000000000..342115d89
--- /dev/null
+++ b/user-service/src/main/resources/application-docker.properties
@@ -0,0 +1,18 @@
+spring.application.name=user-service
+server.port=8080
+
+spring.data.redis.host=redis
+spring.data.redis.port=6379
+
+spring.datasource.hikari.connectionTimeout=20000
+spring.datasource.hikari.maximumPoolSize=5
+
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.datasource.url=jdbc:postgresql://postgres-user:5431/user
+spring.datasource.username=manabie
+spring.datasource.password=manabie@123
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.generate-ddl=true
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.show-sql=true
\ No newline at end of file
diff --git a/user-service/src/main/resources/application.properties b/user-service/src/main/resources/application.properties
new file mode 100644
index 000000000..5e3c9e4d5
--- /dev/null
+++ b/user-service/src/main/resources/application.properties
@@ -0,0 +1,18 @@
+spring.application.name=user-service
+server.port=8081
+
+spring.data.redis.host=localhost
+spring.data.redis.port=6379
+
+spring.datasource.hikari.connectionTimeout=20000
+spring.datasource.hikari.maximumPoolSize=5
+
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.datasource.url=jdbc:postgresql://localhost:5432/user
+spring.datasource.username=postgres
+spring.datasource.password=postgres
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.generate-ddl=true
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.show-sql=true
\ No newline at end of file