diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/microservices/.gitignore b/microservices/.gitignore new file mode 100644 index 0000000..7ed0d6b --- /dev/null +++ b/microservices/.gitignore @@ -0,0 +1,32 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/microservices/Readme.md b/microservices/Readme.md new file mode 100644 index 0000000..c448448 --- /dev/null +++ b/microservices/Readme.md @@ -0,0 +1,38 @@ +# Getting Started + +Cada microservicio se ha construido siguiendo el patrón de diseño "Arquitectura Hexagonal" + +### Microservicios + +* **transaction-bff:** Backend for FrontEnd, usando GraphQL. +* **transaction-ms:** Creación, actualización y consulta de transacciones. + * Envía peticiones asíncronas hacia el microservicio antifraud-ms(Evaluación de la transacción), mediante Apache Kafka + * Recibe peticiones asíncronas con el estado final de la transacción, mediante Apache Kafka +* **antifraud-ms:** Analiza la transacción recibida y setea el correcto estado de la ella. + +### Instalación + +Seguir los siguientes pasos. + +1. Situarse en el file \app-java-codechallenge\docker-compose.yml y ejectuar: docker compose up +2. Crear Base de datos: yapebd +3. Ejecutar el siguiente script sql: app-java-codechallenge\script-bd **script-create_table.sql** +3. Crear los siguientes tópicos: + * kafka-console-producer --broker-list localhost:9092 --topic **topic-transaction-update** + * kafka-console-producer --broker-list localhost:9092 --topic **topic-antifraud-review** +4. Ejecutar los microservicios: + * transaction-ms + * antifraud-ms + * transaction-bff +5. Abrir navegador y escribir: http://localhost:8090/graphiql?path=/graphql +6. Existen dos llamados de recursos: + * **(Mutation) register**: Proceso de registro y evaluación de transacciones + * **(Query) getTransactionByCode**: Busca el transaction según el code + * **(Query) getAllTransactions**: Consigue todas las transacciones +7. En el PR se adjuntan pantallas de peticiones y resultados esperados + + + + + + diff --git a/microservices/antifraud-ms/.gitignore b/microservices/antifraud-ms/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/microservices/antifraud-ms/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/microservices/antifraud-ms/.mvn/wrapper/maven-wrapper.properties b/microservices/antifraud-ms/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..aeccdfd --- /dev/null +++ b/microservices/antifraud-ms/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# 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. +wrapperVersion=3.3.1 +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip diff --git a/microservices/antifraud-ms/mvnw b/microservices/antifraud-ms/mvnw new file mode 100644 index 0000000..ba9212a --- /dev/null +++ b/microservices/antifraud-ms/mvnw @@ -0,0 +1,250 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.1 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl="${value-}" ;; + distributionSha256Sum) distributionSha256Sum="${value-}" ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/microservices/antifraud-ms/mvnw.cmd b/microservices/antifraud-ms/mvnw.cmd new file mode 100644 index 0000000..406932d --- /dev/null +++ b/microservices/antifraud-ms/mvnw.cmd @@ -0,0 +1,146 @@ +<# : batch portion +@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 Apache Maven Wrapper startup batch script, version 3.3.1 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/microservices/antifraud-ms/pom.xml b/microservices/antifraud-ms/pom.xml new file mode 100644 index 0000000..22d3648 --- /dev/null +++ b/microservices/antifraud-ms/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.yape + microservices + 1.0.0-SNAPSHOT + + + antifraud-ms + 0.0.1-SNAPSHOT + antifraud-ms + Antifraud-ms for Spring Boot + + 17 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.kafka + spring-kafka + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.kafka + spring-kafka-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/AntifraudMsApplication.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/AntifraudMsApplication.java new file mode 100644 index 0000000..ce9f3ad --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/AntifraudMsApplication.java @@ -0,0 +1,15 @@ +package com.yape.antifraud; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class AntifraudMsApplication { + + public static void main(String[] args) { + + SpringApplication.run(AntifraudMsApplication.class, args); + } + +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/port/in/ReviewAntiFraudCommand.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/port/in/ReviewAntiFraudCommand.java new file mode 100644 index 0000000..57eb743 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/port/in/ReviewAntiFraudCommand.java @@ -0,0 +1,21 @@ +package com.yape.antifraud.application.port.in; + +import com.yape.antifraud.domain.TransactionStatusEnum; +import lombok.Builder; +import lombok.Value; + +import java.math.BigDecimal; +import java.util.UUID; + +public interface ReviewAntiFraudCommand { + void execute(Command command); + @Value + @Builder + class Command { + Long id; + UUID code; + TransactionStatusEnum status; + BigDecimal value; + } + +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/port/out/TransactionEdaRepository.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/port/out/TransactionEdaRepository.java new file mode 100644 index 0000000..b6f2a6a --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/port/out/TransactionEdaRepository.java @@ -0,0 +1,7 @@ +package com.yape.antifraud.application.port.out; + +import com.yape.antifraud.infra.out.adapter.kafka.model.TransactionOutModel; + +public interface TransactionEdaRepository { + void updateTransaction(TransactionOutModel transactionModel); +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/usecase/ReviewAntiFraudUseCase.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/usecase/ReviewAntiFraudUseCase.java new file mode 100644 index 0000000..0049265 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/application/usecase/ReviewAntiFraudUseCase.java @@ -0,0 +1,39 @@ +package com.yape.antifraud.application.usecase; + +import com.yape.antifraud.application.port.in.ReviewAntiFraudCommand; +import com.yape.antifraud.application.port.out.TransactionEdaRepository; +import com.yape.antifraud.domain.TransactionStatusEnum; +import com.yape.antifraud.infra.out.adapter.kafka.model.TransactionOutModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +@Slf4j +public class ReviewAntiFraudUseCase implements ReviewAntiFraudCommand { + + @Value("${antifraud.transaction.max.value}") + private BigDecimal maxValue; + private final TransactionEdaRepository transactionEdaRepository; + + public ReviewAntiFraudUseCase(TransactionEdaRepository transactionEdaRepository) { + this.transactionEdaRepository = transactionEdaRepository; + } + + @Override + public void execute(Command command) { + log.info("Reviewing transaction id: {}", command.getId()); + TransactionOutModel transactionModel = TransactionOutModel.builder() + .id(command.getId()) + .status(command.getStatus()) + .code(command.getCode()) + .build(); + transactionModel.setStatus(TransactionStatusEnum.APPROVED); + if (command.getValue().compareTo(maxValue) > 0) { + transactionModel.setStatus(TransactionStatusEnum.REJECTED); + } + this.transactionEdaRepository.updateTransaction(transactionModel); + } +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/KafkaConfig.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/KafkaConfig.java new file mode 100644 index 0000000..b304cb1 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/KafkaConfig.java @@ -0,0 +1,52 @@ +package com.yape.antifraud.config; + +import com.yape.antifraud.config.properties.SpringConfigurationProperties; +import com.yape.antifraud.infra.in.adapter.kafka.model.TransactionInModel; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer; +import org.springframework.kafka.support.serializer.JsonDeserializer; + +import java.util.HashMap; +import java.util.Map; + +@EnableKafka +@Configuration +@Slf4j +public class KafkaConfig { + + private final SpringConfigurationProperties springConfigurationProperties; + private static final String EARLIEST = "earliest"; + public KafkaConfig(SpringConfigurationProperties springConfigurationProperties) { + this.springConfigurationProperties = springConfigurationProperties; + } + + @Bean + public ConsumerFactory antifraudConsumerConfigs() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, springConfigurationProperties.getKafka().getBootstrapServers()); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + props.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class); + props.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, StringDeserializer.class); + props.put(ConsumerConfig.GROUP_ID_CONFIG, springConfigurationProperties.getKafka().getConsumer().getGroupId()); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, EARLIEST); + return new DefaultKafkaConsumerFactory<>(props, new ErrorHandlingDeserializer<>(), new ErrorHandlingDeserializer<>(new JsonDeserializer<>(TransactionInModel.class))); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory antifraudKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory + factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(antifraudConsumerConfigs()); + return factory; + } + +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorCode.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorCode.java new file mode 100644 index 0000000..e8ac356 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorCode.java @@ -0,0 +1,33 @@ +package com.yape.antifraud.config.exception; + +public enum ErrorCode { + + BAD_REQUEST(105, "La request esta mal formateada", "BAD_REQUEST"), + INVALID_PARAMETERS_ERROR(110, "{}", "INVALID_PARAMETERS"), + WEB_CLIENT_GENERIC(103, "Unexpected rest client error", "INTERNAL_SERVER_ERROR"), + KAFKA_EXCEPTION(109, "Error interno de kafka", "KAFKA_EXCEPTION"), + INTERNAL_ERROR(108,"Internal Error","INTERNAL_ERROR"), + INVALID_FILTERS_ERROR(109, "Invalid filters", "INVALID_FILTERS"); + + private final int value; + private final String reasonPhrase; + private final String code; + + ErrorCode(int value, String reasonPhrase, String code) { + this.value = value; + this.reasonPhrase = reasonPhrase; + this.code = code; + } + + public int value() { + return this.value; + } + + public String getReasonPhrase() { + return this.reasonPhrase; + } + + public String getCode() { + return this.code; + } +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorHandler.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorHandler.java new file mode 100644 index 0000000..50ceddd --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorHandler.java @@ -0,0 +1,40 @@ +package com.yape.antifraud.config.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@Slf4j +@ControllerAdvice +public class ErrorHandler { + + @ExceptionHandler(Throwable.class) + public ResponseEntity handle(Throwable ex) { + log.error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), ex); + return buildResponseError(HttpStatus.INTERNAL_SERVER_ERROR, ErrorCode.INTERNAL_ERROR); + } + + @ExceptionHandler(KafkaException.class) + public ResponseEntity handle(KafkaException ex) { + log.error(ex.getCode().getCode(), ex); + return buildResponseError(HttpStatus.SERVICE_UNAVAILABLE, ex.getCode()); + } + + private ResponseEntity buildCustomResponseError(HttpStatus httpStatus, ErrorCode errorCode, String customDescription) { + final var response = ErrorResponse.builder() + .errorInternalCode(errorCode.value()) + .errorDescription(customDescription) + .errorCode(errorCode.getCode()) + .build(); + + return new ResponseEntity<>(response, httpStatus); + } + + private ResponseEntity buildResponseError(HttpStatus httpStatus, ErrorCode errorCode) { + return buildCustomResponseError(httpStatus, errorCode, errorCode.getReasonPhrase()); + } + +} + diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorResponse.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorResponse.java new file mode 100644 index 0000000..c667142 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/ErrorResponse.java @@ -0,0 +1,15 @@ +package com.yape.antifraud.config.exception; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; + +@Builder +public class ErrorResponse { + @JsonProperty + int errorInternalCode; + @JsonProperty + String errorDescription; + @JsonProperty + String errorCode; +} + diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/GenericException.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/GenericException.java new file mode 100644 index 0000000..1efaeec --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/GenericException.java @@ -0,0 +1,14 @@ +package com.yape.antifraud.config.exception; + +public abstract class GenericException extends RuntimeException { + private final ErrorCode errorCode; + + public GenericException(ErrorCode errorCode) { + super(errorCode.getReasonPhrase()); + this.errorCode = errorCode; + } + + public ErrorCode getCode() { + return this.errorCode; + } +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/KafkaException.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/KafkaException.java new file mode 100644 index 0000000..daf3b07 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/exception/KafkaException.java @@ -0,0 +1,7 @@ +package com.yape.antifraud.config.exception; + +public class KafkaException extends GenericException { + public KafkaException(ErrorCode ec){ + super(ec); + } +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/ConsumerKafkaProperties.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/ConsumerKafkaProperties.java new file mode 100644 index 0000000..ec59d7f --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/ConsumerKafkaProperties.java @@ -0,0 +1,8 @@ +package com.yape.antifraud.config.properties; + +import lombok.Data; + +@Data +public class ConsumerKafkaProperties { + private String groupId; +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/KafkaProperties.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/KafkaProperties.java new file mode 100644 index 0000000..5337738 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/KafkaProperties.java @@ -0,0 +1,13 @@ +package com.yape.antifraud.config.properties; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +public class KafkaProperties { + private String bootstrapServers; + private TopicProperties topic; + private ConsumerKafkaProperties consumer; +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/SpringConfigurationProperties.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/SpringConfigurationProperties.java new file mode 100644 index 0000000..c33ddd8 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/SpringConfigurationProperties.java @@ -0,0 +1,16 @@ +package com.yape.antifraud.config.properties; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "spring") +@AllArgsConstructor +@NoArgsConstructor +@Data +public class SpringConfigurationProperties { + private KafkaProperties kafka; +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/TopicProperties.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/TopicProperties.java new file mode 100644 index 0000000..454f54a --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/config/properties/TopicProperties.java @@ -0,0 +1,12 @@ +package com.yape.antifraud.config.properties; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopicProperties { + private String antifraud; + private String transaction; +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/domain/TransactionStatusEnum.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/domain/TransactionStatusEnum.java new file mode 100644 index 0000000..aa9502b --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/domain/TransactionStatusEnum.java @@ -0,0 +1,7 @@ +package com.yape.antifraud.domain; + +public enum TransactionStatusEnum { + PENDING, + APPROVED, + REJECTED +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/in/adapter/kafka/AntiFraudListenerAdapter.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/in/adapter/kafka/AntiFraudListenerAdapter.java new file mode 100644 index 0000000..f84f991 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/in/adapter/kafka/AntiFraudListenerAdapter.java @@ -0,0 +1,41 @@ +package com.yape.antifraud.infra.in.adapter.kafka; + +import com.yape.antifraud.application.port.in.ReviewAntiFraudCommand; +import com.yape.antifraud.config.exception.ErrorCode; +import com.yape.antifraud.infra.in.adapter.kafka.model.TransactionInModel; +import com.yape.antifraud.config.exception.KafkaException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class AntiFraudListenerAdapter { + private final ReviewAntiFraudCommand reviewAntiFraudCommand; + + public AntiFraudListenerAdapter(ReviewAntiFraudCommand reviewAntiFraudCommand) { + this.reviewAntiFraudCommand = reviewAntiFraudCommand; + } + + @KafkaListener( + topics = "${spring.kafka.topic.antifraud}", + groupId = "${spring.kafka.consumer.group-id}", + containerFactory = "antifraudKafkaListenerContainerFactory" + + ) + public void listen(TransactionInModel message) { + log.info("AntiFraudListenerAdapter message received from kafka {}", message); + try { + ReviewAntiFraudCommand.Command command = ReviewAntiFraudCommand.Command.builder() + .id(message.getId()) + .code(message.getCode()) + .status(message.getStatus()) + .value(message.getValue()) + .build(); + this.reviewAntiFraudCommand.execute(command); + } catch (Exception ex) { + log.error("Occurred an error in kafka ", ex); + throw new KafkaException(ErrorCode.KAFKA_EXCEPTION); + } + } +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/in/adapter/kafka/model/TransactionInModel.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/in/adapter/kafka/model/TransactionInModel.java new file mode 100644 index 0000000..eae2559 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/in/adapter/kafka/model/TransactionInModel.java @@ -0,0 +1,23 @@ +package com.yape.antifraud.infra.in.adapter.kafka.model; + +import com.yape.antifraud.domain.TransactionStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.UUID; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class TransactionInModel implements Serializable { + private Long id; + private UUID code; + private TransactionStatusEnum status; + private BigDecimal value; + +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/out/adapter/kafka/TransactionKafkaAdapter.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/out/adapter/kafka/TransactionKafkaAdapter.java new file mode 100644 index 0000000..cff982c --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/out/adapter/kafka/TransactionKafkaAdapter.java @@ -0,0 +1,32 @@ +package com.yape.antifraud.infra.out.adapter.kafka; + +import com.yape.antifraud.application.port.out.TransactionEdaRepository; +import com.yape.antifraud.config.exception.ErrorCode; +import com.yape.antifraud.config.properties.SpringConfigurationProperties; +import com.yape.antifraud.config.exception.KafkaException; +import com.yape.antifraud.infra.out.adapter.kafka.model.TransactionOutModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class TransactionKafkaAdapter implements TransactionEdaRepository { + private final KafkaTemplate kafkaTemplate; + private final SpringConfigurationProperties springConfigurationProperties; + public TransactionKafkaAdapter(KafkaTemplate kafkaTemplate, SpringConfigurationProperties springConfigurationProperties) { + this.kafkaTemplate = kafkaTemplate; + this.springConfigurationProperties = springConfigurationProperties; + } + @Override + public void updateTransaction(TransactionOutModel transactionModel) { + try { + log.info("sending to Transaction for update, transaction id:{}, status:{}", transactionModel.getId(), transactionModel.getStatus() ); + this.kafkaTemplate.send(springConfigurationProperties.getKafka().getTopic().getTransaction(), transactionModel); + this.kafkaTemplate.flush(); + } catch (Exception e) { + log.error("Occurred an error in kafka ", e); + throw new KafkaException(ErrorCode.KAFKA_EXCEPTION); + } + } +} diff --git a/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/out/adapter/kafka/model/TransactionOutModel.java b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/out/adapter/kafka/model/TransactionOutModel.java new file mode 100644 index 0000000..94076e9 --- /dev/null +++ b/microservices/antifraud-ms/src/main/java/com/yape/antifraud/infra/out/adapter/kafka/model/TransactionOutModel.java @@ -0,0 +1,20 @@ +package com.yape.antifraud.infra.out.adapter.kafka.model; + +import com.yape.antifraud.domain.TransactionStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.UUID; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TransactionOutModel implements Serializable { + private Long id; + private TransactionStatusEnum status; + private UUID code; +} diff --git a/microservices/antifraud-ms/src/main/resources/application.properties b/microservices/antifraud-ms/src/main/resources/application.properties new file mode 100644 index 0000000..86e4cf5 --- /dev/null +++ b/microservices/antifraud-ms/src/main/resources/application.properties @@ -0,0 +1,14 @@ +spring.application.name=antifraud-ms + +spring.kafka.bootstrap-servers=localhost:9092 +spring.kafka.topic.antifraud=topic-antifraud-review +spring.kafka.topic.transaction=topic-transaction-update +spring.kafka.producer.properties.spring.json.add.type.headers=false +spring.kafka.producer.key-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.consumer.group-id=bcp-group +server.port=8081 + +#max value to review antifraud +antifraud.transaction.max.value=1000 + diff --git a/microservices/antifraud-ms/src/test/java/com/yape/antifraud/AntifraudMsApplicationTests.java b/microservices/antifraud-ms/src/test/java/com/yape/antifraud/AntifraudMsApplicationTests.java new file mode 100644 index 0000000..b4fb80e --- /dev/null +++ b/microservices/antifraud-ms/src/test/java/com/yape/antifraud/AntifraudMsApplicationTests.java @@ -0,0 +1,13 @@ +package com.yape.antifraud; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AntifraudMsApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/microservices/pom.xml b/microservices/pom.xml new file mode 100644 index 0000000..0c45195 --- /dev/null +++ b/microservices/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.0 + + + + com.yape + microservices + 1.0.0-SNAPSHOT + pom + microservices + Microservices for Spring Boot + + 17 + 17 + UTF-8 + + + + transaction-ms + antifraud-ms + transaction-bff + + + + + + + + + + + diff --git a/microservices/transaction-bff/.gitignore b/microservices/transaction-bff/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/microservices/transaction-bff/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/microservices/transaction-bff/.mvn/wrapper/maven-wrapper.properties b/microservices/transaction-bff/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..aeccdfd --- /dev/null +++ b/microservices/transaction-bff/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# 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. +wrapperVersion=3.3.1 +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip diff --git a/microservices/transaction-bff/mvnw b/microservices/transaction-bff/mvnw new file mode 100644 index 0000000..ba9212a --- /dev/null +++ b/microservices/transaction-bff/mvnw @@ -0,0 +1,250 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.1 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl="${value-}" ;; + distributionSha256Sum) distributionSha256Sum="${value-}" ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/microservices/transaction-bff/mvnw.cmd b/microservices/transaction-bff/mvnw.cmd new file mode 100644 index 0000000..406932d --- /dev/null +++ b/microservices/transaction-bff/mvnw.cmd @@ -0,0 +1,146 @@ +<# : batch portion +@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 Apache Maven Wrapper startup batch script, version 3.3.1 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/microservices/transaction-bff/pom.xml b/microservices/transaction-bff/pom.xml new file mode 100644 index 0000000..5aacb67 --- /dev/null +++ b/microservices/transaction-bff/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + + com.yape + microservices + 1.0.0-SNAPSHOT + + transaction-bff + 0.0.1-SNAPSHOT + transaction-bff + Transaction Bff project for Spring Boot + + 17 + 2023.0.1 + + + + org.springframework.boot + spring-boot-starter-graphql + + + com.graphql-java + graphql-java-extended-scalars + 20.0 + + + com.tailrocks.graphql + graphql-datetime-spring-boot-starter + 6.0.0 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework + spring-webflux + test + + + org.springframework.graphql + spring-graphql-test + test + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/TransactionBffApplication.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/TransactionBffApplication.java new file mode 100644 index 0000000..2859fae --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/TransactionBffApplication.java @@ -0,0 +1,16 @@ +package com.yape.transactionbff; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +@EnableFeignClients +public class TransactionBffApplication { + + public static void main(String[] args) { + SpringApplication.run(TransactionBffApplication.class, args); + } + +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/domain/Transaction.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/domain/Transaction.java new file mode 100644 index 0000000..3b4f3fe --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/domain/Transaction.java @@ -0,0 +1,22 @@ +package com.yape.transactionbff.application.domain; + +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionStatus; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Value; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Builder +@Value +@AllArgsConstructor +public class Transaction { + UUID transactionExternalId; + TransactionType transactionType; + TransactionStatus transactionStatus; + BigDecimal value; + LocalDateTime createAt; +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/in/GetTransactionQuery.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/in/GetTransactionQuery.java new file mode 100644 index 0000000..8978809 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/in/GetTransactionQuery.java @@ -0,0 +1,11 @@ +package com.yape.transactionbff.application.port.in; + +import com.yape.transactionbff.application.domain.Transaction; + +import java.util.List; +import java.util.UUID; + +public interface GetTransactionQuery { + Transaction getTransactionByCode(UUID code); + List getAllTransactions(); +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/in/RegisterTransactionCommand.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/in/RegisterTransactionCommand.java new file mode 100644 index 0000000..794c501 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/in/RegisterTransactionCommand.java @@ -0,0 +1,21 @@ +package com.yape.transactionbff.application.port.in; + +import com.yape.transactionbff.application.domain.Transaction; +import lombok.Builder; +import lombok.Value; + +import java.math.BigDecimal; +import java.util.UUID; + +public interface RegisterTransactionCommand { + Transaction execute(Command command); + @Value + @Builder + class Command { + UUID accountExternalIdDebit; + UUID accountExternalIdCredit; + Integer transferTypeId; + BigDecimal value; + } + +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/out/TransactionClient.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/out/TransactionClient.java new file mode 100644 index 0000000..9b1f673 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/port/out/TransactionClient.java @@ -0,0 +1,30 @@ +package com.yape.transactionbff.application.port.out; + + +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionModelRequest; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionModelResponse; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; +import java.util.UUID; + +@FeignClient( + name = "transactions", + url = "http://localhost:8080/transaction-ms" + +) +public interface TransactionClient { + @GetMapping(value = "/{code}",consumes = MediaType.APPLICATION_JSON_VALUE) + TransactionModelResponse getTransactionById(@PathVariable("code") UUID code); + + @GetMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + List getAllTransactions(); + + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + TransactionModelResponse register(@RequestBody TransactionModelRequest request); +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/usecase/GetTransactionUseCase.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/usecase/GetTransactionUseCase.java new file mode 100644 index 0000000..3cf14f4 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/usecase/GetTransactionUseCase.java @@ -0,0 +1,31 @@ +package com.yape.transactionbff.application.usecase; + +import com.yape.transactionbff.application.domain.Transaction; +import com.yape.transactionbff.application.port.in.GetTransactionQuery; +import com.yape.transactionbff.application.port.out.TransactionClient; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionModelResponse; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +public class GetTransactionUseCase implements GetTransactionQuery { + private final TransactionClient transactionClient; + + public GetTransactionUseCase(TransactionClient transactionClient){ + this.transactionClient = transactionClient; + } + @Override + public Transaction getTransactionByCode(UUID code) { + return this.transactionClient.getTransactionById(code).toDomain(); + } + @Override + public List getAllTransactions() { + return this.transactionClient.getAllTransactions().stream() + .map(TransactionModelResponse::toDomain) + .collect(Collectors.toList()); + } + +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/usecase/RegisterTransactionUseCase.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/usecase/RegisterTransactionUseCase.java new file mode 100644 index 0000000..5368282 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/application/usecase/RegisterTransactionUseCase.java @@ -0,0 +1,30 @@ +package com.yape.transactionbff.application.usecase; + +import com.yape.transactionbff.application.domain.Transaction; +import com.yape.transactionbff.application.port.in.RegisterTransactionCommand; +import com.yape.transactionbff.application.port.out.TransactionClient; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionModelRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class RegisterTransactionUseCase implements RegisterTransactionCommand { + private final TransactionClient transactionClient; + + public RegisterTransactionUseCase(TransactionClient transactionClient) { + this.transactionClient = transactionClient; + } + + @Override + public Transaction execute(Command command) { + TransactionModelRequest request = TransactionModelRequest.builder() + .value(command.getValue()) + .accountExternalIdDebit(command.getAccountExternalIdDebit()) + .accountExternalIdCredit(command.getAccountExternalIdCredit()) + .transferTypeId(command.getTransferTypeId()) + .build(); + + return transactionClient.register(request).toDomain(); + } +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/FeignConfig.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/FeignConfig.java new file mode 100644 index 0000000..5628021 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/FeignConfig.java @@ -0,0 +1,14 @@ +package com.yape.transactionbff.config; + +import feign.Logger; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FeignConfig { + @Bean + Logger.Level feignLoggerLevel(){ + return Logger.Level.FULL; + } + +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/GraphQlConfig.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/GraphQlConfig.java new file mode 100644 index 0000000..3223f62 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/GraphQlConfig.java @@ -0,0 +1,18 @@ +package com.yape.transactionbff.config; + +import graphql.scalars.ExtendedScalars; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.graphql.execution.RuntimeWiringConfigurer; + +@Configuration +public class GraphQlConfig { + @Bean + public RuntimeWiringConfigurer runtimeWiringConfigurer() { + return wiringBuilder -> wiringBuilder + .scalar(ExtendedScalars.GraphQLBigDecimal) + .scalar(ExtendedScalars.UUID) + .scalar(ExtendedScalars.DateTime); + + } +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/CustomErrorDecoder.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/CustomErrorDecoder.java new file mode 100644 index 0000000..05bf811 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/CustomErrorDecoder.java @@ -0,0 +1,18 @@ +package com.yape.transactionbff.config.exception; + +import feign.Response; +import feign.codec.ErrorDecoder; +import org.springframework.stereotype.Component; + +@Component +public class CustomErrorDecoder implements ErrorDecoder { + private final ErrorDecoder errorDecoder = new Default(); + + @Override + public Exception decode(String s, Response response) { + if (response.status() == 404) { + return new NotFoundException("Transaction not found"); + } + return new Exception("Unexpected exception"); + } +} \ No newline at end of file diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/CustomExceptionResolver.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/CustomExceptionResolver.java new file mode 100644 index 0000000..3a46347 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/CustomExceptionResolver.java @@ -0,0 +1,22 @@ +package com.yape.transactionbff.config.exception; + +import graphql.GraphQLError; +import graphql.GraphqlErrorBuilder; +import graphql.schema.DataFetchingEnvironment; +import org.springframework.graphql.execution.DataFetcherExceptionResolverAdapter; +import org.springframework.graphql.execution.ErrorType; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; + +@Component +public class CustomExceptionResolver extends DataFetcherExceptionResolverAdapter { + @Override + protected GraphQLError resolveToSingleError(@NonNull Throwable ex, @NonNull DataFetchingEnvironment env){ + return GraphqlErrorBuilder.newError() + .errorType(ErrorType.BAD_REQUEST) + .message(ex.getMessage() != null ? ex.getMessage() : ex.getCause().getMessage() ) + .path(env.getExecutionStepInfo().getPath()) + .location(env.getField().getSourceLocation()) + .build(); + } +} \ No newline at end of file diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/ErrorHandler.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/ErrorHandler.java new file mode 100644 index 0000000..a2fe3af --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/ErrorHandler.java @@ -0,0 +1,44 @@ +package com.yape.transactionbff.config.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + + @ExceptionHandler(Throwable.class) + public ResponseEntity handle(Throwable ex) { + log.error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), ex); + ErrorResponse response = ErrorResponse.builder() + .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .description(ex.getMessage()).build(); + + return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handle(Exception ex) { + log.error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), ex); + ErrorResponse response = ErrorResponse.builder() + .code(HttpStatus.INTERNAL_SERVER_ERROR.value()) + .description(ex.getMessage()).build(); + + return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(NotFoundException.class) + public ResponseEntity handle(NotFoundException ex) { + log.error(HttpStatus.NOT_FOUND.getReasonPhrase(), ex); + ErrorResponse response = ErrorResponse.builder() + .code(HttpStatus.NOT_FOUND.value()) + .description(ex.getMessage()).build(); + + return new ResponseEntity<>(response, HttpStatus.NOT_FOUND); + } + } + + diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/ErrorResponse.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/ErrorResponse.java new file mode 100644 index 0000000..f052cc3 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/ErrorResponse.java @@ -0,0 +1,12 @@ +package com.yape.transactionbff.config.exception; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; + +@Builder +public class ErrorResponse { + @JsonProperty + Integer code; + @JsonProperty + String description; +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/NotFoundException.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/NotFoundException.java new file mode 100644 index 0000000..972599c --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/config/exception/NotFoundException.java @@ -0,0 +1,8 @@ +package com.yape.transactionbff.config.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } + +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/TransactionControllerGraph.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/TransactionControllerGraph.java new file mode 100644 index 0000000..8ba6dbf --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/TransactionControllerGraph.java @@ -0,0 +1,51 @@ +package com.yape.transactionbff.infra.in.adapter.graphql; + +import com.yape.transactionbff.application.port.in.GetTransactionQuery; +import com.yape.transactionbff.application.port.in.RegisterTransactionCommand; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionCreateModelResponse; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionModelRequest; +import com.yape.transactionbff.infra.in.adapter.graphql.model.TransactionModelResponse; +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.MutationMapping; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.stereotype.Controller; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Controller +public class TransactionControllerGraph { + private final GetTransactionQuery getTransactionQuery; + private final RegisterTransactionCommand registerTransactionCommand; + + public TransactionControllerGraph(GetTransactionQuery getTransactionQuery, RegisterTransactionCommand registerTransactionCommand) { + this.getTransactionQuery = getTransactionQuery; + this.registerTransactionCommand = registerTransactionCommand; + } + + @QueryMapping + public TransactionModelResponse getTransactionByCode(@Argument UUID code) { + return TransactionModelResponse.fromDomain(this.getTransactionQuery.getTransactionByCode(code)); + } + + @QueryMapping + public List getAllTransactions() { + return this.getTransactionQuery.getAllTransactions().stream() + .map(TransactionModelResponse::fromDomain) + .collect(Collectors.toList()); + } + + @MutationMapping + public TransactionCreateModelResponse register(@Argument TransactionModelRequest request) { + RegisterTransactionCommand.Command command = RegisterTransactionCommand.Command.builder() + .accountExternalIdCredit(request.getAccountExternalIdCredit()) + .accountExternalIdDebit(request.getAccountExternalIdDebit()) + .transferTypeId(request.getTransferTypeId()) + .value(request.getValue()) + .build(); + + return TransactionCreateModelResponse.fromDomain(this.registerTransactionCommand.execute(command)); + } + +} \ No newline at end of file diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionCreateModelResponse.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionCreateModelResponse.java new file mode 100644 index 0000000..b488ae5 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionCreateModelResponse.java @@ -0,0 +1,40 @@ +package com.yape.transactionbff.infra.in.adapter.graphql.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.yape.transactionbff.application.domain.Transaction; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TransactionCreateModelResponse { + private UUID transactionExternalId; + private TransactionType transactionType; + private BigDecimal value; + private LocalDateTime createAt; + + public Transaction toDomain() { + return Transaction.builder() + .transactionExternalId(transactionExternalId) + .transactionType(transactionType) + .value(value) + .createAt(createAt) + .build(); + } + + public static TransactionCreateModelResponse fromDomain(Transaction transaction) { + return TransactionCreateModelResponse.builder() + .transactionExternalId(transaction.getTransactionExternalId()) + .transactionType(transaction.getTransactionType()) + .value(transaction.getValue()) + .createAt(transaction.getCreateAt()) + .build(); + } +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionModelRequest.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionModelRequest.java new file mode 100644 index 0000000..34251f1 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionModelRequest.java @@ -0,0 +1,20 @@ +package com.yape.transactionbff.infra.in.adapter.graphql.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.UUID; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TransactionModelRequest { + private UUID accountExternalIdDebit; + private UUID accountExternalIdCredit; + private Integer transferTypeId; + private BigDecimal value; +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionModelResponse.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionModelResponse.java new file mode 100644 index 0000000..1ea88e4 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionModelResponse.java @@ -0,0 +1,44 @@ +package com.yape.transactionbff.infra.in.adapter.graphql.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.yape.transactionbff.application.domain.Transaction; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TransactionModelResponse { + private UUID transactionExternalId; + private TransactionType transactionType; + private TransactionStatus transactionStatus; + private BigDecimal value; + private LocalDateTime createAt; + + public Transaction toDomain() { + return Transaction.builder() + .transactionExternalId(transactionExternalId) + .transactionType(transactionType) + .transactionStatus(transactionStatus) + .value(value) + .createAt(createAt) + .build(); + } + + public static TransactionModelResponse fromDomain(Transaction transaction) { + return TransactionModelResponse.builder() + .transactionExternalId(transaction.getTransactionExternalId()) + .transactionType(transaction.getTransactionType()) + .transactionStatus(transaction.getTransactionStatus()) + .value(transaction.getValue()) + .createAt(transaction.getCreateAt()) + .build(); + } + +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionStatus.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionStatus.java new file mode 100644 index 0000000..8f03496 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionStatus.java @@ -0,0 +1,18 @@ +package com.yape.transactionbff.infra.in.adapter.graphql.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@AllArgsConstructor +@NoArgsConstructor +public class TransactionStatus { + private String name; +} diff --git a/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionType.java b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionType.java new file mode 100644 index 0000000..e54edf7 --- /dev/null +++ b/microservices/transaction-bff/src/main/java/com/yape/transactionbff/infra/in/adapter/graphql/model/TransactionType.java @@ -0,0 +1,18 @@ +package com.yape.transactionbff.infra.in.adapter.graphql.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@AllArgsConstructor +@NoArgsConstructor +public class TransactionType { + private String name; +} diff --git a/microservices/transaction-bff/src/main/resources/application.properties b/microservices/transaction-bff/src/main/resources/application.properties new file mode 100644 index 0000000..1d34aa4 --- /dev/null +++ b/microservices/transaction-bff/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.application.name=transaction-bff +spring.graphql.graphiql.enabled=true +spring.graphql.graphiql.path=/graphiql +spring.mvc.favicon.enabled=false +server.port=8090 diff --git a/microservices/transaction-bff/src/main/resources/graphql/schema.graphqls b/microservices/transaction-bff/src/main/resources/graphql/schema.graphqls new file mode 100644 index 0000000..dc9ef58 --- /dev/null +++ b/microservices/transaction-bff/src/main/resources/graphql/schema.graphqls @@ -0,0 +1,43 @@ +scalar BigDecimal +scalar LocalDateTime +scalar UUID + +type TransactionModelResponse { + transactionExternalId: UUID! + transactionType: TransactionType + transactionStatus: TransactionStatus + value: BigDecimal + createAt: LocalDateTime +} + +type TransactionCreateModelResponse { + transactionExternalId: UUID! + transactionType: TransactionType + value: BigDecimal + createAt: LocalDateTime +} + +type TransactionType { + name : String +} + +type TransactionStatus { + name : String +} + +input TransactionModelRequest { + accountExternalIdDebit : UUID + accountExternalIdCredit: UUID + transferTypeId: Int + value: BigDecimal +} + +type Query { + getTransactionByCode(code : UUID) : TransactionModelResponse + getAllTransactions : [TransactionModelResponse] +} + +type Mutation { + register(request : TransactionModelRequest) : TransactionCreateModelResponse +} + diff --git a/microservices/transaction-bff/src/main/resources/static/favicon.ico b/microservices/transaction-bff/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..3cb65d9 Binary files /dev/null and b/microservices/transaction-bff/src/main/resources/static/favicon.ico differ diff --git a/microservices/transaction-bff/src/test/java/com/yape/transactionbff/TransactionBffApplicationTests.java b/microservices/transaction-bff/src/test/java/com/yape/transactionbff/TransactionBffApplicationTests.java new file mode 100644 index 0000000..5924f70 --- /dev/null +++ b/microservices/transaction-bff/src/test/java/com/yape/transactionbff/TransactionBffApplicationTests.java @@ -0,0 +1,13 @@ +package com.yape.transactionbff; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class TransactionBffApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/microservices/transaction-ms/.gitignore b/microservices/transaction-ms/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/microservices/transaction-ms/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/microservices/transaction-ms/.mvn/wrapper/maven-wrapper.properties b/microservices/transaction-ms/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..aeccdfd --- /dev/null +++ b/microservices/transaction-ms/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# 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. +wrapperVersion=3.3.1 +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip diff --git a/microservices/transaction-ms/mvnw b/microservices/transaction-ms/mvnw new file mode 100644 index 0000000..ba9212a --- /dev/null +++ b/microservices/transaction-ms/mvnw @@ -0,0 +1,250 @@ +#!/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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.1 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl="${value-}" ;; + distributionSha256Sum) distributionSha256Sum="${value-}" ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/microservices/transaction-ms/mvnw.cmd b/microservices/transaction-ms/mvnw.cmd new file mode 100644 index 0000000..406932d --- /dev/null +++ b/microservices/transaction-ms/mvnw.cmd @@ -0,0 +1,146 @@ +<# : batch portion +@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 Apache Maven Wrapper startup batch script, version 3.3.1 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/microservices/transaction-ms/pom.xml b/microservices/transaction-ms/pom.xml new file mode 100644 index 0000000..0ee67cb --- /dev/null +++ b/microservices/transaction-ms/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + com.yape + microservices + 1.0.0-SNAPSHOT + + transaction-ms + 0.0.1-SNAPSHOT + transaction-ms + Transaction-ms for Spring Boot + + 17 + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.kafka + spring-kafka + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.postgresql + postgresql + runtime + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-cache + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.kafka + spring-kafka-test + test + + + org.springframework + spring-context + 6.1.8 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/TransactionMsApplication.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/TransactionMsApplication.java new file mode 100644 index 0000000..77fd30e --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/TransactionMsApplication.java @@ -0,0 +1,13 @@ +package com.yape.transaction; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TransactionMsApplication { + + public static void main(String[] args) { + SpringApplication.run(TransactionMsApplication.class, args); + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/exception/NotFoundException.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/exception/NotFoundException.java new file mode 100644 index 0000000..2b46714 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package com.yape.transaction.application.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String msg) { + super(msg); + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/GetTransactionQuery.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/GetTransactionQuery.java new file mode 100644 index 0000000..4ee1d01 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/GetTransactionQuery.java @@ -0,0 +1,12 @@ +package com.yape.transaction.application.port.in; + +import com.yape.transaction.domain.Transaction; + +import java.util.List; +import java.util.UUID; + +public interface GetTransactionQuery { + Transaction getTransactionByCode(UUID code); + + List getAllTransactions(); +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/RegisterTransactionCommand.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/RegisterTransactionCommand.java new file mode 100644 index 0000000..d735b9f --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/RegisterTransactionCommand.java @@ -0,0 +1,21 @@ +package com.yape.transaction.application.port.in; + +import com.yape.transaction.domain.Transaction; +import lombok.Builder; +import lombok.Value; + +import java.math.BigDecimal; +import java.util.UUID; + +public interface RegisterTransactionCommand { + Transaction execute(Command command); + @Value + @Builder + class Command { + UUID accountExternalIdDebit; + UUID accountExternalIdCredit; + Integer transferTypeId; + BigDecimal value; + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/UpdateTransactionCommand.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/UpdateTransactionCommand.java new file mode 100644 index 0000000..b9bf127 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/in/UpdateTransactionCommand.java @@ -0,0 +1,19 @@ +package com.yape.transaction.application.port.in; + +import com.yape.transaction.domain.type.TransactionStatusEnum; +import lombok.Builder; +import lombok.Value; + +import java.util.UUID; + +public interface UpdateTransactionCommand { + void execute(Command command); + @Value + @Builder + class Command { + Long id; + UUID code; + TransactionStatusEnum status; + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/AntiFraudEdaRepository.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/AntiFraudEdaRepository.java new file mode 100644 index 0000000..c97fccd --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/AntiFraudEdaRepository.java @@ -0,0 +1,7 @@ +package com.yape.transaction.application.port.out; + +import com.yape.transaction.domain.Transaction; + +public interface AntiFraudEdaRepository { + void validate(Transaction transaction); +} \ No newline at end of file diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/CacheRepository.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/CacheRepository.java new file mode 100644 index 0000000..8824555 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/CacheRepository.java @@ -0,0 +1,12 @@ +package com.yape.transaction.application.port.out; + +import com.yape.transaction.domain.Transaction; + + +public interface CacheRepository { + Transaction findTransactionById(Long id); + void removeTransactionById(Long id); + Transaction saveTransaction(Transaction transaction); +} + + diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/TransactionRepository.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/TransactionRepository.java new file mode 100644 index 0000000..8bad1ee --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/port/out/TransactionRepository.java @@ -0,0 +1,25 @@ +package com.yape.transaction.application.port.out; + +import com.yape.transaction.infra.out.adapter.data.model.TransactionDataModel; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface TransactionRepository extends JpaRepository { + Optional findOptionalByCode(UUID code); + Optional findOptionalById(Long id); + + @Modifying + @Query("update transaction t set t.status = :status, t.updateAt = :updateAt where t.id = :id") + void updateStatus(@Param(value = "id") Long id, @Param(value = "status") String status, @Param(value = "updateAt") LocalDateTime updateAt); +} + + diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/GetTransactionUseCase.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/GetTransactionUseCase.java new file mode 100644 index 0000000..ca8daa2 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/GetTransactionUseCase.java @@ -0,0 +1,34 @@ +package com.yape.transaction.application.usecase; + +import com.yape.transaction.application.exception.NotFoundException; +import com.yape.transaction.application.port.in.GetTransactionQuery; +import com.yape.transaction.application.port.out.TransactionRepository; +import com.yape.transaction.domain.Transaction; +import com.yape.transaction.infra.out.adapter.data.model.TransactionDataModel; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +public class GetTransactionUseCase implements GetTransactionQuery { + private final TransactionRepository repository; + + public GetTransactionUseCase(TransactionRepository repository){ + this.repository = repository; + } + @Override + public Transaction getTransactionByCode(UUID code) { + return this.repository.findOptionalByCode(code) + .orElseThrow(() -> new NotFoundException("Transaction not found")) + .toDomain(); + } + @Override + public List getAllTransactions() { + return this.repository.findAll().stream() + .map(TransactionDataModel::toDomain) + .collect(Collectors.toList()); + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/RegisterTransactionUseCase.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/RegisterTransactionUseCase.java new file mode 100644 index 0000000..1eb9c47 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/RegisterTransactionUseCase.java @@ -0,0 +1,49 @@ +package com.yape.transaction.application.usecase; + +import com.yape.transaction.application.port.in.RegisterTransactionCommand; +import com.yape.transaction.application.port.out.AntiFraudEdaRepository; +import com.yape.transaction.application.port.out.CacheRepository; +import com.yape.transaction.application.port.out.TransactionRepository; +import com.yape.transaction.domain.Transaction; +import com.yape.transaction.domain.type.TransactionStatusEnum; +import com.yape.transaction.domain.type.TransactionTypeEnum; +import com.yape.transaction.domain.type.TransferTypeEnum; +import com.yape.transaction.infra.out.adapter.data.model.TransactionDataModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +@Slf4j +public class RegisterTransactionUseCase implements RegisterTransactionCommand { + private final TransactionRepository repository; + private final AntiFraudEdaRepository antiFraudEdaRepository; + private final CacheRepository cacheRepository; + + public RegisterTransactionUseCase(TransactionRepository repository, AntiFraudEdaRepository antiFraudEdaRepository, CacheRepository cacheRepository) { + this.repository = repository; + this.antiFraudEdaRepository = antiFraudEdaRepository; + this.cacheRepository = cacheRepository; + } + + @Override + public Transaction execute(Command command) { + TransactionDataModel entity = TransactionDataModel.builder() + .code(UUID.randomUUID()) + .accountExternalIdCredit(command.getAccountExternalIdCredit()) + .accountExternalIdDebit(command.getAccountExternalIdDebit()) + .value(command.getValue()) + .transferType(TransferTypeEnum.findByValue(command.getTransferTypeId()).name()) + .status(TransactionStatusEnum.PENDING.name()) + .type( TransactionTypeEnum.TRANSFER.name()) + .createAt(LocalDateTime.now()) + .build(); + Transaction transaction = this.repository.save(entity).toDomain(); + this.cacheRepository.saveTransaction(transaction); + this.antiFraudEdaRepository.validate(transaction); + + return transaction; + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/UpdateTransactionUseCase.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/UpdateTransactionUseCase.java new file mode 100644 index 0000000..98ffa45 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/application/usecase/UpdateTransactionUseCase.java @@ -0,0 +1,34 @@ +package com.yape.transaction.application.usecase; + +import com.yape.transaction.application.port.in.UpdateTransactionCommand; +import com.yape.transaction.application.port.out.CacheRepository; +import com.yape.transaction.application.port.out.TransactionRepository; +import com.yape.transaction.domain.Transaction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Service +@Slf4j +public class UpdateTransactionUseCase implements UpdateTransactionCommand { + private final TransactionRepository repository; + private final CacheRepository cacheRepository; + + public UpdateTransactionUseCase(TransactionRepository repository, CacheRepository cacheRepository) { + this.repository = repository; + this.cacheRepository = cacheRepository; + } + + @Transactional + @Override + public void execute(Command command) { + Transaction transaction = this.cacheRepository.findTransactionById(command.getId()); + this.repository.updateStatus(transaction.getId(), command.getStatus().name(), LocalDateTime.now()); + log.info("updated id:{}", transaction.getId()); + + this.cacheRepository.removeTransactionById(transaction.getId()); + } + +} \ No newline at end of file diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/CacheConfig.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/CacheConfig.java new file mode 100644 index 0000000..b792bfd --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/CacheConfig.java @@ -0,0 +1,10 @@ +package com.yape.transaction.config; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/KafkaConfig.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/KafkaConfig.java new file mode 100644 index 0000000..6933150 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/KafkaConfig.java @@ -0,0 +1,53 @@ +package com.yape.transaction.config; + +import com.yape.transaction.config.properties.SpringConfigurationProperties; +import com.yape.transaction.infra.in.adapter.kafka.model.TransactionInModel; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer; +import org.springframework.kafka.support.serializer.JsonDeserializer; + +import java.util.HashMap; +import java.util.Map; + +@EnableKafka +@Configuration +@Slf4j +public class KafkaConfig { + + private final SpringConfigurationProperties springConfigurationProperties; + private static final String EARLIEST = "earliest"; + public KafkaConfig(SpringConfigurationProperties springConfigurationProperties) { + this.springConfigurationProperties = springConfigurationProperties; + } + + @Bean + public ConsumerFactory transactionConsumerConfigs() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, springConfigurationProperties.getKafka().getBootstrapServers()); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ErrorHandlingDeserializer.class); + props.put(ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS, StringDeserializer.class); + props.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, StringDeserializer.class); + + props.put(ConsumerConfig.GROUP_ID_CONFIG, springConfigurationProperties.getKafka().getConsumer().getGroupId()); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, EARLIEST); + return new DefaultKafkaConsumerFactory<>(props, new ErrorHandlingDeserializer<>(), new ErrorHandlingDeserializer<>(new JsonDeserializer<>(TransactionInModel.class))); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory transactionKafkaListenerContainerFactory() { + ConcurrentKafkaListenerContainerFactory + factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(transactionConsumerConfigs()); + return factory; + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorCode.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorCode.java new file mode 100644 index 0000000..76ed6f8 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorCode.java @@ -0,0 +1,26 @@ +package com.yape.transaction.config.exception; + +import lombok.Getter; + +public enum ErrorCode { + KAFKA_EXCEPTION(109, "Kafka Error", "KAFKA_EXCEPTION"), + INTERNAL_ERROR(108,"Internal Error","INTERNAL_ERROR"), + TRANSACTION_NOT_FOUND(110,"Transaction Not Found","NOT_FOUND"); + + private final int value; + @Getter + private final String reasonPhrase; + @Getter + private final String code; + + ErrorCode(int value, String reasonPhrase, String code) { + this.value = value; + this.reasonPhrase = reasonPhrase; + this.code = code; + } + + public int value() { + return this.value; + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorHandler.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorHandler.java new file mode 100644 index 0000000..15b0145 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorHandler.java @@ -0,0 +1,47 @@ +package com.yape.transaction.config.exception; + +import com.yape.transaction.application.exception.NotFoundException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@Slf4j +@ControllerAdvice +public class ErrorHandler { + + @ExceptionHandler(Throwable.class) + public ResponseEntity handle(Throwable ex) { + log.error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), ex); + return buildResponseError(HttpStatus.INTERNAL_SERVER_ERROR, ErrorCode.INTERNAL_ERROR); + } + + @ExceptionHandler(NotFoundException.class) + public ResponseEntity handle(NotFoundException ex) { + log.error(ErrorCode.TRANSACTION_NOT_FOUND.getCode(), ex); + return buildResponseError(HttpStatus.NOT_FOUND, ErrorCode.TRANSACTION_NOT_FOUND); + } + + @ExceptionHandler(KafkaException.class) + public ResponseEntity handle(KafkaException ex) { + log.error(ex.getCode().getCode(), ex); + return buildResponseError(HttpStatus.SERVICE_UNAVAILABLE, ex.getCode()); + } + + private ResponseEntity buildCustomResponseError(HttpStatus httpStatus, ErrorCode errorCode, String customDescription) { + final var response = ErrorResponse.builder() + .errorInternalCode(errorCode.value()) + .errorDescription(customDescription) + .errorCode(errorCode.getCode()) + .build(); + + return new ResponseEntity<>(response, httpStatus); + } + + private ResponseEntity buildResponseError(HttpStatus httpStatus, ErrorCode errorCode) { + return buildCustomResponseError(httpStatus, errorCode, errorCode.getReasonPhrase()); + } + +} + diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorResponse.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorResponse.java new file mode 100644 index 0000000..1e4ea8f --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/ErrorResponse.java @@ -0,0 +1,15 @@ +package com.yape.transaction.config.exception; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; + +@Builder +public class ErrorResponse { + @JsonProperty + int errorInternalCode; + @JsonProperty + String errorDescription; + @JsonProperty + String errorCode; +} + diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/GenericException.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/GenericException.java new file mode 100644 index 0000000..1d30a16 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/GenericException.java @@ -0,0 +1,15 @@ +package com.yape.transaction.config.exception; + +public abstract class GenericException extends RuntimeException { + private final ErrorCode errorCode; + + public GenericException(ErrorCode errorCode) { + super(errorCode.getReasonPhrase()); + this.errorCode = errorCode; + } + + public ErrorCode getCode() { + return this.errorCode; + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/KafkaException.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/KafkaException.java new file mode 100644 index 0000000..dcf78ab --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/exception/KafkaException.java @@ -0,0 +1,7 @@ +package com.yape.transaction.config.exception; + +public class KafkaException extends GenericException { + public KafkaException(ErrorCode ec){ + super(ec); + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/ConsumerKafkaProperties.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/ConsumerKafkaProperties.java new file mode 100644 index 0000000..a763d0f --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/ConsumerKafkaProperties.java @@ -0,0 +1,8 @@ +package com.yape.transaction.config.properties; + +import lombok.Data; + +@Data +public class ConsumerKafkaProperties { + private String groupId; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/KafkaProperties.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/KafkaProperties.java new file mode 100644 index 0000000..d57e749 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/KafkaProperties.java @@ -0,0 +1,13 @@ +package com.yape.transaction.config.properties; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +public class KafkaProperties { + private String bootstrapServers; + private TopicProperties topic; + private ConsumerKafkaProperties consumer; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/SpringConfigurationProperties.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/SpringConfigurationProperties.java new file mode 100644 index 0000000..95f688d --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/SpringConfigurationProperties.java @@ -0,0 +1,16 @@ +package com.yape.transaction.config.properties; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "spring") +@AllArgsConstructor +@NoArgsConstructor +@Data +public class SpringConfigurationProperties { + private KafkaProperties kafka; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/TopicProperties.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/TopicProperties.java new file mode 100644 index 0000000..7fe06f9 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/config/properties/TopicProperties.java @@ -0,0 +1,12 @@ +package com.yape.transaction.config.properties; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopicProperties { + private String antifraud; + private String transaction; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/Transaction.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/Transaction.java new file mode 100644 index 0000000..62e91e8 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/Transaction.java @@ -0,0 +1,29 @@ +package com.yape.transaction.domain; + +import com.yape.transaction.domain.type.TransactionStatusEnum; +import com.yape.transaction.domain.type.TransactionTypeEnum; +import com.yape.transaction.domain.type.TransferTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Value; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Builder +@Value +@AllArgsConstructor +public class Transaction { + Long id; + UUID code; + UUID accountExternalIdDebit; + UUID accountExternalIdCredit; + TransferTypeEnum transferType; + TransactionTypeEnum type; + TransactionStatusEnum status; + + BigDecimal value; + LocalDateTime createAt; + LocalDateTime updateAt; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransactionStatusEnum.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransactionStatusEnum.java new file mode 100644 index 0000000..5508a39 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransactionStatusEnum.java @@ -0,0 +1,7 @@ +package com.yape.transaction.domain.type; + +public enum TransactionStatusEnum { + PENDING, + APPROVED, + REJECTED +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransactionTypeEnum.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransactionTypeEnum.java new file mode 100644 index 0000000..6a62f5c --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransactionTypeEnum.java @@ -0,0 +1,7 @@ +package com.yape.transaction.domain.type; + +public enum TransactionTypeEnum { + DEPOSIT, + WITHDRAW, + TRANSFER +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransferTypeEnum.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransferTypeEnum.java new file mode 100644 index 0000000..fab5cae --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/domain/type/TransferTypeEnum.java @@ -0,0 +1,18 @@ +package com.yape.transaction.domain.type; + +import java.util.Arrays; + +public enum TransferTypeEnum { + ONUS(1), + INTERBANK(2); + private final int value; + TransferTypeEnum(int value){ + this.value = value; + } + public static TransferTypeEnum findByValue(int value) { + return Arrays.stream(TransferTypeEnum.values()) + .filter(e -> e.value == value) + .findAny() + .orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", value))); + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/kafka/TransactionListenerAdapter.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/kafka/TransactionListenerAdapter.java new file mode 100644 index 0000000..524e1b1 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/kafka/TransactionListenerAdapter.java @@ -0,0 +1,40 @@ +package com.yape.transaction.infra.in.adapter.kafka; + +import com.yape.transaction.application.port.in.UpdateTransactionCommand; +import com.yape.transaction.config.exception.ErrorCode; +import com.yape.transaction.infra.in.adapter.kafka.model.TransactionInModel; +import com.yape.transaction.config.exception.KafkaException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class TransactionListenerAdapter { + private final UpdateTransactionCommand updateTransactionCommand; + + public TransactionListenerAdapter(UpdateTransactionCommand updateTransactionCommand) { + this.updateTransactionCommand = updateTransactionCommand; + } + + @KafkaListener( + topics = "${spring.kafka.topic.transaction}", + groupId = "${spring.kafka.consumer.group-id}", + containerFactory = "transactionKafkaListenerContainerFactory" + + ) + public void listen(TransactionInModel message) { + log.info("TransactionListenerAdapter message received from kafka {}", message); + try { + UpdateTransactionCommand.Command command = UpdateTransactionCommand.Command.builder() + .code(message.getCode()) + .id(message.getId()) + .status(message.getStatus()) + .build(); + this.updateTransactionCommand.execute(command); + } catch (Exception ex) { + log.error("Occurred an error in kafka ", ex); + throw new KafkaException(ErrorCode.KAFKA_EXCEPTION); + } + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/kafka/model/TransactionInModel.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/kafka/model/TransactionInModel.java new file mode 100644 index 0000000..2353be5 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/kafka/model/TransactionInModel.java @@ -0,0 +1,21 @@ +package com.yape.transaction.infra.in.adapter.kafka.model; + +import com.yape.transaction.domain.type.TransactionStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.UUID; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class TransactionInModel implements Serializable { + private Long id; + private UUID code; + private TransactionStatusEnum status; + +} \ No newline at end of file diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/TransactionControllerAdapter.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/TransactionControllerAdapter.java new file mode 100644 index 0000000..52ec2d3 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/TransactionControllerAdapter.java @@ -0,0 +1,58 @@ +package com.yape.transaction.infra.in.adapter.rest; + +import com.yape.transaction.application.port.in.GetTransactionQuery; +import com.yape.transaction.application.port.in.RegisterTransactionCommand; +import com.yape.transaction.infra.in.adapter.rest.model.TransactionModelRequest; +import com.yape.transaction.infra.in.adapter.rest.model.TransactionModelResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/transaction-ms") +public class TransactionControllerAdapter { + + private final GetTransactionQuery getTransactionQuery; + private final RegisterTransactionCommand registerTransactionCommand; + + public TransactionControllerAdapter(GetTransactionQuery getTransactionQuery, + RegisterTransactionCommand registerTransactionCommand){ + this.getTransactionQuery = getTransactionQuery; + this.registerTransactionCommand = registerTransactionCommand; + } + @GetMapping("/{code}") + public ResponseEntity getTransactionByCode(@PathVariable UUID code) { + TransactionModelResponse response = TransactionModelResponse.fromDomain(this.getTransactionQuery.getTransactionByCode(code)); + return ResponseEntity.ok(response); + } + + @GetMapping + public ResponseEntity> getAllTransactions() { + List response = this.getTransactionQuery.getAllTransactions().stream() + .map(TransactionModelResponse::fromDomain) + .collect(Collectors.toList()); + return ResponseEntity.ok(response); + } + + @PostMapping + public ResponseEntity register(@RequestBody TransactionModelRequest request) { + RegisterTransactionCommand.Command command = RegisterTransactionCommand.Command.builder() + .accountExternalIdCredit(request.getAccountExternalIdCredit()) + .accountExternalIdDebit(request.getAccountExternalIdDebit()) + .transferTypeId(request.getTransferTypeId()) + .value(request.getValue()) + .build(); + TransactionModelResponse response = TransactionModelResponse.fromDomain(this.registerTransactionCommand.execute(command)); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionModelRequest.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionModelRequest.java new file mode 100644 index 0000000..2c57b20 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionModelRequest.java @@ -0,0 +1,20 @@ +package com.yape.transaction.infra.in.adapter.rest.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.UUID; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TransactionModelRequest { + private UUID accountExternalIdDebit; + private UUID accountExternalIdCredit; + private Integer transferTypeId; + private BigDecimal value; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionModelResponse.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionModelResponse.java new file mode 100644 index 0000000..8583034 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionModelResponse.java @@ -0,0 +1,34 @@ +package com.yape.transaction.infra.in.adapter.rest.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.yape.transaction.domain.Transaction; +import lombok.Builder; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class TransactionModelResponse { + private UUID transactionExternalId; + private TransactionType transactionType; + private TransactionStatus transactionStatus; + private BigDecimal value; + private LocalDateTime createAt; + + public static TransactionModelResponse fromDomain(Transaction transaction) { + return TransactionModelResponse.builder() + .transactionExternalId(transaction.getCode()) + .transactionType(TransactionType.builder().name(transaction.getType().name()).build()) + .transactionStatus(TransactionStatus.builder().name(transaction.getStatus().name()).build()) + .value(transaction.getValue()) + .createAt(transaction.getCreateAt()) + .build(); + } + +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionStatus.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionStatus.java new file mode 100644 index 0000000..b1a8365 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionStatus.java @@ -0,0 +1,17 @@ +package com.yape.transaction.infra.in.adapter.rest.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class TransactionStatus { + private String name; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionType.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionType.java new file mode 100644 index 0000000..32f13e4 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/in/adapter/rest/model/TransactionType.java @@ -0,0 +1,17 @@ +package com.yape.transaction.infra.in.adapter.rest.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class TransactionType { + private String name; +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/cache/CacheRepositoryAdapter.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/cache/CacheRepositoryAdapter.java new file mode 100644 index 0000000..29d03cf --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/cache/CacheRepositoryAdapter.java @@ -0,0 +1,39 @@ +package com.yape.transaction.infra.out.adapter.cache; + +import com.yape.transaction.application.exception.NotFoundException; +import com.yape.transaction.application.port.out.CacheRepository; +import com.yape.transaction.application.port.out.TransactionRepository; +import com.yape.transaction.domain.Transaction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class CacheRepositoryAdapter implements CacheRepository { + private final TransactionRepository repository; + public CacheRepositoryAdapter(TransactionRepository repository){ + this.repository = repository; + } + @Cacheable(cacheNames= "transactionCache", key="#id") + @Override + public Transaction findTransactionById(Long id){ + return this.repository.findOptionalById(id) + .orElseThrow(() -> new NotFoundException("transaction not found")).toDomain(); + } + + @CacheEvict(cacheNames = "transactionCache", key = "#id") + @Override + public void removeTransactionById(Long id) { + log.info("deleting from cache id:{}", id); + } + + @CachePut(cacheNames="transactionCache", key="#transaction.id") + @Override + public Transaction saveTransaction(Transaction transaction) { + log.info("saving cache id:{}", transaction.getId()); + return transaction; + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/data/model/TransactionDataModel.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/data/model/TransactionDataModel.java new file mode 100644 index 0000000..7376311 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/data/model/TransactionDataModel.java @@ -0,0 +1,60 @@ +package com.yape.transaction.infra.out.adapter.data.model; + +import com.yape.transaction.domain.Transaction; +import com.yape.transaction.domain.type.TransactionStatusEnum; +import com.yape.transaction.domain.type.TransactionTypeEnum; +import com.yape.transaction.domain.type.TransferTypeEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.UUID; + +@Builder +@Entity(name = "transaction") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TransactionDataModel { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name ="code") + private UUID code; + @Column(name = "account_external_id_debit") + private UUID accountExternalIdDebit; + @Column(name = "account_external_id_credit") + private UUID accountExternalIdCredit; + private String transferType; + private String type; + private String status; + private BigDecimal value; + @Column(name = "created_at") + private LocalDateTime createAt; + @Column(name = "updated_at") + private LocalDateTime updateAt; + + public Transaction toDomain() { + return Transaction.builder() + .id(id) + .code(code) + .accountExternalIdDebit(accountExternalIdDebit) + .accountExternalIdCredit(accountExternalIdCredit) + .transferType(TransferTypeEnum.valueOf(transferType)) + .type(TransactionTypeEnum.valueOf(type)) + .status(TransactionStatusEnum.valueOf(status)) + .value(value) + .createAt(createAt) + .updateAt(updateAt) + .build(); + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/kafka/AntiFraudKafkaAdapter.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/kafka/AntiFraudKafkaAdapter.java new file mode 100644 index 0000000..35f3c28 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/kafka/AntiFraudKafkaAdapter.java @@ -0,0 +1,37 @@ +package com.yape.transaction.infra.out.adapter.kafka; + +import com.yape.transaction.application.port.out.AntiFraudEdaRepository; +import com.yape.transaction.config.exception.ErrorCode; +import com.yape.transaction.config.properties.SpringConfigurationProperties; +import com.yape.transaction.domain.Transaction; +import com.yape.transaction.config.exception.KafkaException; +import com.yape.transaction.infra.out.adapter.kafka.model.TransactionOutModel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class AntiFraudKafkaAdapter implements AntiFraudEdaRepository { + private final KafkaTemplate kafkaTemplate; + private final SpringConfigurationProperties springConfigurationProperties; + + public AntiFraudKafkaAdapter(KafkaTemplate kafkaTemplate, SpringConfigurationProperties springConfigurationProperties) { + this.kafkaTemplate = kafkaTemplate; + this.springConfigurationProperties = springConfigurationProperties; + } + + @Override + public void validate(Transaction transaction) { + TransactionOutModel transactionModel = TransactionOutModel.fromDomain(transaction); + try { + log.info("sending to Anfifraud, transaction id:{}", transactionModel.getId() ); + this.kafkaTemplate.send(springConfigurationProperties.getKafka().getTopic().getAntifraud(), transactionModel); + this.kafkaTemplate.flush(); + } catch (Exception e) { + log.error("Occurred an error in kafka ", e); + throw new KafkaException(ErrorCode.KAFKA_EXCEPTION); + } + + } +} diff --git a/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/kafka/model/TransactionOutModel.java b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/kafka/model/TransactionOutModel.java new file mode 100644 index 0000000..7ac5925 --- /dev/null +++ b/microservices/transaction-ms/src/main/java/com/yape/transaction/infra/out/adapter/kafka/model/TransactionOutModel.java @@ -0,0 +1,33 @@ +package com.yape.transaction.infra.out.adapter.kafka.model; + +import com.yape.transaction.domain.Transaction; +import com.yape.transaction.domain.type.TransactionStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.UUID; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TransactionOutModel implements Serializable { + private Long id; + private UUID code; + private TransactionStatusEnum status; + private BigDecimal value; + + public static TransactionOutModel fromDomain(Transaction transaction) { + return TransactionOutModel.builder() + .id(transaction.getId()) + .code(transaction.getCode()) + .status(transaction.getStatus()) + .value(transaction.getValue()) + .build(); + } + + } diff --git a/microservices/transaction-ms/src/main/resources/application.properties b/microservices/transaction-ms/src/main/resources/application.properties new file mode 100644 index 0000000..cbc9eb7 --- /dev/null +++ b/microservices/transaction-ms/src/main/resources/application.properties @@ -0,0 +1,21 @@ +spring.application.name=transaction-ms + +spring.datasource.driver-class-name=org.postgresql.Driver +#docker +spring.datasource.url=jdbc:postgresql://localhost:5432/yapebd + +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.datasource.initialize=true +spring.jpa.hibernate.ddl-auto=none +spring.kafka.bootstrap-servers=localhost:9092 +spring.kafka.topic.antifraud=topic-antifraud-review +spring.kafka.topic.transaction=topic-transaction-update +spring.kafka.producer.properties.spring.json.add.type.headers=false +spring.kafka.producer.key-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.consumer.group-id=bcp-group + +spring.jpa.database=POSTGRESQL +spring.datasource.platform=postgres +spring.jpa.show-sql=true diff --git a/microservices/transaction-ms/src/test/java/com/yape/transaction/TransactionMsApplicationTests.java b/microservices/transaction-ms/src/test/java/com/yape/transaction/TransactionMsApplicationTests.java new file mode 100644 index 0000000..b11b0ef --- /dev/null +++ b/microservices/transaction-ms/src/test/java/com/yape/transaction/TransactionMsApplicationTests.java @@ -0,0 +1,13 @@ +package com.yape.transaction; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class TransactionMsApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/script-bd/script-create_table.sql b/script-bd/script-create_table.sql new file mode 100644 index 0000000..66379f5 --- /dev/null +++ b/script-bd/script-create_table.sql @@ -0,0 +1,18 @@ +CREATE TABLE transaction +( + "id" BIGSERIAL PRIMARY KEY, + "code" UUID NOT NULL, + "account_external_id_debit" UUID NOT NULL, + "account_external_id_credit" UUID NOT NULL, + "value" NUMERIC NOT NULL, + "tranfer_type" VARCHAR(50) NOT NULL, + "type" VARCHAR(50) NOT NULL, + "status" VARCHAR(50) NOT NULL, + "created_at" TIMESTAMP NOT NULL, + "updated_at" TIMESTAMP, + CONSTRAINT "code_unique" UNIQUE (code) +); + +ALTER TABLE transaction ADD CONSTRAINT status_check check(status in('PENDING', 'APPROVED', 'REJECTED')); +ALTER TABLE transaction ADD CONSTRAINT transfer_type_check check(tranfer_type in('ONUS', 'INTERBANK')); +ALTER TABLE transaction ADD CONSTRAINT type_check check(type in('DEPOSIT', 'WITHDRAW', 'TRANSFER')); \ No newline at end of file