commit 64a5253a6976dbb7dd6fc8774af6910dea543174 Author: Ramon Ramirez Date: Sat Jan 10 19:05:08 2026 -0400 first commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..94810d0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91a800a --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ +# TLS Certificates +.certs/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..8dea6c2 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip diff --git a/README.md b/README.md new file mode 100644 index 0000000..b3cbb25 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# bus-legal-customer-product-directory + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: + +```shell script +./mvnw quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at . + +## Packaging and running the application + +The application can be packaged using: + +```shell script +./mvnw package +``` + +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: + +```shell script +./mvnw package -Dquarkus.package.jar.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: + +```shell script +./mvnw package -Dnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: + +```shell script +./mvnw package -Dnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/bus-legal-customer-product-directory-1.0-native-quarkus-jdk17-runner` + +If you want to learn more about building native executables, please consult . diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..bd8896b --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/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 +# +# http://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.4 +# +# 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 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.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${scriptName#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_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/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 + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..92450f9 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : 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 http://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.4 +@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 -eq $False) { "/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_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::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 + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -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/pom.xml b/pom.xml new file mode 100644 index 0000000..b7139a8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + com.banesco + bus-legal-customer-product-directory + 1.0-native-quarkus-jdk17 + bus-legal-customer-product-directory + API Business - Read legal customer accounts + + + 17 + 17 + 17 + 3.14.1 + 1.18.2 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.22.3 + 1.18.42 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + ${quarkus.platform.group-id} + quarkus-camel-bom + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-rest-jackson + + + io.quarkus + quarkus-hibernate-validator + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-config-yaml + + + io.quarkus + quarkus-rest-client + + + io.quarkus + quarkus-rest-client-jackson + + + io.quarkus + quarkus-smallrye-health + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + + org.eclipse.jkube + openshift-maven-plugin + ${compiler-plugin-openshift.version} + + + + + maven-volumen + /root/.m2 + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + native-image-agent + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + + + + + native + + + native + + + + native + true + + + + + src/main/resources + + application-dev.yaml + application-local.yaml + + + + + + + \ No newline at end of file diff --git a/scripts/native/Dockerfile b/scripts/native/Dockerfile new file mode 100644 index 0000000..c3b34be --- /dev/null +++ b/scripts/native/Dockerfile @@ -0,0 +1,31 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/rec-legal-customer-product-directory . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/rec-legal-customer-product-directory +# +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +RUN mkdir -p /work +ENV TZ="America/Caracas" +ENV LANGUAGE='en_US:en' +VOLUME /tmp +COPY /file/*-runner /work/app +RUN chmod -R 775 /work +RUN ls -ltra /work/ +EXPOSE 8080 +WORKDIR /work/ + +ENTRYPOINT ["./app", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/scripts/native/file/bus-legal-customer-product-directory-1.0-native-quarkus-jdk17-runner b/scripts/native/file/bus-legal-customer-product-directory-1.0-native-quarkus-jdk17-runner new file mode 100644 index 0000000..0a42899 Binary files /dev/null and b/scripts/native/file/bus-legal-customer-product-directory-1.0-native-quarkus-jdk17-runner differ diff --git a/src/main/docker/Dockerfile.jvm b/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..90aaf5a --- /dev/null +++ b/src/main/docker/Dockerfile.jvm @@ -0,0 +1,98 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/bus-legal-customer-product-directory-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/bus-legal-customer-product-directory-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/bus-legal-customer-product-directory-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-17:1.23 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/src/main/docker/Dockerfile.legacy-jar b/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..505741d --- /dev/null +++ b/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,94 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/bus-legal-customer-product-directory-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/bus-legal-customer-product-directory-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/bus-legal-customer-product-directory-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-17:1.23 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/src/main/docker/Dockerfile.native b/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..feb3a2d --- /dev/null +++ b/src/main/docker/Dockerfile.native @@ -0,0 +1,29 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/bus-legal-customer-product-directory . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/bus-legal-customer-product-directory +# +# The ` registry.access.redhat.com/ubi9/ubi-minimal:9.7` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`. +### +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/src/main/docker/Dockerfile.native-micro b/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..abda865 --- /dev/null +++ b/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,32 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/bus-legal-customer-product-directory . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/bus-legal-customer-product-directory +# +# The `quay.io/quarkus/ubi9-quarkus-micro-image:2.0` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`. +### +FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/src/main/java/com/banesco/common/application/helper/MessageResponseHelper.java b/src/main/java/com/banesco/common/application/helper/MessageResponseHelper.java new file mode 100644 index 0000000..0654e92 --- /dev/null +++ b/src/main/java/com/banesco/common/application/helper/MessageResponseHelper.java @@ -0,0 +1,203 @@ +package com.banesco.common.application.helper; + +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.ErrorMapping; +import com.banesco.common.domain.model.StatusResponse; +import com.banesco.common.infrastructure.config.MessagesConfig; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; +import lombok.extern.slf4j.Slf4j; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@ApplicationScoped +@RegisterForReflection +public class MessageResponseHelper { + + private final MessagesConfig messagesConfig; + private final Map errorMappings; + private static final String ERROR_DEFAULT = "default"; + private static final String SUCCESS_DEFAULT = "200"; + private static final String ERROR_FILE_PATH = "errors-mapping/errors.json"; + private final ObjectMapper objectMapper; + + @Inject + public MessageResponseHelper( + ObjectMapper objectMapper, + MessagesConfig messagesConfig + ) { + this.objectMapper = objectMapper; + this.messagesConfig = messagesConfig; + this.errorMappings = initializeErrorMappings(); + } + + public Response handleException(HttpStatusCodeException exception) { + log.error("Error interno controlado: {}", exception.getMessage()); + return buildErrorResponse(exception); + } + + public Response handleGenericException(Exception exception) { + log.error("Error interno no controlado: {}", exception.getMessage()); + return buildErrorResponse(HttpStatusCodeException.internalServer("500")); + } + + public StatusResponse createStatusResponse(String code) { + ErrorMapping successMapping = getError(code); + + return StatusResponse.builder() + .statusCode(successMapping.getStatusCode()) + .message(successMapping.getDescription()) + .build(); + } + + public StatusResponse createError( + ErrorMapping mapping, + String fieldPath + ) { + String message = mapping.getDescription(); + + if (fieldPath != null && message != null && message.contains("%s")) { + message = String.format(message, fieldPath); + } + + return StatusResponse.builder() + .statusCode(mapping.getStatusCode()) + .message(message) + .build(); + } + + public ApiResponse buildServiceUnavailableResponse() { + return new ApiResponse<>(createStatusResponse("503")); + } + + private ErrorMapping getError(String errorCode) { + return errorMappings.getOrDefault( + errorCode, errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping()) + ); + } + + private Response buildErrorResponse(HttpStatusCodeException exception) { + ErrorMapping mapping = errorMappings.getOrDefault( + exception.getErrorCode(), + errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping()) + ); + StatusResponse status = createError( + mapping, exception.getFieldPath() + ); + + log.error( + "[{}] Message {} -> {}", + exception.getExceptionType(), + exception.getErrorCode(), + status.getMessage() + ); + + return Response.status(mapping.getHttpCode()) + .entity(new ApiResponse<>(status)) + .build(); + } + + private Map initializeErrorMappings() { + try { + String json; + + if (isReadingFromProps()) { + json = messagesConfig.getErrorMessagesJson(); + log.info("Cargando mensajes de errores desde properties"); + } else { + json = loadFromJsonFile(); + log.info("Cargando mensajes de errores desde archivo JSON"); + } + + if (json == null || json.isEmpty()) { + log.warn("No se encontró JSON de errores"); + return createDefaultMappings(); + } + + List mappings = objectMapper.readValue(json, new TypeReference>() {}); + Map result = new ConcurrentHashMap<>(); + + for (ErrorMapping mapping : mappings) { + if (mapping.getBackendCode() != null && !mapping.getBackendCode().trim().isEmpty()) { + if (result.containsKey(mapping.getBackendCode())) { + log.warn("Clave duplicada encontrada en mappings de errores: {}", mapping.getBackendCode()); + } else { + result.put(mapping.getBackendCode(), mapping); + } + } else { + log.warn("Ignorando mapping sin backendCode válido: {}", mapping.getDescription()); + } + } + + log.info("Mappings de errores cargados exitosamente, total válidos: {}", result.size()); + return result; + } catch (Exception e) { + log.error("Error cargando mappings de errores: {}", e.getMessage()); + return createDefaultMappings(); + } + } + + private String loadFromJsonFile() { + try (InputStream is = MessageResponseHelper.class.getClassLoader().getResourceAsStream(ERROR_FILE_PATH)) { + if (is == null) { + log.warn("No se encontró el archivo de errores: {}", ERROR_FILE_PATH); + return ""; + } + return new String(is.readAllBytes()); + } catch (Exception e) { + log.error("Error leyendo archivo de errores: {}", e.getMessage()); + return ""; + } + } + + private Map createDefaultMappings() { + Map defaults = new ConcurrentHashMap<>(); + defaults.put(ERROR_DEFAULT, createDefaultMapping()); + defaults.put(SUCCESS_DEFAULT, createSuccessMapping()); + return defaults; + } + + private ErrorMapping createDefaultMapping() { + ErrorMapping mapping = new ErrorMapping(); + mapping.setBackendCode(ERROR_DEFAULT); + mapping.setHttpCode(500); + mapping.setStatusCode("500"); + mapping.setDescription("Error interno del servidor"); + return mapping; + } + + private ErrorMapping createSuccessMapping() { + ErrorMapping mapping = new ErrorMapping(); + mapping.setBackendCode(SUCCESS_DEFAULT); + mapping.setHttpCode(200); + mapping.setStatusCode("000"); + mapping.setDescription("Operación exitosa"); + return mapping; + } + + public boolean isReadingFromProps() { + return messagesConfig.isReadFromProps(); + } + + public boolean isSuccessStatusCode(StatusResponse statusResponse) { + if (statusResponse == null || statusResponse.getStatusCode() == null) { + return false; + } + + try { + int statusCode = Integer.parseInt(statusResponse.getStatusCode()); + return statusCode >= 200 && statusCode < 300; + } catch (NumberFormatException e) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/application/service/HttpClientService.java b/src/main/java/com/banesco/common/application/service/HttpClientService.java new file mode 100644 index 0000000..a6c170c --- /dev/null +++ b/src/main/java/com/banesco/common/application/service/HttpClientService.java @@ -0,0 +1,246 @@ +package com.banesco.common.application.service; + +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.exception.HttpApiResponseException; +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.HttpRequest; +import com.banesco.common.domain.model.StatusResponse; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.client.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Slf4j +@ApplicationScoped +public class HttpClientService implements HttpClientUseCase { + + private final ObjectMapper objectMapper; + + @Inject + public HttpClientService(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public T execute(HttpRequest request) { + String finalUrl = request.getUrl(); + + if (request.getPathParams() != null && !request.getPathParams().isEmpty()) { + log.debug("PathParams antes de reemplazar: {}", request.getPathParams()); + log.debug("URL original: {}", finalUrl); + + for (Map.Entry entry : request.getPathParams().entrySet()) { + String placeholder = "{" + entry.getKey() + "}"; + finalUrl = finalUrl.replace(placeholder, entry.getValue()); + } + } + + log.info("URL final: {}", finalUrl); + + if (request.getHeaders() != null) { + log.info("Headers request: {}", request.getHeaders()); + } + + if (request.getQueryParams() != null) { + log.info("Query params request: {}", request.getQueryParams()); + } + + if (request.getBody() != null) { + log.info("Body request: {}", request.getBody()); + } + + try (Client client = createClient(request.getConnectTimeout(), request.getReadTimeout())) { + WebTarget target = client.target(finalUrl); + + if (request.getQueryParams() != null && !request.getQueryParams().isEmpty()) { + request.getQueryParams().forEach(target::queryParam); + } + + Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON); + + if (request.getHeaders() != null && !request.getHeaders().isEmpty()) { + request.getHeaders().forEach(builder::header); + } + + Response response = buildRequest(builder, request); + + return handleResponse(request, response); + } catch (HttpStatusCodeException | HttpApiResponseException e) { + throw e; + } catch (Exception e) { + log.error("Error de conexión {} {}: {}", request.getMethod(), request.getUrl(), e.getMessage()); + throw HttpStatusCodeException.serviceUnavailable( + "HTTP_CONNECTION_ERROR", + "Error de conexión con el servicio externo: " + e.getMessage() + ); + } + } + + private Response buildRequest( + Invocation.Builder builder, + HttpRequest request + ) { + log.info( + "Método HTTP: {}, Headers enviados: {}", + request.getMethod().name(), + request.getHeaders() + ); + + return switch (request.getMethod()) { + case GET -> builder.get(); + case POST -> builder.post(Entity.entity(request.getBody(), MediaType.APPLICATION_JSON)); + case PUT -> builder.put(Entity.entity(request.getBody(), MediaType.APPLICATION_JSON)); + case DELETE -> builder.delete(); + case PATCH -> builder.method("PATCH", Entity.entity(request.getBody(), MediaType.APPLICATION_JSON)); + case HEAD -> builder.head(); + case OPTIONS -> builder.options(); + default -> throw new IllegalArgumentException("Método HTTP no soportado: " + request.getMethod()); + }; + } + + private Client createClient(int connectTimeout, int readTimeout) { + return ClientBuilder.newBuilder() + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .readTimeout(readTimeout, TimeUnit.MILLISECONDS) + .build(); + } + + @SuppressWarnings("unchecked") + private T handleResponse( + HttpRequest request, + Response response + ) { + int statusCode = response.getStatus(); + log.info("Respuesta {} {} - Status: {}", request.getMethod(), request.getUrl(), statusCode); + + try (response) { + if (statusCode >= 200 && statusCode < 300) { + if (request.getResponseType() == Void.class || request.getResponseType() == void.class) { + return null; + } + + T result = (T) response.readEntity(request.getResponseType()); + log.debug("Respuesta exitosa {} {}: {}", request.getMethod(), request.getUrl(), result); + return result; + } else { + String responseBody = response.readEntity(String.class); + + log.error( + "Error HTTP {} {} - Status: {} - Body: {}", + request.getMethod(), + request.getUrl(), + statusCode, + responseBody + ); + + if (isApiResponseFormat(responseBody)) { + ApiResponse apiResponse = parseApiResponse(responseBody, request.getResponseType()); + throw new HttpApiResponseException(statusCode, apiResponse); + } else { + throw mapHttpStatusToException(statusCode, responseBody); + } + } + } catch (HttpStatusCodeException | HttpApiResponseException e) { + throw e; + } catch (Exception e) { + log.error( + "Error procesando respuesta {} {}: {}", + request.getMethod(), + request.getUrl(), + e.getMessage() + ); + throw HttpStatusCodeException.internalServer( + "ERROR", + "Error procesando respuesta del servicio externo: " + e.getMessage() + ); + } + } + + private boolean isApiResponseFormat(String responseBody) { + try { + if (responseBody == null || responseBody.trim().isEmpty()) { + return false; + } + + return responseBody.contains("\"data\"") && + responseBody.contains("\"statusResponse\"") && + responseBody.contains("\"statusCode\"") && + responseBody.contains("\"message\""); + } catch (Exception e) { + return false; + } + } + + private ApiResponse parseApiResponse( + String responseBody, + Class responseType + ) { + try { + JavaType javaType; + + if (responseType == ApiResponse.class) { + javaType = objectMapper.getTypeFactory().constructParametricType( + ApiResponse.class, + objectMapper.getTypeFactory().constructType(Object.class) + ); + + } else { + javaType = objectMapper.getTypeFactory().constructParametricType( + ApiResponse.class, + objectMapper.getTypeFactory().constructType(responseType) + ); + } + + return objectMapper.readValue(responseBody, javaType); + } catch (Exception e) { + log.error("Error parseando ApiResponse: {}", e.getMessage()); + + return new ApiResponse<>( + StatusResponse.builder() + .statusCode("500") + .message("Error parseando respuesta del servicio") + .build() + ); + } + } + + private HttpStatusCodeException mapHttpStatusToException( + int statusCode, + String errorBody + ) { + String errorCode = "HTTP_" + statusCode; + String defaultMessage = "Error en servicio externo: HTTP " + statusCode; + String message = errorBody != null && !errorBody.isEmpty() + ? errorBody + : defaultMessage; + + return switch (statusCode) { + case 400 -> HttpStatusCodeException.badRequest(errorCode, message); + case 401 -> HttpStatusCodeException.unauthorized(errorCode, message); + case 403 -> HttpStatusCodeException.forbidden(errorCode, message); + case 404 -> HttpStatusCodeException.notFound(errorCode, message); + case 405 -> HttpStatusCodeException.methodNotAllowed(errorCode, message); + case 408 -> HttpStatusCodeException.fromStatusCode(408, errorCode, message); + case 409 -> HttpStatusCodeException.conflict(errorCode, message); + case 410 -> HttpStatusCodeException.gone(errorCode, message); + case 412 -> HttpStatusCodeException.preconditionFailed(errorCode, message); + case 415 -> HttpStatusCodeException.unsupportedMediaType(errorCode, message); + case 422 -> HttpStatusCodeException.unprocessableEntity(errorCode, message); + case 429 -> HttpStatusCodeException.tooManyRequests(errorCode, message); + case 500 -> HttpStatusCodeException.internalServer(errorCode, message); + case 501 -> HttpStatusCodeException.notImplemented(errorCode, message); + case 502 -> HttpStatusCodeException.badGateway(errorCode, message); + case 503 -> HttpStatusCodeException.serviceUnavailable(errorCode, message); + case 504 -> HttpStatusCodeException.gatewayTimeout(errorCode, message); + default -> HttpStatusCodeException.fromStatusCode(statusCode, errorCode, message); + }; + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java b/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java new file mode 100644 index 0000000..c09feaf --- /dev/null +++ b/src/main/java/com/banesco/common/application/usecase/HttpClientUseCase.java @@ -0,0 +1,7 @@ +package com.banesco.common.application.usecase; + +import com.banesco.common.domain.model.HttpRequest; + +public interface HttpClientUseCase { + T execute(HttpRequest request); +} diff --git a/src/main/java/com/banesco/common/domain/exception/BaseApiException.java b/src/main/java/com/banesco/common/domain/exception/BaseApiException.java new file mode 100644 index 0000000..c44d4dd --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/BaseApiException.java @@ -0,0 +1,21 @@ +package com.banesco.common.domain.exception; + +import lombok.Getter; + +@Getter +public abstract class BaseApiException extends RuntimeException { + private final String errorCode; + private final String fieldPath; + private final String exceptionType; + + protected BaseApiException(String errorCode, String message, String fieldPath, String exceptionType) { + super(message); + this.errorCode = errorCode; + this.fieldPath = fieldPath; + this.exceptionType = exceptionType; + } + + protected BaseApiException(String errorCode, String fieldPath, String exceptionType) { + this(errorCode, null, fieldPath, exceptionType); + } +} diff --git a/src/main/java/com/banesco/common/domain/exception/BusinessException.java b/src/main/java/com/banesco/common/domain/exception/BusinessException.java new file mode 100644 index 0000000..6d44727 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/BusinessException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class BusinessException extends BaseApiException { + public BusinessException(String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, "business"); + } + + public BusinessException(String errorCode, String fieldPath) { + super(errorCode, fieldPath, "business"); + } +} diff --git a/src/main/java/com/banesco/common/domain/exception/HttpApiResponseException.java b/src/main/java/com/banesco/common/domain/exception/HttpApiResponseException.java new file mode 100644 index 0000000..0d20bff --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/HttpApiResponseException.java @@ -0,0 +1,20 @@ +package com.banesco.common.domain.exception; + +import com.banesco.common.domain.model.ApiResponse; +import lombok.Getter; + +@Getter +public class HttpApiResponseException extends RuntimeException { + private final int statusCode; + private final ApiResponse apiResponse; + + public HttpApiResponseException(int statusCode, ApiResponse apiResponse) { + super( + String.format("HTTP %d: %s", statusCode, + apiResponse.getStatusResponse() != null ? + apiResponse.getStatusResponse().getMessage() : "Error sin mensaje") + ); + this.statusCode = statusCode; + this.apiResponse = apiResponse; + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/domain/exception/HttpClientException.java b/src/main/java/com/banesco/common/domain/exception/HttpClientException.java new file mode 100644 index 0000000..04f4f03 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/HttpClientException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class HttpClientException extends RuntimeException { + public HttpClientException(String message) { + super(message); + } + + public HttpClientException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/banesco/common/domain/exception/HttpStatusCodeException.java b/src/main/java/com/banesco/common/domain/exception/HttpStatusCodeException.java new file mode 100644 index 0000000..588bb6c --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/HttpStatusCodeException.java @@ -0,0 +1,320 @@ +package com.banesco.common.domain.exception; + +import lombok.*; + +import java.util.Map; +import java.util.HashMap; + +@Getter +@ToString +@Builder +public class HttpStatusCodeException extends BaseApiException { + private final int statusCode; + + private static final Map STATUS_CATEGORIES = new HashMap<>(); + + static { + STATUS_CATEGORIES.put(100, "continue"); + STATUS_CATEGORIES.put(101, "switching-protocols"); + STATUS_CATEGORIES.put(102, "processing"); + STATUS_CATEGORIES.put(103, "early-hints"); + STATUS_CATEGORIES.put(200, "ok"); + STATUS_CATEGORIES.put(201, "created"); + STATUS_CATEGORIES.put(202, "accepted"); + STATUS_CATEGORIES.put(203, "non-authoritative-information"); + STATUS_CATEGORIES.put(204, "no-content"); + STATUS_CATEGORIES.put(205, "reset-content"); + STATUS_CATEGORIES.put(206, "partial-content"); + STATUS_CATEGORIES.put(207, "multi-status"); + STATUS_CATEGORIES.put(208, "already-reported"); + STATUS_CATEGORIES.put(226, "im-used"); + STATUS_CATEGORIES.put(300, "multiple-choices"); + STATUS_CATEGORIES.put(301, "moved-permanently"); + STATUS_CATEGORIES.put(302, "found"); + STATUS_CATEGORIES.put(303, "see-other"); + STATUS_CATEGORIES.put(304, "not-modified"); + STATUS_CATEGORIES.put(305, "use-proxy"); + STATUS_CATEGORIES.put(307, "temporary-redirect"); + STATUS_CATEGORIES.put(308, "permanent-redirect"); + STATUS_CATEGORIES.put(400, "bad-request"); + STATUS_CATEGORIES.put(401, "unauthorized"); + STATUS_CATEGORIES.put(402, "payment-required"); + STATUS_CATEGORIES.put(403, "forbidden"); + STATUS_CATEGORIES.put(404, "not-found"); + STATUS_CATEGORIES.put(405, "method-not-allowed"); + STATUS_CATEGORIES.put(406, "not-acceptable"); + STATUS_CATEGORIES.put(407, "proxy-authentication-required"); + STATUS_CATEGORIES.put(408, "request-timeout"); + STATUS_CATEGORIES.put(409, "conflict"); + STATUS_CATEGORIES.put(410, "gone"); + STATUS_CATEGORIES.put(411, "length-required"); + STATUS_CATEGORIES.put(412, "precondition-failed"); + STATUS_CATEGORIES.put(413, "payload-too-large"); + STATUS_CATEGORIES.put(414, "uri-too-long"); + STATUS_CATEGORIES.put(415, "unsupported-media-type"); + STATUS_CATEGORIES.put(416, "range-not-satisfiable"); + STATUS_CATEGORIES.put(417, "expectation-failed"); + STATUS_CATEGORIES.put(418, "im-a-teapot"); + STATUS_CATEGORIES.put(421, "misdirected-request"); + STATUS_CATEGORIES.put(422, "unprocessable-entity"); + STATUS_CATEGORIES.put(423, "locked"); + STATUS_CATEGORIES.put(424, "failed-dependency"); + STATUS_CATEGORIES.put(425, "too-early"); + STATUS_CATEGORIES.put(426, "upgrade-required"); + STATUS_CATEGORIES.put(428, "precondition-required"); + STATUS_CATEGORIES.put(429, "too-many-requests"); + STATUS_CATEGORIES.put(431, "request-header-fields-too-large"); + STATUS_CATEGORIES.put(451, "unavailable-for-legal-reasons"); + STATUS_CATEGORIES.put(500, "internal-server-error"); + STATUS_CATEGORIES.put(501, "not-implemented"); + STATUS_CATEGORIES.put(502, "bad-gateway"); + STATUS_CATEGORIES.put(503, "service-unavailable"); + STATUS_CATEGORIES.put(504, "gateway-timeout"); + STATUS_CATEGORIES.put(505, "http-version-not-supported"); + STATUS_CATEGORIES.put(506, "variant-also-negotiates"); + STATUS_CATEGORIES.put(507, "insufficient-storage"); + STATUS_CATEGORIES.put(508, "loop-detected"); + STATUS_CATEGORIES.put(510, "not-extended"); + STATUS_CATEGORIES.put(511, "network-authentication-required"); + } + + public HttpStatusCodeException(int statusCode, String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, getHttpStatusCategory(statusCode)); + this.statusCode = statusCode; + } + + public HttpStatusCodeException(int statusCode, String errorCode, String message) { + this(statusCode, errorCode, message, null); + } + + public HttpStatusCodeException(int statusCode, String errorCode) { + this(statusCode, errorCode, getDefaultMessage(statusCode), null); + } + + public HttpStatusCodeException(int statusCode) { + this(statusCode, "HTTP_" + statusCode); + } + + private static String getHttpStatusCategory(int statusCode) { + String category = STATUS_CATEGORIES.get(statusCode); + + if (category != null) { + return category; + } + + if (statusCode >= 100 && statusCode < 200) return "informational"; + if (statusCode >= 200 && statusCode < 300) return "success"; + if (statusCode >= 300 && statusCode < 400) return "redirection"; + if (statusCode >= 400 && statusCode < 500) return "client-error"; + if (statusCode >= 500 && statusCode < 600) return "server-error"; + return "unknown"; + } + + private static String getDefaultMessage(int statusCode) { + return switch (statusCode) { + case 200 -> "OK"; + case 201 -> "Created"; + case 202 -> "Accepted"; + case 204 -> "No Content"; + case 400 -> "Bad Request"; + case 401 -> "Unauthorized"; + case 403 -> "Forbidden"; + case 404 -> "Not Found"; + case 405 -> "Method Not Allowed"; + case 408 -> "Request Timeout"; + case 409 -> "Conflict"; + case 410 -> "Gone"; + case 422 -> "Unprocessable Entity"; + case 429 -> "Too Many Requests"; + case 500 -> "Internal Server Error"; + case 502 -> "Bad Gateway"; + case 503 -> "Service Unavailable"; + case 504 -> "Gateway Timeout"; + default -> "HTTP " + statusCode; + }; + } + + public static HttpStatusCodeException badRequest(String errorCode) { + return new HttpStatusCodeException(400, errorCode); + } + + public static HttpStatusCodeException badRequest(String errorCode, String fieldPath) { + return new HttpStatusCodeException(400, errorCode, getDefaultMessage(400), fieldPath); + } + + public static HttpStatusCodeException unauthorized(String errorCode) { + return new HttpStatusCodeException(401, errorCode); + } + + public static HttpStatusCodeException unauthorized(String errorCode, String fieldPath) { + return new HttpStatusCodeException(401, errorCode, getDefaultMessage(401), fieldPath); + } + + public static HttpStatusCodeException forbidden(String errorCode) { + return new HttpStatusCodeException(403, errorCode); + } + + public static HttpStatusCodeException forbidden(String errorCode, String fieldPath) { + return new HttpStatusCodeException(403, errorCode, getDefaultMessage(403), fieldPath); + } + + public static HttpStatusCodeException notFound(String errorCode) { + return new HttpStatusCodeException(404, errorCode); + } + + public static HttpStatusCodeException notFound(String errorCode, String fieldPath) { + return new HttpStatusCodeException(404, errorCode, getDefaultMessage(404), fieldPath); + } + + public static HttpStatusCodeException methodNotAllowed(String errorCode) { + return new HttpStatusCodeException(405, errorCode); + } + + public static HttpStatusCodeException methodNotAllowed(String errorCode, String fieldPath) { + return new HttpStatusCodeException(405, errorCode, getDefaultMessage(405), fieldPath); + } + + public static HttpStatusCodeException conflict(String errorCode) { + return new HttpStatusCodeException(409, errorCode); + } + + public static HttpStatusCodeException conflict(String errorCode, String fieldPath) { + return new HttpStatusCodeException(409, errorCode, getDefaultMessage(409), fieldPath); + } + + public static HttpStatusCodeException unprocessableEntity(String errorCode) { + return new HttpStatusCodeException(422, errorCode); + } + + public static HttpStatusCodeException unprocessableEntity(String errorCode, String fieldPath) { + return new HttpStatusCodeException(422, errorCode, getDefaultMessage(422), fieldPath); + } + + public static HttpStatusCodeException tooManyRequests(String errorCode) { + return new HttpStatusCodeException(429, errorCode); + } + + public static HttpStatusCodeException tooManyRequests(String errorCode, String fieldPath) { + return new HttpStatusCodeException(429, errorCode, getDefaultMessage(429), fieldPath); + } + + public static HttpStatusCodeException internalServer(String errorCode) { + return new HttpStatusCodeException(500, errorCode); + } + + public static HttpStatusCodeException internalServer(String errorCode, String fieldPath) { + return new HttpStatusCodeException(500, errorCode, getDefaultMessage(500), fieldPath); + } + + public static HttpStatusCodeException badGateway(String errorCode) { + return new HttpStatusCodeException(502, errorCode); + } + + public static HttpStatusCodeException badGateway(String errorCode, String fieldPath) { + return new HttpStatusCodeException(502, errorCode, getDefaultMessage(502), fieldPath); + } + + public static HttpStatusCodeException serviceUnavailable(String errorCode) { + return new HttpStatusCodeException(503, errorCode); + } + + public static HttpStatusCodeException serviceUnavailable(String errorCode, String fieldPath) { + return new HttpStatusCodeException(503, errorCode, getDefaultMessage(503), fieldPath); + } + + public static HttpStatusCodeException gatewayTimeout(String errorCode) { + return new HttpStatusCodeException(504, errorCode); + } + + public static HttpStatusCodeException gatewayTimeout(String errorCode, String fieldPath) { + return new HttpStatusCodeException(504, errorCode, getDefaultMessage(504), fieldPath); + } + + public static HttpStatusCodeException paymentRequired(String errorCode) { + return new HttpStatusCodeException(402, errorCode); + } + + public static HttpStatusCodeException paymentRequired(String errorCode, String fieldPath) { + return new HttpStatusCodeException(402, errorCode, getDefaultMessage(402), fieldPath); + } + + public static HttpStatusCodeException gone(String errorCode) { + return new HttpStatusCodeException(410, errorCode); + } + + public static HttpStatusCodeException gone(String errorCode, String fieldPath) { + return new HttpStatusCodeException(410, errorCode, getDefaultMessage(410), fieldPath); + } + + public static HttpStatusCodeException preconditionFailed(String errorCode) { + return new HttpStatusCodeException(412, errorCode); + } + + public static HttpStatusCodeException preconditionFailed(String errorCode, String fieldPath) { + return new HttpStatusCodeException(412, errorCode, getDefaultMessage(412), fieldPath); + } + + public static HttpStatusCodeException unsupportedMediaType(String errorCode) { + return new HttpStatusCodeException(415, errorCode); + } + + public static HttpStatusCodeException unsupportedMediaType(String errorCode, String fieldPath) { + return new HttpStatusCodeException(415, errorCode, getDefaultMessage(415), fieldPath); + } + + public static HttpStatusCodeException notImplemented(String errorCode) { + return new HttpStatusCodeException(501, errorCode); + } + + public static HttpStatusCodeException notImplemented(String errorCode, String fieldPath) { + return new HttpStatusCodeException(501, errorCode, getDefaultMessage(501), fieldPath); + } + + public static HttpStatusCodeException ok(String errorCode) { + return new HttpStatusCodeException(200, errorCode); + } + + public static HttpStatusCodeException ok(String errorCode, String fieldPath) { + return new HttpStatusCodeException(200, errorCode, getDefaultMessage(200), fieldPath); + } + + public static HttpStatusCodeException created(String errorCode) { + return new HttpStatusCodeException(201, errorCode); + } + + public static HttpStatusCodeException created(String errorCode, String fieldPath) { + return new HttpStatusCodeException(201, errorCode, getDefaultMessage(201), fieldPath); + } + + public static HttpStatusCodeException accepted(String errorCode) { + return new HttpStatusCodeException(202, errorCode); + } + + public static HttpStatusCodeException accepted(String errorCode, String fieldPath) { + return new HttpStatusCodeException(202, errorCode, getDefaultMessage(202), fieldPath); + } + + public static HttpStatusCodeException noContent(String errorCode) { + return new HttpStatusCodeException(204, errorCode); + } + + public static HttpStatusCodeException noContent(String errorCode, String fieldPath) { + return new HttpStatusCodeException(204, errorCode, getDefaultMessage(204), fieldPath); + } + + public static HttpStatusCodeException requestTimeout(String errorCode) { + return new HttpStatusCodeException(408, errorCode); + } + + public static HttpStatusCodeException requestTimeout(String errorCode, String fieldPath) { + return new HttpStatusCodeException(408, errorCode, getDefaultMessage(408), fieldPath); + } + + public static HttpStatusCodeException fromStatusCode(int statusCode, String errorCode) { + return new HttpStatusCodeException(statusCode, errorCode); + } + + public static HttpStatusCodeException fromStatusCode(int statusCode, String errorCode, String fieldPath) { + return new HttpStatusCodeException(statusCode, errorCode, getDefaultMessage(statusCode), fieldPath); + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/domain/model/Agreement.java b/src/main/java/com/banesco/common/domain/model/Agreement.java new file mode 100644 index 0000000..d6c30c9 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Agreement.java @@ -0,0 +1,21 @@ +package com.banesco.common.domain.model; + +import com.banesco.common.domain.model.Arrangement; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.List; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Agreement { + @Schema(description = "Información del arreglo de la cuenta") + private List arrangement; +} diff --git a/src/main/java/com/banesco/common/domain/model/ApiResponse.java b/src/main/java/com/banesco/common/domain/model/ApiResponse.java new file mode 100644 index 0000000..a5e4845 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/ApiResponse.java @@ -0,0 +1,18 @@ +package com.banesco.common.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class ApiResponse { + private T data; + private StatusResponse statusResponse; + + public ApiResponse(StatusResponse statusResponse) { + this.statusResponse = statusResponse; + this.data = null; + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/domain/model/Arrangement.java b/src/main/java/com/banesco/common/domain/model/Arrangement.java new file mode 100644 index 0000000..52ee99a --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Arrangement.java @@ -0,0 +1,18 @@ +package com.banesco.common.domain.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Arrangement { + @Schema(description = "Información del tipo de arreglo de la cuenta (Si la cuenta corriente tiene interés)") + private ArrangementType arrangementType; +} diff --git a/src/main/java/com/banesco/common/domain/model/ArrangementType.java b/src/main/java/com/banesco/common/domain/model/ArrangementType.java new file mode 100644 index 0000000..732ea42 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/ArrangementType.java @@ -0,0 +1,32 @@ +package com.banesco.common.domain.model; + +public enum ArrangementType { + SWEEP_ARRANGEMENT, + WITHDRAWAL_ARRANGEMENT, + DEPOSIT_ARRANGEMENT, + INTEREST_ARRANGEMENT, + FEE_ARRANGEMENT, + LIEN_ARRANGEMENT, + INFORMATION_ARRANGEMENT, + PAYMENT_ARRANGEMENT, + ENTITLEMENT_ARRANGEMENT, + PERIOD_ARRANGEMENT, + COLLATERAL_ARRANGEMENT, + LIMIT_ARRANGEMENT, + ACCESS_ARRANGEMENT, + CARD_PAYMENT_ARRANGEMENT, + STANDING_ORDER_ARRANGEMENT, + OVERDRAFT_ARRANGEMENT, + REPAYMENT_ARRANGEMENT, + STATEMENT_ARRANGEMENT, + CREDIT_TRANSFER_ARRANGEMENT, + PRODUCT_AND_SERVICE_ARRANGEMENT, + FACTORING_ARRANGEMENT, + ROLLOVER_ARRANGEMENT, + RESTRUCTURING_ARRANGEMENT, + INSURANCE_ARRANGEMENT, + COLLECTION_ARRANGEMENT, + UNDERWRITING_ARRANGEMENT, + TERMINATION_ARRANGEMENT, + MATURITY_ARRANGEMENT, +} diff --git a/src/main/java/com/banesco/common/domain/model/BalanceType.java b/src/main/java/com/banesco/common/domain/model/BalanceType.java new file mode 100644 index 0000000..0d24022 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/BalanceType.java @@ -0,0 +1,26 @@ +package com.banesco.common.domain.model; + +public enum BalanceType { + OPENING_BALANCE, + CLOSING_BALANCE, + CURRENT_BALANCE, + AVAILABLE_BALANCE, + LEDGER_BALANCE, + RESERVE_BALANCE, + FREE_BALANCE, + PRINCIPAL_BALANCE, + CLOSING_AVAILABLE, + CLOSING_BOOKED, + FORWARD_AVAILABLE, + INFORMATION, + INTERIM_AVAILABLE, + INTERIM_BOOKED, + OPENING_AVAILABLE, + OPENING_BOOKED, + PREVIOUSLY_CLOSED_BOOKED, + EXPECTED, + DAILY_MAXIMUM, + MONTHLY_MAXIMUM, + TRANSACTION_MAXIMUM, + TRANSACTION_MINIMUM, +} diff --git a/src/main/java/com/banesco/common/domain/model/BankService.java b/src/main/java/com/banesco/common/domain/model/BankService.java new file mode 100644 index 0000000..210d1e5 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/BankService.java @@ -0,0 +1,16 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class BankService { + private String bankCode; + private String serviceCode; + private String eventCode; +} diff --git a/src/main/java/com/banesco/common/domain/model/CurrencyType.java b/src/main/java/com/banesco/common/domain/model/CurrencyType.java new file mode 100644 index 0000000..29c2d34 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/CurrencyType.java @@ -0,0 +1,9 @@ +package com.banesco.common.domain.model; + +public enum CurrencyType { + BASE, + REPORTING, + SECONDARY, + TRANSFER +} + diff --git a/src/main/java/com/banesco/common/domain/model/DominionConfig.java b/src/main/java/com/banesco/common/domain/model/DominionConfig.java new file mode 100644 index 0000000..36014c6 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/DominionConfig.java @@ -0,0 +1,13 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@RegisterForReflection +public class DominionConfig { + private String url; + private TimeoutConfig timeout; +} diff --git a/src/main/java/com/banesco/common/domain/model/ErrorMapping.java b/src/main/java/com/banesco/common/domain/model/ErrorMapping.java new file mode 100644 index 0000000..56437fd --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/ErrorMapping.java @@ -0,0 +1,22 @@ +package com.banesco.common.domain.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Setter +@Getter +@ToString +@RegisterForReflection +public class ErrorMapping { + @JsonProperty("backendCode") + String backendCode; + @JsonProperty("httpCode") + int httpCode; + @JsonProperty("statusCode") + String statusCode; + @JsonProperty("description") + String description; +} diff --git a/src/main/java/com/banesco/common/domain/model/HttpRequest.java b/src/main/java/com/banesco/common/domain/model/HttpRequest.java new file mode 100644 index 0000000..de49cd9 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/HttpRequest.java @@ -0,0 +1,26 @@ +package com.banesco.common.domain.model; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +import java.util.Map; + +@Getter +@Builder +@ToString +public class HttpRequest { + private String url; + private HttpMethod method; + private Object body; + private Map pathParams; + private Map queryParams; + private Map headers; + private Class responseType; + private int connectTimeout; + private int readTimeout; + + public enum HttpMethod { + GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS + } +} diff --git a/src/main/java/com/banesco/common/domain/model/Identifier.java b/src/main/java/com/banesco/common/domain/model/Identifier.java new file mode 100644 index 0000000..5aeef67 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Identifier.java @@ -0,0 +1,14 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class Identifier { + private String identifierValue; +} diff --git a/src/main/java/com/banesco/common/domain/model/Name.java b/src/main/java/com/banesco/common/domain/model/Name.java new file mode 100644 index 0000000..37eea67 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Name.java @@ -0,0 +1,14 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class Name { + private String fullName; +} diff --git a/src/main/java/com/banesco/common/domain/model/StatusResponse.java b/src/main/java/com/banesco/common/domain/model/StatusResponse.java new file mode 100644 index 0000000..8bb4406 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/StatusResponse.java @@ -0,0 +1,15 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class StatusResponse { + private String statusCode; + private String message; +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/domain/model/TimeoutConfig.java b/src/main/java/com/banesco/common/domain/model/TimeoutConfig.java new file mode 100644 index 0000000..5db3277 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/TimeoutConfig.java @@ -0,0 +1,13 @@ +package com.banesco.common.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@RegisterForReflection +public class TimeoutConfig { + private int connect; + private int response; +} diff --git a/src/main/java/com/banesco/common/infrastructure/config/MessagesConfig.java b/src/main/java/com/banesco/common/infrastructure/config/MessagesConfig.java new file mode 100644 index 0000000..7b8f1a5 --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/config/MessagesConfig.java @@ -0,0 +1,32 @@ +package com.banesco.common.infrastructure.config; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.Getter; +import org.eclipse.microprofile.config.Config; + +@ApplicationScoped +@Getter +public class MessagesConfig { + + private final boolean readFromProps; + private final String errorMessagesJson; + private final String messagesKey; + + private static final String KEY = "busLogalCustomerProductDirectory"; + + @Inject + public MessagesConfig(Config config) { + this.readFromProps = config.getValue("api.read-messages.from-props", Boolean.class); + this.errorMessagesJson = config.getValue("api." + KEY + ".messages.content", String.class); + this.messagesKey = config.getValue("api." + KEY + ".messages.key", String.class); + } + + public String getErrorMessagesJson() { + if (readFromProps) { + return errorMessagesJson != null ? errorMessagesJson : ""; + } else { + return ""; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/infrastructure/config/RequestValidationConfig.java b/src/main/java/com/banesco/common/infrastructure/config/RequestValidationConfig.java new file mode 100644 index 0000000..d027945 --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/config/RequestValidationConfig.java @@ -0,0 +1,12 @@ +package com.banesco.common.infrastructure.config; + +import io.smallrye.config.ConfigMapping; + +@ConfigMapping(prefix = "api.allowed.request-validation") +public interface RequestValidationConfig { + String customerIbsNumber(); + String accountStatus(); + String productCvCode(); + String limitType(); + String cacheaIndicator(); +} diff --git a/src/main/java/com/banesco/common/infrastructure/config/RestClientConfig.java b/src/main/java/com/banesco/common/infrastructure/config/RestClientConfig.java new file mode 100644 index 0000000..de7d7e6 --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/config/RestClientConfig.java @@ -0,0 +1,72 @@ +package com.banesco.common.infrastructure.config; + +import com.banesco.common.domain.model.DominionConfig; +import com.banesco.module.security_trace.domain.model.SecurityTraceConfig; +import com.banesco.module.service_status.domain.model.ServiceStatusConfig; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.config.Config; + +import java.util.Map; + +@Slf4j +@ApplicationScoped +@RegisterForReflection +public class RestClientConfig { + + private final Config config; + private final ObjectMapper objectMapper; + + private static final String API_BASE = "api.rest-client."; + private static final String API_DOMAIN_NAME = "dom-legal-customer-product-directory"; + private static final String API_SECURITY_TRACE_NAME = "security-trace"; + private static final String API_SERVICE_STATUS_NAME = "service-status"; + + @Inject + public RestClientConfig( + Config config, + ObjectMapper objectMapper + ) { + this.config = config; + this.objectMapper = objectMapper; + } + + public DominionConfig getDomLegalCustomerConfig() { + return getConfig(API_DOMAIN_NAME, DominionConfig.class); + } + + public SecurityTraceConfig getSecurityTraceConfig() { + return getConfig(API_SECURITY_TRACE_NAME, SecurityTraceConfig.class); + } + + public ServiceStatusConfig getServiceStatusConfig() { + return getConfig(API_SERVICE_STATUS_NAME, ServiceStatusConfig.class); + } + + private T getConfig( + String configName, + Class configType + ) { + try { + String fullConfigName = API_BASE + configName; + String json = config.getValue(fullConfigName, String.class); + log.info("Configurando {}: {}", fullConfigName, json); + + if (json == null || json.trim().isEmpty()) { + throw new IllegalStateException("Configuración no encontrada para: " + fullConfigName); + } + + Map configMap = objectMapper.readValue(json, new TypeReference<>() {}); + return objectMapper.convertValue(configMap, configType); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Formato JSON inválido para " + configName + ": " + e.getMessage(), e); + } catch (Exception e) { + throw new IllegalStateException("Error cargando configuración del servicio " + configName + ": " + e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java b/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java new file mode 100644 index 0000000..633fd0f --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/context/RequestContext.java @@ -0,0 +1,22 @@ +package com.banesco.common.infrastructure.context; + +import org.slf4j.MDC; + +public class RequestContext { + + private RequestContext() {} + + private static final String REQUEST_ID = "requestId"; + + public static String getRequestId() { + return MDC.get(REQUEST_ID); + } + + public static void setRequestId(String requestId) { + MDC.put(REQUEST_ID, requestId); + } + + public static void clear() { + MDC.remove(REQUEST_ID); + } +} diff --git a/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java b/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java new file mode 100644 index 0000000..c6090ec --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/filter/RequestIdFilter.java @@ -0,0 +1,27 @@ +package com.banesco.common.infrastructure.filter; + +import com.banesco.common.infrastructure.context.RequestContext; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.container.ContainerResponseContext; +import jakarta.ws.rs.container.ContainerResponseFilter; +import jakarta.ws.rs.ext.Provider; + +import java.util.UUID; + +@Provider +public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter { + + @Override + public void filter(ContainerRequestContext requestContext) { + RequestContext.setRequestId(UUID.randomUUID().toString().substring(0, 13)); + } + + @Override + public void filter( + ContainerRequestContext requestContext, + ContainerResponseContext responseContext + ) { + RequestContext.clear(); + } +} diff --git a/src/main/java/com/banesco/module/account/domain/model/Account.java b/src/main/java/com/banesco/module/account/domain/model/Account.java new file mode 100644 index 0000000..c3d5ca4 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/Account.java @@ -0,0 +1,35 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.common.domain.model.Agreement; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.List; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Account { + @Schema(description = "Estado de la cuenta") + private AccountStatus accountStatus; + @Schema(description = "Lista de datos de identificación de la cuenta") + private List accountIdentification; + @Schema(description = "Tipo de cuenta") + private AccountType accountType; + @Schema(description = "Información de los tipos de balances de la cuenta") + private List accountBalance; + @Schema(description = "Información de la moneda de la cuenta") + private List accountCurrency; + @Schema(description = "Información complementaria de la cuenta") + private List accountInvolvement; + @Schema(description = "Información relacional de la cuenta con otras o consigo misma") + private List accountRelationship; + @Schema(description = "Información del acuerdo de la cuenta") + private List agreement; +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountBalance.java b/src/main/java/com/banesco/module/account/domain/model/AccountBalance.java new file mode 100644 index 0000000..380252b --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountBalance.java @@ -0,0 +1,18 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.common.domain.model.BalanceType; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.math.BigDecimal; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class AccountBalance { + private BigDecimal balanceAmount; + private BalanceType balanceType; +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountCurrency.java b/src/main/java/com/banesco/module/account/domain/model/AccountCurrency.java new file mode 100644 index 0000000..40e8b5d --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountCurrency.java @@ -0,0 +1,16 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.common.domain.model.CurrencyType; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class AccountCurrency { + private String currencyCode; + private CurrencyType currencyType; +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountIdentification.java b/src/main/java/com/banesco/module/account/domain/model/AccountIdentification.java new file mode 100644 index 0000000..93c97d8 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountIdentification.java @@ -0,0 +1,16 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.common.domain.model.Identifier; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class AccountIdentification { + private AccountIdentificationType accountIdentificationType; + private Identifier accountIdentification; +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountIdentificationType.java b/src/main/java/com/banesco/module/account/domain/model/AccountIdentificationType.java new file mode 100644 index 0000000..c954018 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountIdentificationType.java @@ -0,0 +1,15 @@ +package com.banesco.module.account.domain.model; + +public enum AccountIdentificationType { + BBAN, + IBAN, + UPIC, + ACCOUNT_NUMBER, + PAN, + PAYM, + WALLET, + SORT_CODE_AND_ACCOUNT_NUMBER, + PRODUCT_CODE, + BANK_NUMBER, + ACCOUNT_CLASS +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountInvolvement.java b/src/main/java/com/banesco/module/account/domain/model/AccountInvolvement.java new file mode 100644 index 0000000..1c983d6 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountInvolvement.java @@ -0,0 +1,18 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.module.party.domain.model.Party; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class AccountInvolvement { + private AccountInvolvementType accountInvolvementType; + @Schema(description = "Información del cliente") + private Party partyReference; +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountInvolvementType.java b/src/main/java/com/banesco/module/account/domain/model/AccountInvolvementType.java new file mode 100644 index 0000000..93f382d --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountInvolvementType.java @@ -0,0 +1,10 @@ +package com.banesco.module.account.domain.model; + +public enum AccountInvolvementType { + PARTY_IS_OWNER_OF_ACCOUNT, + PARTY_IS_SERVICER_OF_ACCOUNT, + PARTY_IS_PAYEE_ON_ACCOUNT, + PARTY_IS_PRIMARY_OWNER_OF_ACCOUNT, + PARTY_IS_CO_OWNER_OF_ACCOUNT, + PARTY_IS_JOINT_OWNER_OF_ACCOUNT, +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountRelationship.java b/src/main/java/com/banesco/module/account/domain/model/AccountRelationship.java new file mode 100644 index 0000000..8dc07eb --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountRelationship.java @@ -0,0 +1,14 @@ +package com.banesco.module.account.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class AccountRelationship { + private AccountRelationshipType accountRelationshipType; +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountRelationshipType.java b/src/main/java/com/banesco/module/account/domain/model/AccountRelationshipType.java new file mode 100644 index 0000000..b9a7269 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountRelationshipType.java @@ -0,0 +1,7 @@ +package com.banesco.module.account.domain.model; + +public enum AccountRelationshipType { + ACCOUNT_IS_PARENT_ACCOUNT_FOR_ACCOUNT, + ACCOUNT_IS_SUB_ACCOUNT_FOR_ACCOUNT, + ACCOUNT_IS_LINKED_TO_ACCOUNT, +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountStatus.java b/src/main/java/com/banesco/module/account/domain/model/AccountStatus.java new file mode 100644 index 0000000..5cc5f92 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountStatus.java @@ -0,0 +1,16 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.module.account.domain.model.AccountStatusType; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class AccountStatus { + private String status; + private AccountStatusType statusType; +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountStatusType.java b/src/main/java/com/banesco/module/account/domain/model/AccountStatusType.java new file mode 100644 index 0000000..f5cebe9 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountStatusType.java @@ -0,0 +1,9 @@ +package com.banesco.module.account.domain.model; + +public enum AccountStatusType { + ENABLED, + DISABLED, + DELETED, + PROFORMA, + PENDING, +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountType.java b/src/main/java/com/banesco/module/account/domain/model/AccountType.java new file mode 100644 index 0000000..e73892e --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountType.java @@ -0,0 +1,20 @@ +package com.banesco.module.account.domain.model; + +public enum AccountType { + INDIVIDUAL_ACCOUNT, + JOINT_ACCOUNT, + LIQUIDATION_ACCOUNT, + PROVISION_ACCOUNT, + PARTNERSHIP_ACCOUNT, + DEBIT_ACCOUNT, + CREDIT_ACCOUNT, + VOSTRO_ACCOUNT, + NOSTRO_ACCOUNT, + PAYMENT_ACCOUNT, + SETTLEMENT_ACCOUNT, + SAVING_ACCOUNT, + CURRENT_ACCOUNT, + CASH_ACCOUNT, + LOAN_ACCOUNT, + INVESTMENT_ACCOUNT, +} diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/application/service/LegalCustomerProductDirectoryService.java b/src/main/java/com/banesco/module/legal_customer_product_directory/application/service/LegalCustomerProductDirectoryService.java new file mode 100644 index 0000000..0562f81 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/application/service/LegalCustomerProductDirectoryService.java @@ -0,0 +1,123 @@ +package com.banesco.module.legal_customer_product_directory.application.service; + +import com.banesco.common.application.helper.MessageResponseHelper; +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.infrastructure.context.RequestContext; +import com.banesco.module.legal_customer_product_directory.application.usecase.DomainUseCase; +import com.banesco.module.legal_customer_product_directory.application.usecase.LegalCustomerProductDirectoryUseCase; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; +import com.banesco.module.legal_customer_product_directory.domain.dto.response.LegalCustomerProductDirectoryResponse; +import com.banesco.module.security_trace.application.usecase.SecurityTraceUseCase; +import com.banesco.module.security_trace.domain.dto.response.SecurityTraceResponse; +import com.banesco.module.service_status.application.usecase.ServiceStatusUseCase; +import com.banesco.module.service_status.domain.dto.request.ServiceStatusRequest; +import com.banesco.module.service_status.domain.dto.response.ServiceStatusResponse; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; + +@Slf4j +@ApplicationScoped +public class LegalCustomerProductDirectoryService implements LegalCustomerProductDirectoryUseCase { + + private final MessageResponseHelper messageResponseHelper; + private final ServiceStatusUseCase serviceStatusUseCase; + private final SecurityTraceUseCase securityTraceUseCase; + private final DomainUseCase domainUseCase; + + @Inject + public LegalCustomerProductDirectoryService( + MessageResponseHelper messageResponseHelper, + ServiceStatusUseCase serviceStatusUseCase, + SecurityTraceUseCase securityTraceUseCase, + DomainUseCase domainUseCase + ) { + this.messageResponseHelper = messageResponseHelper; + this.serviceStatusUseCase = serviceStatusUseCase; + this.securityTraceUseCase = securityTraceUseCase; + this.domainUseCase = domainUseCase; + } + + @Override + public ApiResponse execute( + LegalCustomerProductDirectoryRequest request + ) { + log.info("Iniciando ejecución para el cliente: {}", request.getCustomerIbsNumber()); + + if(!serviceStatus(request)) { + log.info("Estatus del servicio no disponible: {}", request.getCustomerIbsNumber()); + throw HttpStatusCodeException.serviceUnavailable("VRN04"); + } + + try { + ApiResponse response = domain(request); + + if ( + !Objects.isNull(response.getData()) && + messageResponseHelper.isSuccessStatusCode(response.getStatusResponse()) + ) { + return new ApiResponse<>(response.getData(), messageResponseHelper.createStatusResponse( + response.getStatusResponse().getStatusCode() + )); + } + + throw HttpStatusCodeException.serviceUnavailable("503"); + } catch (HttpStatusCodeException e) { + log.error("Excepción HTTP del api de dominio: {} - {}", e.getStatusCode(), e.getMessage()); + throw e; + } catch (Exception e) { + log.error("Excepción genérica del api de dominio: {}", e.getMessage()); + throw e; + } finally { + securityTrace(); + } + } + + private ApiResponse domain( + LegalCustomerProductDirectoryRequest request + ) { + log.info("Ejecutando llamada al api de dominio: {}", request.getCustomerIbsNumber()); + return domainUseCase.execute(request, LegalCustomerProductDirectoryResponse.class); + } + + private boolean serviceStatus( + LegalCustomerProductDirectoryRequest request + ) { + log.info("Ejecutando llamada al api de la consulta del estatus del servicio"); + + boolean isServiceActive = false; + + try { + ServiceStatusResponse response = serviceStatusUseCase.execute( + ServiceStatusRequest.builder() + .applicationId(request.getAppId()) + .transactionId(RequestContext.getRequestId()) + .build(), + ServiceStatusResponse.class + ); + + isServiceActive = Objects.equals(response.getStatus(), "A"); + } catch (HttpStatusCodeException e) { + log.info( + "Error HTTP al ejecutar la consulta del estadus del servicio: {} -> {}", + e.getStatusCode(), + e.getMessage() + ); + } catch (Exception e) { + log.info( + "Error al ejecutar la consulta del estadus del servicio: {}", + e.getMessage() + ); + } + + return isServiceActive; + } + + private void securityTrace() { + log.info("Ejecutando llamada al api de la traza de seguridad"); + securityTraceUseCase.execute(SecurityTraceResponse.class); + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/DomainUseCase.java b/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/DomainUseCase.java new file mode 100644 index 0000000..acc18c2 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/DomainUseCase.java @@ -0,0 +1,13 @@ +package com.banesco.module.legal_customer_product_directory.application.usecase; + +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; + +import java.util.Map; + +public interface DomainUseCase { + ApiResponse execute( + LegalCustomerProductDirectoryRequest params, + Class responseType + ); +} diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/LegalCustomerProductDirectoryUseCase.java b/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/LegalCustomerProductDirectoryUseCase.java new file mode 100644 index 0000000..6313926 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/LegalCustomerProductDirectoryUseCase.java @@ -0,0 +1,11 @@ +package com.banesco.module.legal_customer_product_directory.application.usecase; + +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; +import com.banesco.module.legal_customer_product_directory.domain.dto.response.LegalCustomerProductDirectoryResponse; + +public interface LegalCustomerProductDirectoryUseCase { + ApiResponse execute( + LegalCustomerProductDirectoryRequest request + ); +} diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/request/LegalCustomerProductDirectoryRequest.java b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/request/LegalCustomerProductDirectoryRequest.java new file mode 100644 index 0000000..c317ea9 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/request/LegalCustomerProductDirectoryRequest.java @@ -0,0 +1,42 @@ +package com.banesco.module.legal_customer_product_directory.domain.dto.request; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.util.Map; +import java.util.Objects; + +import static java.util.Map.entry; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class LegalCustomerProductDirectoryRequest { + @NonNull + private String customerReferenceFintechId; // Header obligatorio + @NonNull + private String appId; // Header obligatorio + @NonNull + private String customerIbsNumber; // VCUSCUN - Obligatorio (Número de cliente IBS) + + private String bankNumber; // VACMBNK - Número de Banco (filtro) + private String currencyCode; // VACMCCY - Moneda (filtro) + private String accountStatus; // VACMAST - Estatus de Cuenta (filtro) + private String productCvCode; // VACMPROCV - Código de Producto Cuenta Verde (filtro) + private String productCode; // VACMPRO - Código de Producto (filtro) + private String channelCode; // VAFILICANAL - Código de Canal (filtro) + private String serviceType; // VAFILICOSER - Tipo de Servicio (filtro) + private String affiliationStatus; // VAFILISTATU - Estatus de afiliación (filtro) + private String limitType; // VALIMIT - Pagador/Receptor (PAG/REC) (filtro) + private String casheaIndicator; // VCASHEA - SI/NO (filtro) + + public Map toQueryString() { + return Map.ofEntries( + entry("appId", Objects.toString(getAppId(), "")), + entry("customerReferenceFintechId", Objects.toString(getCustomerReferenceFintechId(), "")) + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/response/LegalCustomerProductDirectoryResponse.java b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/response/LegalCustomerProductDirectoryResponse.java new file mode 100644 index 0000000..00a7edf --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/response/LegalCustomerProductDirectoryResponse.java @@ -0,0 +1,18 @@ +package com.banesco.module.legal_customer_product_directory.domain.dto.response; + +import com.banesco.module.legal_customer_product_directory.domain.model.CustomerProductAndServiceDirectory; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@Schema(description = "Respuesta del directorio de cuentas de cliente legal") +public class LegalCustomerProductDirectoryResponse { + @Schema(description = "Directorio de cuentas") + private CustomerProductAndServiceDirectory customerProductAndServiceDirectory; +} diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/domain/model/CustomerProductAndServiceDirectory.java b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/model/CustomerProductAndServiceDirectory.java new file mode 100644 index 0000000..eb4df37 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/model/CustomerProductAndServiceDirectory.java @@ -0,0 +1,20 @@ +package com.banesco.module.legal_customer_product_directory.domain.model; + +import com.banesco.module.account.domain.model.Account; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +import java.util.List; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@Schema(description = "Directorio de cuentas") +public class CustomerProductAndServiceDirectory { + @Schema(description = "Lista de cuentas") + private List accounts; +} diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/client/DomLegalCustomerDirectoryClient.java b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/client/DomLegalCustomerDirectoryClient.java new file mode 100644 index 0000000..1dc292e --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/client/DomLegalCustomerDirectoryClient.java @@ -0,0 +1,95 @@ +package com.banesco.module.legal_customer_product_directory.infrastructure.client; + +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.exception.HttpApiResponseException; +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.DominionConfig; +import com.banesco.common.domain.model.HttpRequest; +import com.banesco.common.domain.model.StatusResponse; +import com.banesco.common.infrastructure.config.RestClientConfig; +import com.banesco.module.legal_customer_product_directory.application.usecase.DomainUseCase; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +@Slf4j +@ApplicationScoped +public class DomLegalCustomerDirectoryClient implements DomainUseCase { + private final HttpClientUseCase httpClientUseCase; + private final DominionConfig dominionConfig; + + @Inject + public DomLegalCustomerDirectoryClient( + HttpClientUseCase httpClientUseCase, + RestClientConfig restClientConfig + ) { + this.httpClientUseCase = httpClientUseCase; + this.dominionConfig = restClientConfig.getDomLegalCustomerConfig(); + log.info("Configuración cargada para bus-legal-customer-directory: {}", dominionConfig); + } + + @Override + @SuppressWarnings("unchecked") + public ApiResponse execute( + LegalCustomerProductDirectoryRequest params, + Class responseType + ) { + String customerIbsNumber = params.getCustomerIbsNumber(); + + log.info("Consultando información del cliente: {}", customerIbsNumber); + + HttpRequest request = HttpRequest.builder() + .url(dominionConfig.getUrl()) + .method(HttpRequest.HttpMethod.GET) + .pathParams(Map.of("customerIbsNumber", customerIbsNumber)) + .queryParams(params.toQueryString()) + .responseType(ApiResponse.class) + .connectTimeout(dominionConfig.getTimeout().getConnect()) + .readTimeout(dominionConfig.getTimeout().getResponse()) + .build(); + + log.debug("Request configurado: {}", request); + + try { + ApiResponse response = httpClientUseCase.execute(request); + + log.info( + "Solicitud del api de dominio exitoso: {} -> {}", + response.getStatusResponse(), + response.getData() + ); + + return response; + } catch (HttpApiResponseException e) { + log.error( + "Error HTTP con ApiResponse consultando cliente {}: {}", + customerIbsNumber, + e.getApiResponse() + ); + + return (ApiResponse) e.getApiResponse(); + } catch (HttpStatusCodeException e) { + log.error( + "Error HTTP consultando cliente {}: {} - {}", + customerIbsNumber, + e.getStatusCode(), + e.getMessage() + ); + + return new ApiResponse<>( + StatusResponse.builder() + .statusCode(String.valueOf(e.getStatusCode())) + .message(e.getMessage()) + .build() + ); + } catch (Exception e) { + log.error("Error consultando cliente {}: {}", customerIbsNumber, e.getMessage()); + + return new ApiResponse<>(StatusResponse.builder().statusCode("503").build()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/resource/LegalCustomerProductDirectoryResource.java b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/resource/LegalCustomerProductDirectoryResource.java new file mode 100644 index 0000000..e0eac1a --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/resource/LegalCustomerProductDirectoryResource.java @@ -0,0 +1,352 @@ +package com.banesco.module.legal_customer_product_directory.infrastructure.resource; + +import com.banesco.common.application.helper.MessageResponseHelper; +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.StatusResponse; +import com.banesco.module.legal_customer_product_directory.application.usecase.LegalCustomerProductDirectoryUseCase; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; +import com.banesco.module.legal_customer_product_directory.domain.dto.response.LegalCustomerProductDirectoryResponse; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.media.SchemaProperty; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; + +import java.util.Objects; + +@Slf4j +@Path("/bus-legal-customer-product-directory") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class LegalCustomerProductDirectoryResource { + + private final LegalCustomerProductDirectoryUseCase useCase; + private final MessageResponseHelper messageResponseHelper; + + @Inject + public LegalCustomerProductDirectoryResource( + LegalCustomerProductDirectoryUseCase useCase, + MessageResponseHelper messageResponseHelper + ) { + this.useCase = useCase; + this.messageResponseHelper = messageResponseHelper; + } + + @GET + @Path("/retrieve/{customerIbsNumber : (?!retrieve$).*}") + @Operation( + summary = "Recuperar productos de cliente legal", + description = "Consulta masiva de cuentas por número de cliente IBS" + ) + @APIResponses(value = { + @APIResponse( + responseCode = "200", + description = "Operación exitosa", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = ApiResponse.class, + properties = { + @SchemaProperty( + name = "data", + oneOf = {LegalCustomerProductDirectoryResponse.class} + ), + @SchemaProperty( + name = "statusResponse", + implementation = StatusResponse.class + ) + } + ), + examples = @ExampleObject( + name = "Ejemplo exitoso", + value = """ + { + "data": { + "customerProductAndServiceDirectory": { + "accounts": [ + { + "accountStatus": { + "status": "A", + "statusType": "ENABLED" + }, + "accountType": "CURRENT_ACCOUNT", + "accountIdentification": [ + { + "accountIdentificationType": "ACCOUNT_NUMBER", + "accountIdentification": { + "identifierValue": "01340025330253093528" + } + }, + { + "accountIdentificationType": "BANK_NUMBER", + "accountIdentification": { + "identifierValue": "01" + } + }, + { + "accountIdentificationType": "PRODUCT_CODE", + "accountIdentification": { + "identifierValue": "3980" + } + }, + { + "accountIdentificationType": "ACCOUNT_CLASS", + "accountIdentification": { + "identifierValue": "80" + } + } + ], + "accountBalance": [ + { + "balanceAmount": 390417.36, + "balanceType": "AVAILABLE_BALANCE" + }, + { + "balanceAmount": 100000.00, + "balanceType": "DAILY_MAXIMUM" + }, + { + "balanceAmount": 3000000.00, + "balanceType": "MONTHLY_MAXIMUM" + }, + { + "balanceAmount": 0.01, + "balanceType": "TRANSACTION_MINIMUM" + }, + { + "balanceAmount": 100000.00, + "balanceType": "TRANSACTION_MAXIMUM" + } + ], + "accountCurrency": [ + { + "currencyCode": "BS", + "currencyType": "BASE" + } + ], + "accountInvolvement": [ + { + "accountInvolvementType": "PARTY_IS_OWNER_OF_ACCOUNT", + "partyReference": { + "partyName": [ + { + "fullName": "TASCA RESTAURANT GOOD WORLD CEN, C.A." + } + ], + "partyType": "ORGANISATION", + "partyIdentification": [ + { + "partyIdentificationType": "TAX_IDENTIFICATION_NUMBER", + "partyIdentification": { + "identifierValue": "J000000001" + } + }, + { + "partyIdentificationType": "TELEPHONE_NUMBER", + "partyIdentification": { + "identifierValue": "04122710660" + } + }, + { + "partyIdentificationType": "TELEPHONE_OPERATOR", + "partyIdentification": { + "identifierValue": "0412" + } + } + ] + } + } + ], + "accountRelationship": [ + { + "accountRelationshipType": "ACCOUNT_IS_SUB_ACCOUNT_FOR_ACCOUNT" + } + ], + "agreement": [ + { + "arrangement": [ + { + "arrangementType": "INTEREST_ARRANGEMENT" + } + ] + } + ] + } + ] + } + }, + "statusResponse": { + "statusCode": "200", + "message": "Operación exitosa" + } + } + """ + ) + ) + ), + @APIResponse( + responseCode = "400", + description = "Error en formato o campo requerido", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = ApiResponse.class, + properties = { + @SchemaProperty( + name = "data", + oneOf = {LegalCustomerProductDirectoryResponse.class} + ), + @SchemaProperty( + name = "statusResponse", + implementation = StatusResponse.class + ) + } + ), + examples = { + @ExampleObject( + name = "Error VDE01 - Campo obligatorio", + value = """ + { + "data": null, + "statusResponse": { + "statusCode": "VDE01", + "message": "El campo customerIbsNumber es obligatorio" + } + } + """ + ), + @ExampleObject( + name = "Error VDE02 - Valor no permitido", + value = """ + { + "data": null, + "statusResponse": { + "statusCode": "VDE02", + "message": "El valor 'XYZ' no es permitido para el campo accountStatus. Valores permitidos: A, O, ACTBSUSD" + } + } + """ + ) + } + ) + ), + @APIResponse( + responseCode = "401", + description = "No autorizado", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = ApiResponse.class, + properties = { + @SchemaProperty( + name = "data", + oneOf = {LegalCustomerProductDirectoryResponse.class} + ), + @SchemaProperty( + name = "statusResponse", + implementation = StatusResponse.class + ) + } + ), + examples = { + @ExampleObject( + name = "Error VRN08 - Identificación del fintechId no coincide", + value = """ + { + "data": null, + "statusResponse": { + "statusCode": "VRN08", + "message": "El fintechId proporcionado no coincide con el registrado para este cliente" + } + } + """ + ) + } + ) + ), + @APIResponse( + responseCode = "503", + description = "Servicio no disponible", + content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema( + implementation = ApiResponse.class, + properties = { + @SchemaProperty( + name = "data", + oneOf = {LegalCustomerProductDirectoryResponse.class} + ), + @SchemaProperty( + name = "statusResponse", + implementation = StatusResponse.class + ) + } + ), + examples = { + @ExampleObject( + name = "Error VRN04 - Fuera de horario", + value = """ + { + "data": null, + "statusResponse": { + "statusCode": "VRN04", + "message": "El servicio no está disponible fuera del horario operativo" + } + } + """ + ), + @ExampleObject( + name = "Error VDR13 - OSB no disponible", + value = """ + { + "data": null, + "statusResponse": { + "statusCode": "VDR13", + "message": "El servicio OSB no está disponible en este momento" + } + } + """ + ) + } + ) + ) + }) + public Response retrieve( + @PathParam("customerIbsNumber") + @Parameter(description = "Número de cliente IBS (VCUSCUN)", example = "200053197") + String customerIbsNumber, + + @QueryParam("appId") + @Parameter(description = "ID de la aplicación", required = true, example = "DANIAPP") + String appId, + + @QueryParam("customerReferenceFintechId") + @Parameter(description = "ID de la fintech", example = "pranical-test") + String customerReferenceFintechId + ) { + log.info("Iniciando consulta para cliente IBS: {}", customerIbsNumber); + + try { + return Response.ok(useCase.execute( + LegalCustomerProductDirectoryRequest.builder() + .customerIbsNumber(Objects.toString(customerIbsNumber, "")) + .customerReferenceFintechId(Objects.toString(customerReferenceFintechId, "")) + .appId(Objects.toString(appId, "")) + .build() + )).build(); + } catch (HttpStatusCodeException e) { + return messageResponseHelper.handleException(e); + } catch (Exception e) { + return messageResponseHelper.handleGenericException(e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/party/domain/model/Party.java b/src/main/java/com/banesco/module/party/domain/model/Party.java new file mode 100644 index 0000000..a367cd3 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/Party.java @@ -0,0 +1,21 @@ +package com.banesco.module.party.domain.model; + +import com.banesco.common.domain.model.Name; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.util.List; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Party { + private List partyName; + private PartyType partyType; + private List partyIdentification; +} diff --git a/src/main/java/com/banesco/module/party/domain/model/PartyDateTime.java b/src/main/java/com/banesco/module/party/domain/model/PartyDateTime.java new file mode 100644 index 0000000..c04d4ba --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyDateTime.java @@ -0,0 +1,15 @@ +package com.banesco.module.party.domain.model; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class PartyDateTime { + private String dateTimeType; + private String dateTime; +} diff --git a/src/main/java/com/banesco/module/party/domain/model/PartyIdentification.java b/src/main/java/com/banesco/module/party/domain/model/PartyIdentification.java new file mode 100644 index 0000000..4afa156 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyIdentification.java @@ -0,0 +1,17 @@ +package com.banesco.module.party.domain.model; + +import com.banesco.common.domain.model.Identifier; +import com.banesco.module.party.domain.model.PartyIdentificationType; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class PartyIdentification { + private PartyIdentificationType partyIdentificationType; + private Identifier partyIdentification; +} diff --git a/src/main/java/com/banesco/module/party/domain/model/PartyIdentificationType.java b/src/main/java/com/banesco/module/party/domain/model/PartyIdentificationType.java new file mode 100644 index 0000000..2a5fd19 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyIdentificationType.java @@ -0,0 +1,20 @@ +package com.banesco.module.party.domain.model; + +public enum PartyIdentificationType { + TAX_IDENTIFICATION_NUMBER, + NATIONAL_REGISTRATION_NUMBER, + REGISTRATION_AUTHORITY_IDENTIFICATION, + LEI_LEGAL_ENTITY_IDENTIFIER, + ALIEN_REGISTRATION_NUMBER, + PASSPORT_NUMBER, + TAX_EXEMPT_IDENTIFICATION_NUMBER, + CORPORATE_IDENTIFICATION, + DRIVER_LICENSE_NUMBER, + FOREIGN_INVESTMENT_IDENTITY_NUMBER, + SOCIAL_SECURITY_NUMBER, + IDENTITY_CARD_NUMBER, + CONCAT, + NATIONAL_REGISTRATION_IDENTIFICATION_NUMBER, + TELEPHONE_NUMBER, + TELEPHONE_OPERATOR, +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/party/domain/model/PartyType.java b/src/main/java/com/banesco/module/party/domain/model/PartyType.java new file mode 100644 index 0000000..ec99c5f --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyType.java @@ -0,0 +1,6 @@ +package com.banesco.module.party.domain.model; + +public enum PartyType { + PERSON, + ORGANISATION +} diff --git a/src/main/java/com/banesco/module/security_trace/application/usecase/SecurityTraceUseCase.java b/src/main/java/com/banesco/module/security_trace/application/usecase/SecurityTraceUseCase.java new file mode 100644 index 0000000..b66b6ed --- /dev/null +++ b/src/main/java/com/banesco/module/security_trace/application/usecase/SecurityTraceUseCase.java @@ -0,0 +1,5 @@ +package com.banesco.module.security_trace.application.usecase; + +public interface SecurityTraceUseCase { + T execute(Class responseType); +} diff --git a/src/main/java/com/banesco/module/security_trace/domain/dto/request/SecurityTraceRequest.java b/src/main/java/com/banesco/module/security_trace/domain/dto/request/SecurityTraceRequest.java new file mode 100644 index 0000000..74d0cdd --- /dev/null +++ b/src/main/java/com/banesco/module/security_trace/domain/dto/request/SecurityTraceRequest.java @@ -0,0 +1,133 @@ +package com.banesco.module.security_trace.domain.dto.request; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.math.BigDecimal; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class SecurityTraceRequest { + private String codBan; + private String codMon; + private String codEve; + private String codEve2; + private String login; + private String fecHor; + private String nacCli; + private Integer cedRifCli; + private String tipoProductoCli; + private String tipoProductoBen; + private String productoCli; + private String codEmpresa; + private String nacBen; + private Integer cedBen; + private String nombreBen; + private String productoBen; + private BigDecimal monto; + private String referencia; + private String nroDePago; + private String desPago; + private String objeto; + private Integer tipoRespuesta; + private String msgRespuesta; + private Integer tiempoRespuesta; + private String codFintech; + private String cedRifFintech; + private String nombreFintech; + private String tipoDispositivo; + private String desDispositivo; + private String ipCli; + private String refInternacional; + private BigDecimal montoReceptor; + private String refBanco; + private String stsTransaccion; + private String tipoMonedaEmi; + private String tipoMonedaRec; + private String paisEmisor; + private String paisReceptor; + private BigDecimal montoEmisor; + private String numeroCuentaEmi; + private String numeroCuentaRec; + private String numeroTelefonoRec; + private String propositoTransaccion; + private BigDecimal montoComision; + private String tipoIdEmisor; + private String idEmisor; + private BigDecimal numeroTarjeta; + private Integer codigoAutoriza; + private String refUniversal; + private String fechaAprobacion; + private String refBanesco; + private String fecHorCarga; + private String tipoProductoEmisor; + private String ctaEmisor; + private String tipoProductoReceptor; + private String ctaReceptor; + private String idReceptor; + private String nombreReceptor; + private String refExterna; + private String origenFondos; + private String destinoFondos; + private String fecHorTransaccion; + private String metodoPagoEmisor; + private String valorMetodoPagoEmisor; + private String entFinancieraEmisor; + private String nombreEmisor; + private String monedaEmisor; + private String metodoPagoReceptor; + private String valorMetodoPagoReceptor; + private String entFinancieraReceptor; + private String monedaReceptor; + private String descPago; + private String descTransaccion; + private BigDecimal tasaComision; + private BigDecimal numTarjeta; + private String codAutorizacion; + private String fecHorAprobacion; + private BigDecimal montoIgtf; + private BigDecimal montoLbtr; + private BigDecimal tasaCambio; + private BigDecimal montoTotalBs; + private String sp; + private String cn; + private String OU; + private String distinguishedName; + private String userPassword; + private Integer Enabled; + private String givenName; + private String sn; + private String mail; + private String employeeNumber; + private String employeeID; + private String mobile; + private String homePhone; + private String company; + private String personalTitle; + private String title; + private String description; + private String departament; + private String otherMobile; + private String otherHomePhone; + private String telephoneNumber; + private String facsimileTelephoneNumber; + private String postalAddress; + private String postalCode; + private String st; + private String owner; + private String serialNumber; + private String usuarioMaster; + private Integer lockoutTime; + private Integer lockoutDuration; + private Integer lockoutThreshold; + private Integer badPwdCount; + private String badPasswordTime; + private String oU; + private Integer enabled; +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/security_trace/domain/dto/response/SecurityTraceResponse.java b/src/main/java/com/banesco/module/security_trace/domain/dto/response/SecurityTraceResponse.java new file mode 100644 index 0000000..1adb9d3 --- /dev/null +++ b/src/main/java/com/banesco/module/security_trace/domain/dto/response/SecurityTraceResponse.java @@ -0,0 +1,14 @@ +package com.banesco.module.security_trace.domain.dto.response; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class SecurityTraceResponse { + private String msg; +} diff --git a/src/main/java/com/banesco/module/security_trace/domain/model/SecurityTraceConfig.java b/src/main/java/com/banesco/module/security_trace/domain/model/SecurityTraceConfig.java new file mode 100644 index 0000000..42a1660 --- /dev/null +++ b/src/main/java/com/banesco/module/security_trace/domain/model/SecurityTraceConfig.java @@ -0,0 +1,16 @@ +package com.banesco.module.security_trace.domain.model; + +import com.banesco.common.domain.model.TimeoutConfig; +import com.banesco.module.security_trace.domain.dto.request.SecurityTraceRequest; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@RegisterForReflection +public class SecurityTraceConfig { + private String url; + private TimeoutConfig timeout; + private SecurityTraceRequest request; +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/security_trace/infrastructure/client/SecurityTraceClient.java b/src/main/java/com/banesco/module/security_trace/infrastructure/client/SecurityTraceClient.java new file mode 100644 index 0000000..ebc5705 --- /dev/null +++ b/src/main/java/com/banesco/module/security_trace/infrastructure/client/SecurityTraceClient.java @@ -0,0 +1,67 @@ +package com.banesco.module.security_trace.infrastructure.client; + +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.HttpRequest; +import com.banesco.common.infrastructure.config.RestClientConfig; +import com.banesco.module.security_trace.application.usecase.SecurityTraceUseCase; +import com.banesco.module.security_trace.domain.dto.request.SecurityTraceRequest; +import com.banesco.module.security_trace.domain.model.SecurityTraceConfig; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ApplicationScoped +public class SecurityTraceClient implements SecurityTraceUseCase { + private final HttpClientUseCase httpClientUseCase; + private final SecurityTraceConfig securityTraceConfig; + + @Inject + public SecurityTraceClient( + HttpClientUseCase httpClientUseCase, + RestClientConfig restClientConfig + ) { + this.httpClientUseCase = httpClientUseCase; + this.securityTraceConfig = restClientConfig.getSecurityTraceConfig(); + log.info("Configuración cargada para security-trace: {}", securityTraceConfig); + } + + @Override + public T execute(Class responseType) { + SecurityTraceRequest body = securityTraceConfig.getRequest(); + + log.info("Ejecutando traza de seguridad: {}", body); + + HttpRequest request = HttpRequest.builder() + .url(securityTraceConfig.getUrl()) + .method(HttpRequest.HttpMethod.POST) + .body(body) + .responseType(responseType) + .connectTimeout(securityTraceConfig.getTimeout().getConnect()) + .readTimeout(securityTraceConfig.getTimeout().getResponse()) + .build(); + + log.debug("Request configurado: {}", request); + + try { + T response = httpClientUseCase.execute(request); + + log.info("Solicitud de traza de seguridad exitosa: {}", response); + + return response; + } catch (HttpStatusCodeException e) { + log.error( + "Error HTTP ejecutando traza de seguridad: {} - {}", + e.getStatusCode(), + e.getMessage() + ); + + throw e; + } catch (Exception e) { + log.error("Error ejecutando traza de seguridad: {}", e.getMessage()); + + throw HttpStatusCodeException.serviceUnavailable("503"); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/service_status/application/usecase/ServiceStatusUseCase.java b/src/main/java/com/banesco/module/service_status/application/usecase/ServiceStatusUseCase.java new file mode 100644 index 0000000..4e6b537 --- /dev/null +++ b/src/main/java/com/banesco/module/service_status/application/usecase/ServiceStatusUseCase.java @@ -0,0 +1,10 @@ +package com.banesco.module.service_status.application.usecase; + +import com.banesco.module.service_status.domain.dto.request.ServiceStatusRequest; + +public interface ServiceStatusUseCase { + T execute( + ServiceStatusRequest params, + Class responseType + ); +} diff --git a/src/main/java/com/banesco/module/service_status/domain/dto/request/ServiceStatusRequest.java b/src/main/java/com/banesco/module/service_status/domain/dto/request/ServiceStatusRequest.java new file mode 100644 index 0000000..db697dd --- /dev/null +++ b/src/main/java/com/banesco/module/service_status/domain/dto/request/ServiceStatusRequest.java @@ -0,0 +1,19 @@ +package com.banesco.module.service_status.domain.dto.request; + +import com.banesco.common.domain.model.BankService; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ServiceStatusRequest { + private String applicationId; + private String transactionId; + private BankService bankService; +} diff --git a/src/main/java/com/banesco/module/service_status/domain/dto/response/ServiceStatusResponse.java b/src/main/java/com/banesco/module/service_status/domain/dto/response/ServiceStatusResponse.java new file mode 100644 index 0000000..624b220 --- /dev/null +++ b/src/main/java/com/banesco/module/service_status/domain/dto/response/ServiceStatusResponse.java @@ -0,0 +1,14 @@ +package com.banesco.module.service_status.domain.dto.response; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class ServiceStatusResponse { + private String status; +} diff --git a/src/main/java/com/banesco/module/service_status/domain/model/ServiceStatusConfig.java b/src/main/java/com/banesco/module/service_status/domain/model/ServiceStatusConfig.java new file mode 100644 index 0000000..b9cef33 --- /dev/null +++ b/src/main/java/com/banesco/module/service_status/domain/model/ServiceStatusConfig.java @@ -0,0 +1,16 @@ +package com.banesco.module.service_status.domain.model; + +import com.banesco.common.domain.model.TimeoutConfig; +import com.banesco.module.service_status.domain.dto.request.ServiceStatusRequest; +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@RegisterForReflection +public class ServiceStatusConfig { + private String url; + private TimeoutConfig timeout; + private ServiceStatusRequest request; +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/service_status/infrastructure/client/ServiceStatusClient.java b/src/main/java/com/banesco/module/service_status/infrastructure/client/ServiceStatusClient.java new file mode 100644 index 0000000..1dbcc5c --- /dev/null +++ b/src/main/java/com/banesco/module/service_status/infrastructure/client/ServiceStatusClient.java @@ -0,0 +1,74 @@ +package com.banesco.module.service_status.infrastructure.client; + +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.exception.HttpStatusCodeException; +import com.banesco.common.domain.model.HttpRequest; +import com.banesco.common.infrastructure.config.RestClientConfig; +import com.banesco.module.service_status.application.usecase.ServiceStatusUseCase; +import com.banesco.module.service_status.domain.dto.request.ServiceStatusRequest; +import com.banesco.module.service_status.domain.model.ServiceStatusConfig; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ApplicationScoped +public class ServiceStatusClient implements ServiceStatusUseCase { + private final HttpClientUseCase httpClientUseCase; + private final ServiceStatusConfig serviceStatusConfig; + + @Inject + public ServiceStatusClient( + HttpClientUseCase httpClientUseCase, + RestClientConfig restClientConfig + ) { + this.httpClientUseCase = httpClientUseCase; + this.serviceStatusConfig = restClientConfig.getServiceStatusConfig(); + log.info("Configuración cargada para service-status: {}", serviceStatusConfig); + } + + @Override + public T execute( + ServiceStatusRequest params, + Class responseType + ) { + ServiceStatusRequest body = ServiceStatusRequest.builder() + .applicationId(params.getApplicationId()) + .transactionId(params.getTransactionId()) + .bankService(serviceStatusConfig.getRequest().getBankService()) + .build(); + + log.info("Ejecutando verificación de estado del servicio: {}", body); + + HttpRequest request = HttpRequest.builder() + .url(serviceStatusConfig.getUrl()) + .method(HttpRequest.HttpMethod.POST) + .body(body) + .responseType(responseType) + .connectTimeout(serviceStatusConfig.getTimeout().getConnect()) + .readTimeout(serviceStatusConfig.getTimeout().getResponse()) + .build(); + + log.debug("Request configurado: {}", request); + + try { + T response = httpClientUseCase.execute(request); + + log.info("Solicitud de estado del servicio exitosa: {}", response); + + return response; + } catch (HttpStatusCodeException e) { + log.error( + "Error HTTP ejecutando verificación de estado del servicio: {} - {}", + e.getStatusCode(), + e.getMessage() + ); + + throw e; + } catch (Exception e) { + log.error("Error ejecutando verificación de estado del servicio: {}", e.getMessage()); + + throw HttpStatusCodeException.serviceUnavailable("503"); + } + } +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..93f9212 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,20 @@ +quarkus: + http: + port: 8082 + idle-timeout: 30s + thread-pool: + max-threads: 100 + core-threads: 1 + +api: + source-id: BLCPD + read-messages: + from-props: true + busLogalCustomerProductDirectory: + messages: + key: 'busLogalCustomerProductDirectory' + content: '[{"backendCode":"200","httpCode":200,"statusCode":"200","description":"Operacion exitosa"},{"backendCode":"R404","httpCode":404,"statusCode":"404","description":"Datos de validación no encontrado."},{"backendCode":"503","httpCode":503,"statusCode":"503","description":"Uso interno"},{"backendCode":"422","httpCode":422,"statusCode":"422","description":"Uso interno"},{"backendCode":"500","httpCode":500,"statusCode":"500","description":"Uso interno"},{"backendCode":"100","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-382505","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-380002","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"ERROR","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"400","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"401","httpCode":401,"statusCode":"401","description":"Uso interno"},{"backendCode":"403","httpCode":403,"statusCode":"403","description":"Uso interno"},{"backendCode":"404","httpCode":404,"statusCode":"404","description":"Uso interno"},{"backendCode":"default","httpCode":409,"statusCode":"409","description":"Conflicto"},{"backendCode":"424","httpCode":424,"statusCode":"424","description":"Error de dependencia"},{"backendCode":"VDE01","httpCode":400,"statusCode":"VDE01","description":"VDE01 - Error en dato de entrada obligatorio: %s"},{"backendCode":"VDE02","httpCode":400,"statusCode":"VDE02","description":"VDE02 - Error en valor permitido para campo: %s"},{"backend_code":"VRN04","http_code":"503","status_code":"VRN04","description":"Servicio en horario de mantenimiento","status":"error"},{"backend_code":"204","http_code":"200","status_code":"200","description":"Cliente sin productos","status":"ok"}]' + rest-client: + dom-legal-customer-product-directory: '{"url":"http://localhost:8083/dom-legal-customer-product-directory/retrieve/{customerIbsNumber}/{appId}/{customerReferenceFintechId}","timeout":{"connect":20000,"response":20000}}' + security-trace: '{"url":"http://api-register-security-route-apis-banesco-dev.apps.desplakur3.desintra.banesco.com/register-security/save","timeout":{"connect":20000,"response":20000},"request":{"sp":"spAPI_Traza","eventCod":"CANCTARJ","bankCod":"01","curCod":"BS"}}' + service-status: '{"url":"http://api-get-service-status-route-apis-banesco-dev.apps.desplakur3.desintra.banesco.com/service/status","timeout":{"connect":20000,"response":20000},"request":{"applicationId": "","transactionId": "","bankService": {"bankCode": "01","serviceCode": "APIFI","eventCode": "P2PVUEL"}}}' \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..bd100a3 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,41 @@ +quarkus: + application: + name: bus-legal-customer-product-directory + version: 1.0.0 + ## Profile + profile: dev + http: + non-application-root-path: actuator + native: + file-encoding: UTF-8 + container-build: true + builder-image: quay.io/quarkus/ubi-quarkus-mandrel-builder-image:23.0.5.0-Final-java17 + container-runtime: docker + additional-build-args: + - -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime + native-image-xmx: 6G + resources: + excludes: resources/*.yaml + ## OpenApi + smallrye-openapi: + path: /openapi + enable: 'true' + ## Swagger IU + swagger-ui: + path: /swagger-ui + always-include: 'true' + ## Logs + log: + level: INFO + console: + enable: true + format: "%d{HH:mm:ss.SSS} %-5p [%t] [%X{requestId}] %c{1} - %s%e%n" + smallrye-health: + root-path: /actuator/health + liveness-path: /actuator/health/live + readiness-path: /actuator/health/ready + ui: + enable: false + debug: + print-startup-times: true + reflection: false \ No newline at end of file