commit 52d66d7663674df3a8a996665a87df3ee9bf8a73 Author: Ramon Ramirez Date: Sun Jan 4 18:02:38 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..e15aa93 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# rec-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/rec-legal-customer-product-directory-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult . + +## Provided Code + +### REST + +Easily start your REST Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) 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..c1054fc --- /dev/null +++ b/pom.xml @@ -0,0 +1,157 @@ + + + 4.0.0 + com.banesco + rec-legal-customer-product-directory + 1.0.0-SNAPSHOT + + + 17 + 17 + 17 + 3.14.1 + 3.5.4 + 3.5.4 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.30.5 + 1.18.42 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${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 + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + + ${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 + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${compiler-plugin-surefire.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${compiler-plugin-failsafe.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + false + true + + + + \ No newline at end of file diff --git a/src/main/docker/Dockerfile.jvm b/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..ee8a532 --- /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/rec-legal-customer-product-directory-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/rec-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/rec-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..ca591e9 --- /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/rec-legal-customer-product-directory-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/rec-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/rec-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..427759f --- /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/rec-legal-customer-product-directory . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/rec-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..4cfac97 --- /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/rec-legal-customer-product-directory . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/rec-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/ErrorResponseHelper.java b/src/main/java/com/banesco/common/application/helper/ErrorResponseHelper.java new file mode 100644 index 0000000..4f2880a --- /dev/null +++ b/src/main/java/com/banesco/common/application/helper/ErrorResponseHelper.java @@ -0,0 +1,219 @@ +package com.banesco.common.application.helper; + +import com.banesco.common.domain.exception.InternalServerException; +import com.banesco.common.infrastructure.config.ErrorMessagesConfig; +import com.banesco.common.domain.exception.BaseApiException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.StatusResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import jakarta.ws.rs.core.Response; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@ApplicationScoped +public class ErrorResponseHelper { + + private final ErrorMessagesConfig errorMessagesConfig; + 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 ErrorResponseHelper( + ObjectMapper objectMapper, + ErrorMessagesConfig errorMessagesConfig + ) { + this.objectMapper = objectMapper; + this.errorMessagesConfig = errorMessagesConfig; + this.errorMappings = initializeErrorMappings(); + } + + public static class ErrorMapping { + @JsonProperty("backendCode") + String backendCode; + @JsonProperty("httpCode") + int httpCode; + @JsonProperty("statusCode") + String statusCode; + @JsonProperty("description") + String description; + } + + public Response handleException(BaseApiException exception) { + return buildErrorResponse(exception); + } + + public Response handleGenericException(Exception exception) { + log.error("Error interno no controlado: {}", exception.getMessage(), exception); + return buildErrorResponse(new InternalServerException("500", null)); + } + + public StatusResponse createSuccessResponse(String code) { + ErrorMapping successMapping = getError(code); + + return StatusResponse.builder() + .statusCode(successMapping.statusCode) + .message(successMapping.description) + .build(); + } + + public StatusResponse createErrorResponse( + ErrorMapping mapping, + String fieldPath + ) { + String message = mapping.description; + + if (fieldPath != null && message != null && message.contains("%s")) { + message = String.format(message, fieldPath); + } + + return StatusResponse.builder() + .statusCode(mapping.statusCode) + .message(message) + .build(); + } + + public StatusResponse handleGenericException(BaseApiException exception) { + ErrorMapping mapping = errorMappings.getOrDefault( + exception.getErrorCode(), + errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping()) + ); + StatusResponse status = createErrorResponse( + mapping, null + ); + + log.error( + "[{}] Error {} -> {}", + exception.getExceptionType(), + exception.getErrorCode(), + status.getMessage() + ); + + return status; + } + + public ApiResponse buildServiceUnavailableResponse() { + return new ApiResponse<>(null, createSuccessResponse("503")); + } + + private ErrorMapping getError(String errorCode) { + return errorMappings.getOrDefault( + errorCode, errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping()) + ); + } + + private Response buildErrorResponse(BaseApiException exception) { + ErrorMapping mapping = errorMappings.getOrDefault( + exception.getErrorCode(), + errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping()) + ); + StatusResponse status = createErrorResponse( + mapping, exception.getFieldPath() + ); + + + log.error( + "[{}] Error {} -> {}", + exception.getExceptionType(), + exception.getErrorCode(), + status.getMessage() + ); + + return Response.status(mapping.httpCode) + .entity(new ApiResponse<>(null, status)) + .build(); + } + + private Map initializeErrorMappings() { + try { + String json; + + if (isReadingFromProps()) { + json = errorMessagesConfig.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.backendCode != null && !mapping.backendCode.trim().isEmpty()) { + if (result.containsKey(mapping.backendCode)) { + log.warn("Clave duplicada encontrada en mappings de errores: {}", mapping.backendCode); + } else { + result.put(mapping.backendCode, mapping); + } + } else { + log.warn("Ignorando mapping sin backendCode válido: {}", mapping.description); + } + } + + 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 = ErrorResponseHelper.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.backendCode = ERROR_DEFAULT; + mapping.httpCode = 500; + mapping.statusCode = "500"; + mapping.description = "Error interno del servidor"; + return mapping; + } + + private ErrorMapping createSuccessMapping() { + ErrorMapping mapping = new ErrorMapping(); + mapping.backendCode = SUCCESS_DEFAULT; + mapping.httpCode = 200; + mapping.statusCode = "000"; + mapping.description = "Operación exitosa"; + return mapping; + } + + public boolean isReadingFromProps() { + return errorMessagesConfig.isReadFromProps(); + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java b/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java new file mode 100644 index 0000000..87409b6 --- /dev/null +++ b/src/main/java/com/banesco/common/application/helper/RequestValidatorHelper.java @@ -0,0 +1,43 @@ +package com.banesco.common.application.helper; + +import com.banesco.common.domain.exception.BadRequestException; +import com.banesco.common.infrastructure.config.RequestValidationConfig; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class RequestValidatorHelper { + private final RequestValidationConfig config; + + @Inject + public RequestValidatorHelper(RequestValidationConfig config) { + this.config = config; + } + + public void validateRequired(LegalCustomerProductDirectoryRequest request) { + checkEmpty(request.getCustomerIbsNumber(), "customerIbsNumber"); + checkEmpty(request.getCustomerReferenceFintechId(), "customerReferenceFintechId"); + checkEmpty(request.getAppId(), "appId"); + } + + public void validateFieldValues(LegalCustomerProductDirectoryRequest request) { + validate(request.getCustomerIbsNumber(), config.customerIbsNumber(), "customerIbsNumber"); + validate(request.getAccountStatus(), config.accountStatus(), "accountStatus"); + validate(request.getProductCvCode(), config.productCvCode(), "productCvCode"); + validate(request.getLimitType(), config.limitType(), "limitType"); + validate(request.getCasheaIndicator(), config.cacheaIndicator(), "casheaIndicator"); + } + + private void checkEmpty(String value, String fieldName) { + if (value == null || value.trim().isEmpty()) { + throw new BadRequestException("VDE01", fieldName); + } + } + + private void validate(String value, String regex, String fieldName) { + if (value != null && !value.isEmpty() && !value.matches(regex)) { + throw new BadRequestException("VDE02", fieldName); + } + } +} 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..c772de9 --- /dev/null +++ b/src/main/java/com/banesco/common/application/service/HttpClientService.java @@ -0,0 +1,124 @@ +package com.banesco.common.application.service; + +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.model.HttpRequest; +import jakarta.ws.rs.core.MediaType; +import lombok.extern.slf4j.Slf4j; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; + +import java.util.concurrent.TimeUnit; +import java.util.Map; + +@Slf4j +@ApplicationScoped +public class HttpClientService implements HttpClientUseCase { + + @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(response, request); + + } + } + + private Response buildRequest(Invocation.Builder builder, HttpRequest request) { + log.info("Método HTTP: {}, Headers enviados: {}", request.getMethod(), builder); + + 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(Response response, HttpRequest request) { + log.info("Respuesta {} {} - Status: {}", request.getMethod(), request.getUrl(), response.getStatus()); + + if (response.getStatus() >= 200 && response.getStatus() < 300) { + try { + 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; + } catch (Exception e) { + log.error("Error parseando respuesta {} {}: {}", request.getMethod(), request.getUrl(), e.getMessage()); + throw new HttpClientException("Error parseando respuesta", e); + } + } else { + String errorBody = response.readEntity(String.class); + log.error("Error HTTP {} {} - Status: {} - Body: {}", request.getMethod(), request.getUrl(), response.getStatus(), errorBody); + throw new HttpClientException("HTTP Error " + response.getStatus() + ": " + errorBody); + } + } + + public static class HttpClientException extends RuntimeException { + public HttpClientException(String message) { + super(message); + } + + public HttpClientException(String message, Throwable cause) { + super(message, cause); + } + } +} \ 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/BadRequestException.java b/src/main/java/com/banesco/common/domain/exception/BadRequestException.java new file mode 100644 index 0000000..8c7630a --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/BadRequestException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class BadRequestException extends BaseApiException { + public BadRequestException(String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, "bad-request"); + } + + public BadRequestException(String errorCode, String fieldPath) { + super(errorCode, fieldPath, "bad-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/InternalServerException.java b/src/main/java/com/banesco/common/domain/exception/InternalServerException.java new file mode 100644 index 0000000..c96d30d --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/InternalServerException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class InternalServerException extends BaseApiException { + public InternalServerException(String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, "internal-server"); + } + + public InternalServerException(String errorCode, String fieldPath) { + super(errorCode, fieldPath, "internal-server"); + } +} diff --git a/src/main/java/com/banesco/common/domain/exception/NotFoundException.java b/src/main/java/com/banesco/common/domain/exception/NotFoundException.java new file mode 100644 index 0000000..3593fb3 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/NotFoundException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class NotFoundException extends BaseApiException { + public NotFoundException(String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, "not-found"); + } + + public NotFoundException(String errorCode, String fieldPath) { + super(errorCode, fieldPath, "not-found"); + } +} diff --git a/src/main/java/com/banesco/common/domain/exception/ServiceUnavailableException.java b/src/main/java/com/banesco/common/domain/exception/ServiceUnavailableException.java new file mode 100644 index 0000000..f5195ad --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/ServiceUnavailableException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class ServiceUnavailableException extends BaseApiException { + public ServiceUnavailableException(String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, "service-unavailable"); + } + + public ServiceUnavailableException(String errorCode, String fieldPath) { + super(errorCode, fieldPath, "service-unavailable"); + } +} diff --git a/src/main/java/com/banesco/common/domain/exception/UnauthorizedException.java b/src/main/java/com/banesco/common/domain/exception/UnauthorizedException.java new file mode 100644 index 0000000..e8994d4 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/exception/UnauthorizedException.java @@ -0,0 +1,11 @@ +package com.banesco.common.domain.exception; + +public class UnauthorizedException extends BaseApiException { + public UnauthorizedException(String errorCode, String message, String fieldPath) { + super(errorCode, message, fieldPath, "unauthorized"); + } + + public UnauthorizedException(String errorCode, String fieldPath) { + super(errorCode, fieldPath, "unauthorized"); + } +} 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..3c3a8f9 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/ApiResponse.java @@ -0,0 +1,13 @@ +package com.banesco.common.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class ApiResponse { + private T data; + private StatusResponse statusResponse; +} diff --git a/src/main/java/com/banesco/common/domain/model/CurrencyType.kt b/src/main/java/com/banesco/common/domain/model/CurrencyType.kt new file mode 100644 index 0000000..af7074e --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/CurrencyType.kt @@ -0,0 +1,8 @@ +package com.banesco.common.domain.model + +enum class CurrencyType { + BASE, + REPORTING, + SECONDARY, + TRANSFER +} \ No newline at end of file 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..4c8b082 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/HttpRequest.java @@ -0,0 +1,25 @@ +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..17dc121 --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Identifier.java @@ -0,0 +1,13 @@ +package com.banesco.common.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Identifier { + private String identifierValue; + private String issuingAuthority; +} 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..48963be --- /dev/null +++ b/src/main/java/com/banesco/common/domain/model/Name.java @@ -0,0 +1,12 @@ +package com.banesco.common.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +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/infrastructure/config/ErrorMessagesConfig.java b/src/main/java/com/banesco/common/infrastructure/config/ErrorMessagesConfig.java new file mode 100644 index 0000000..63fc8bd --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/config/ErrorMessagesConfig.java @@ -0,0 +1,32 @@ +package com.banesco.common.infrastructure.config; + +import jakarta.enterprise.context.ApplicationScoped; +import lombok.Getter; +import org.eclipse.microprofile.config.Config; +import jakarta.inject.Inject; + +@ApplicationScoped +@Getter +public class ErrorMessagesConfig { + + private final boolean readFromProps; + private final String errorMessagesJson; + private final String messagesKey; + + private static final String KEY = "recLogalCustomerProductDirectory"; + + @Inject + public ErrorMessagesConfig(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..90cd90e --- /dev/null +++ b/src/main/java/com/banesco/common/infrastructure/config/RestClientConfig.java @@ -0,0 +1,85 @@ +package com.banesco.common.infrastructure.config; + +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import jakarta.enterprise.context.ApplicationScoped; +import org.eclipse.microprofile.config.Config; +import jakarta.inject.Inject; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; + +@Slf4j +@ApplicationScoped +public class RestClientConfig { + + private final Config config; + private final ObjectMapper objectMapper; + + @Inject + public RestClientConfig(Config config, ObjectMapper objectMapper) { + this.config = config; + this.objectMapper = objectMapper; + } + + public BusConfig getBusLegalCustomerConfig() { + try { + String json = config.getValue("api.rest-client.bus-legal-customer-product-directory", String.class); + log.info("Configurando bus-legal-customer-product-directory: {}", json); + + Map configMap = objectMapper.readValue(json, new TypeReference<>() {}); + return objectMapper.convertValue(configMap, BusConfig.class); + + } catch (Exception e) { + log.error("Error cargando config bus-legal-customer-product-directory", e); + throw new RuntimeException("Error cargando configuración", e); + } + } + + public RegisterSecurityConfig getRegisterSecurityConfig() { + try { + String json = config.getValue("api.rest-client.register-security", String.class); + log.info("Configurando register-security: {}", json); + + Map configMap = objectMapper.readValue(json, new TypeReference<>() {}); + return objectMapper.convertValue(configMap, RegisterSecurityConfig.class); + + } catch (Exception e) { + log.error("Error cargando config register-security", e); + throw new RuntimeException("Error cargando configuración", e); + } + } + + @Getter + @ToString + public static class BusConfig { + private String url; + private TimeoutConfig timeout; + private Map config; + } + + @Getter + @ToString + public static class RegisterSecurityConfig { + private String url; + private TimeoutConfig timeout; + private SecurityConfig config; + } + + @Getter + @ToString + public static class TimeoutConfig { + private int connect; + private int response; + } + + @Getter + @ToString + public static class SecurityConfig { + private String sp; + private String eventCod; + private String bankCod; + private String curCod; + } +} 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..01111f6 --- /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, 8)); + } + + @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..23db675 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/Account.java @@ -0,0 +1,23 @@ +package com.banesco.module.account.domain.model; + +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Account { + private AccountStatus accountStatus; + private List accountIdentification; + private List accountDate; + private String accountType; + private String accountPurpose; + private List accountBalance; + private List accountCurrency; + private String accountDescription; + private String accountName; + private List accountInvolvement; +} \ 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..4d3167f --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountBalance.java @@ -0,0 +1,15 @@ +package com.banesco.module.account.domain.model; + +import lombok.*; + +import java.math.BigDecimal; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class AccountBalance { + private BigDecimal balanceAmount; + private String 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..cd398d9 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountCurrency.java @@ -0,0 +1,14 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.common.domain.model.CurrencyType; +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class AccountCurrency { + private String currencyCode; + private CurrencyType currencyType; +} diff --git a/src/main/java/com/banesco/module/account/domain/model/AccountDateTime.java b/src/main/java/com/banesco/module/account/domain/model/AccountDateTime.java new file mode 100644 index 0000000..b3e121e --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountDateTime.java @@ -0,0 +1,13 @@ +package com.banesco.module.account.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class AccountDateTime { + private String dateType; + private String date; +} 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..40cd70b --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountIdentification.java @@ -0,0 +1,14 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.common.domain.model.Identifier; +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class AccountIdentification { + private String accountIdentificationType; + private Identifier accountIdentification; +} 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..f819d6d --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountInvolvement.java @@ -0,0 +1,14 @@ +package com.banesco.module.account.domain.model; + +import com.banesco.module.party.domain.model.Party; +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class AccountInvolvement { + private String accountInvolvementType; + private Party partyReference; +} 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..69d1423 --- /dev/null +++ b/src/main/java/com/banesco/module/account/domain/model/AccountStatus.java @@ -0,0 +1,13 @@ +package com.banesco.module.account.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class AccountStatus { + private String status; + private String statusType; +} \ No newline at end of file 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..15720b9 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/application/service/LegalCustomerProductDirectoryService.java @@ -0,0 +1,69 @@ +package com.banesco.module.legal_customer_product_directory.application.service; + +import com.banesco.common.application.helper.ErrorResponseHelper; +import com.banesco.common.application.helper.RequestValidatorHelper; +import com.banesco.common.domain.exception.BaseApiException; +import com.banesco.common.domain.exception.ServiceUnavailableException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.module.legal_customer_product_directory.application.usecase.BusinessUseCase; +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.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@ApplicationScoped +public class LegalCustomerProductDirectoryService implements LegalCustomerProductDirectoryUseCase { + + private final RequestValidatorHelper requestValidatorHelper; + private final BusinessUseCase businessUseCase; + + @Inject + public LegalCustomerProductDirectoryService( + ErrorResponseHelper errorResponseHelper, + RequestValidatorHelper requestValidatorHelper, + BusinessUseCase businessUseCase + ) { + this.requestValidatorHelper = requestValidatorHelper; + this.businessUseCase = businessUseCase; + } + + @Override + public ApiResponse execute( + LegalCustomerProductDirectoryRequest request + ) { + log.info("Iniciando ejecución para el cliente: {}", request.getCustomerIbsNumber()); + + validate(request); + + try { + return business(request); + } catch (BaseApiException e) { + throw e; + } catch (Exception e) { + throw new ServiceUnavailableException("503", e.getMessage(), null); + } + } + + private ApiResponse business( + LegalCustomerProductDirectoryRequest request + ) { + log.info("Calling business service for client: {}", request.getCustomerIbsNumber()); + + return businessUseCase.execute( + request.getCustomerIbsNumber(), + request.toBusinessRequest(), + request.toBusinessHeaders(), + LegalCustomerProductDirectoryResponse.class + ); + } + + private void validate( + LegalCustomerProductDirectoryRequest request + ) { + requestValidatorHelper.validateRequired(request); + requestValidatorHelper.validateFieldValues(request); + } +} \ No newline at end of file diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/BusinessUseCase.java b/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/BusinessUseCase.java new file mode 100644 index 0000000..8e9fd65 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/application/usecase/BusinessUseCase.java @@ -0,0 +1,14 @@ +package com.banesco.module.legal_customer_product_directory.application.usecase; + +import com.banesco.common.domain.model.ApiResponse; + +import java.util.Map; + +public interface BusinessUseCase { + ApiResponse execute( + String customerIbsNumber, + Map queryParams, + Map headers, + 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..5d86c71 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/request/LegalCustomerProductDirectoryRequest.java @@ -0,0 +1,55 @@ +package com.banesco.module.legal_customer_product_directory.domain.dto.request; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.util.Map; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class LegalCustomerProductDirectoryRequest { + + @NonNull + private String customerIbsNumber; // VCUSCUN - Obligatorio (Número de cliente IBS) + @NonNull + private String customerReferenceFintechId; // Header obligatorio + @NonNull + private String appId; // Header obligatorio + + 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 toBusinessHeaders() { + return Map.of( + "appId", getAppId(), + "customerReferenceFintechId", getCustomerReferenceFintechId() + ); + } + + public Map toBusinessRequest() { + return Map.of( + "bankNumber", getBankNumber(), + "currencyCode", getCurrencyCode(), + "accountStatus", getAccountStatus(), + "productCvCode", getProductCvCode(), + "productCode", getProductCode(), + "channelCode", getChannelCode(), + "serviceType", getServiceType(), + "affiliationStatus", getAffiliationStatus(), + "limitType", getLimitType(), + "cashIndicator", getCasheaIndicator() + ); + } +} \ 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..0642d56 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/dto/response/LegalCustomerProductDirectoryResponse.java @@ -0,0 +1,16 @@ +package com.banesco.module.legal_customer_product_directory.domain.dto.response; + +import com.banesco.common.domain.model.StatusResponse; +import com.banesco.module.legal_customer_product_directory.domain.model.CustomerProductAndServiceDirectory; +import lombok.*; + +@Getter +@Setter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LegalCustomerProductDirectoryResponse { + private CustomerProductAndServiceDirectory customerProductAndServiceDirectory; + private StatusResponse statusResponse; +} 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..104e923 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/domain/model/CustomerProductAndServiceDirectory.java @@ -0,0 +1,17 @@ +package com.banesco.module.legal_customer_product_directory.domain.model; + +import com.banesco.module.account.domain.model.Account; +import com.banesco.module.party.domain.model.Party; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class CustomerProductAndServiceDirectory { + private Party party; + private List accounts; +} diff --git a/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/client/BusLegalCustomerDirectoryClient.java b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/client/BusLegalCustomerDirectoryClient.java new file mode 100644 index 0000000..ff8d015 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/client/BusLegalCustomerDirectoryClient.java @@ -0,0 +1,63 @@ +package com.banesco.module.legal_customer_product_directory.infrastructure.client; + +import com.banesco.common.application.helper.ErrorResponseHelper; +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.HttpRequest; +import com.banesco.common.infrastructure.config.RestClientConfig; +import com.banesco.module.legal_customer_product_directory.application.usecase.BusinessUseCase; +import lombok.extern.slf4j.Slf4j; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.Map; + +@Slf4j +@ApplicationScoped +public class BusLegalCustomerDirectoryClient implements BusinessUseCase { + private final HttpClientUseCase httpClientUseCase; + private final ErrorResponseHelper errorResponseHelper; + private final RestClientConfig.BusConfig busConfig; + + @Inject + public BusLegalCustomerDirectoryClient( + HttpClientUseCase httpClientUseCase, + ErrorResponseHelper errorResponseHelper, + RestClientConfig restClientConfig + ) { + this.httpClientUseCase = httpClientUseCase; + this.errorResponseHelper = errorResponseHelper; + this.busConfig = restClientConfig.getBusLegalCustomerConfig(); + log.info("Configuración cargada para bus-legal-customer-directory: {}", busConfig); + } + + @Override + public ApiResponse execute( + String customerIbsNumber, + Map queryParams, + Map headers, + Class responseType + ) { + log.info("Consultando información del cliente: {}", customerIbsNumber); + + try { + HttpRequest request = HttpRequest.builder() + .url(busConfig.getUrl()) + .method(HttpRequest.HttpMethod.GET) + .pathParams(Map.of("customerIbsNumber", customerIbsNumber)) + .queryParams(queryParams) + .headers(headers) + .responseType(responseType) + .connectTimeout(busConfig.getTimeout().getConnect()) + .readTimeout(busConfig.getTimeout().getResponse()) + .build(); + + log.debug("Request configurado: {}", request); + + return httpClientUseCase.execute(request); + } catch (Exception e) { + log.error("Error consultando cliente {}: {}", customerIbsNumber, e.getMessage()); + return errorResponseHelper.buildServiceUnavailableResponse(); + } + } +} \ 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..1e6a372 --- /dev/null +++ b/src/main/java/com/banesco/module/legal_customer_product_directory/infrastructure/resource/LegalCustomerProductDirectoryResource.java @@ -0,0 +1,346 @@ +package com.banesco.module.legal_customer_product_directory.infrastructure.resource; + +import com.banesco.common.application.helper.ErrorResponseHelper; +import com.banesco.common.domain.exception.BaseApiException; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.module.legal_customer_product_directory.application.usecase.LegalCustomerProductDirectoryUseCase; +import com.banesco.module.legal_customer_product_directory.domain.dto.request.LegalCustomerProductDirectoryRequest; +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.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; + +@Slf4j +@Path("/rec-legal-customer-product-directory") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class LegalCustomerProductDirectoryResource { + private final LegalCustomerProductDirectoryUseCase useCase; + private final ErrorResponseHelper errorResponseHelper; + + @Inject + public LegalCustomerProductDirectoryResource( + LegalCustomerProductDirectoryUseCase useCase, + ErrorResponseHelper errorResponseHelper + ) { + this.useCase = useCase; + this.errorResponseHelper = errorResponseHelper; + } + + @GET + @Path("/retrieve/{customerIbsNumber}") + @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), + examples = @ExampleObject( + name = "Ejemplo exitoso", + value = """ + { + "data": { + "customerProductAndServiceDirectory": { + "party": { + "partyName": [ + { + "fullName": "TASCA RESTAURANT GOOD WORLD CEN, C.A." + } + ], + "partyType": "ORGANISATION", + "partyDateTime": [], + "partyIdentification": [ + { + "partyIdentificationType": "TAX_IDENTIFICATION_NUMBER", + "partyIdentification": { + "identifierValue": "J000000001", + "issuingAuthority": "" + } + } + ], + "partyLegalStructureType": "" + }, + "accounts": [ + { + "accountStatus": { + "status": "D" + }, + "accountIdentification": [ + { + "accountIdentificationType": "NUMERO_CUENTA", + "accountIdentification": { + "identifierValue": "01340025330253093528", + "issuingAuthority": "" + } + }, + { + "accountIdentificationType": "NUMERO_BANCO", + "accountIdentification": { + "identifierValue": "01", + "issuingAuthority": "" + } + }, + { + "accountIdentificationType": "CODIGO_PRODUCTO", + "accountIdentification": { + "identifierValue": "3980", + "issuingAuthority": "" + } + }, + { + "accountIdentificationType": "CLASE_CUENTA", + "accountIdentification": { + "identifierValue": "80", + "issuingAuthority": "" + } + }, + { + "accountIdentificationType": "TIPO_CUENTA", + "accountIdentification": { + "identifierValue": "MMK", + "issuingAuthority": "" + } + } + ], + "accountBalance": [ + { + "balanceAmount": 390417.36, + "balanceType": "SALDO_DISPONIBLE" + }, + { + "balanceAmount": 100000.00, + "balanceType": "MAXIMO_DIARIO" + }, + { + "balanceAmount": 100000.00, + "balanceType": "MAXIMO_TRANSACCION" + }, + { + "balanceAmount": 3000000.00, + "balanceType": "MAXIMO_MENSUAL" + }, + { + "balanceAmount": 0.01, + "balanceType": "MINIMO_TRANSACCION" + } + ], + "accountCurrency": [ + { + "currencyCode": "BS", + "currencyType": "BASE" + } + ], + "accountDescription": "TASCA RESTAURANT GOOD WORLD CEN, C.A.", + "accountInvolvement": [ + { + "accountInvolvementType": "CONTACTO_TELEFONICO", + "partyReference": { + "partyIdentification": [ + { + "partyIdentificationType": "TELEPHONE_NUMBER", + "partyIdentification": { + "identifierValue": "04122710660", + "issuingAuthority": "" + } + } + ] + } + } + ] + } + ] + } + }, + "statusResponse": { + "statusCode": "000", + "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), + 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), + 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), + 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( + @HeaderParam("customerReferenceFintechId") + @Parameter(description = "ID de la fintech", required = true, example = "pranical-test") + String customerReferenceFintechId, + + @HeaderParam("appId") + @Parameter(description = "ID de la aplicación", required = true, example = "DANIAPP") + String appId, + + @PathParam("customerIbsNumber") + @Parameter(description = "Número de cliente IBS (VCUSCUN)", required = true, example = "200053197") + String customerIbsNumber, + + @QueryParam("bankNumber") + @Parameter(description = "Número de banco (VACMBNK)", example = "01") + String bankNumber, + + @QueryParam("currencyCode") + @Parameter(description = "Código de moneda (VACMCCY)", example = "BS") + String currencyCode, + + @QueryParam("accountStatus") + @Parameter(description = "Estatus de cuenta (VACMAST). 'A'=Activa BS, 'O'=Activa USD, 'ACTBSUSD'=Ambas", example = "A") + String accountStatus, + + @QueryParam("productCvCode") + @Parameter(description = "Código de Producto Cuenta Verde (VACMPROCV). 'CV'=Cuenta verde, 'CVFL'=Cuenta verde con opción activa", example = "CV") + String productCvCode, + + @QueryParam("productCode") + @Parameter(description = "Código de producto (VACMPRO)", example = "3980") + String productCode, + + @QueryParam("channelCode") + @Parameter(description = "Código de canal (VAFILICANAL)", example = "APP") + String channelCode, + + @QueryParam("serviceType") + @Parameter(description = "Tipo de servicio (VAFILICOSER)", example = "P2P") + String serviceType, + + @QueryParam("affiliationStatus") + @Parameter(description = "Estatus de afiliación (VAFILISTATU)", example = "A") + String affiliationStatus, + + @QueryParam("limitType") + @Parameter(description = "Pagador/Receptor (VALIMIT). 'PAG'=Pagadora, 'REC'=Receptora", example = "PAG") + String limitType, + + @QueryParam("casheaIndicator") + @Parameter(description = "Indicador cash (VCASHEA). 'SI'=En tabla cashea, 'NO'=No en tabla cashea", example = "SI") + String casheaIndicator + ) { + log.info("Iniciando consulta para cliente IBS: {}", customerIbsNumber); + + try { + return Response.ok(useCase.execute( + LegalCustomerProductDirectoryRequest.builder() + .customerIbsNumber(customerIbsNumber) + .customerReferenceFintechId(customerReferenceFintechId) + .appId(appId) + .bankNumber(bankNumber) + .currencyCode(currencyCode) + .accountStatus(accountStatus) + .productCvCode(productCvCode) + .productCode(productCode) + .channelCode(channelCode) + .serviceType(serviceType) + .affiliationStatus(affiliationStatus) + .limitType(limitType) + .casheaIndicator(casheaIndicator) + .build() + )).build(); + } catch (BaseApiException e) { + return errorResponseHelper.handleException(e); + } catch (Exception e) { + return errorResponseHelper.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..9a0c190 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/Party.java @@ -0,0 +1,19 @@ +package com.banesco.module.party.domain.model; + +import com.banesco.common.domain.model.Name; +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Party { + private List partyName; + private PartyType partyType; + private List partyDateTime; + private List partyIdentification; + private String partyLegalStructureType; +} 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..07c3463 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyDateTime.java @@ -0,0 +1,13 @@ +package com.banesco.module.party.domain.model; + +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +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..41e9ca3 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyIdentification.java @@ -0,0 +1,14 @@ +package com.banesco.module.party.domain.model; + +import com.banesco.common.domain.model.Identifier; +import lombok.*; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +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..cebcb82 --- /dev/null +++ b/src/main/java/com/banesco/module/party/domain/model/PartyIdentificationType.java @@ -0,0 +1,19 @@ +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 +} \ 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/application/usecase/SecurityUseCase.java b/src/main/java/com/banesco/module/security/application/usecase/SecurityUseCase.java new file mode 100644 index 0000000..d328b05 --- /dev/null +++ b/src/main/java/com/banesco/module/security/application/usecase/SecurityUseCase.java @@ -0,0 +1,7 @@ +package com.banesco.module.security.application.usecase; + +import com.banesco.common.domain.model.ApiResponse; + +public interface SecurityUseCase { + ApiResponse execute(Class responseType); +} diff --git a/src/main/java/com/banesco/module/security/domain/dto/request/SecurityRequest.java b/src/main/java/com/banesco/module/security/domain/dto/request/SecurityRequest.java new file mode 100644 index 0000000..03c70ae --- /dev/null +++ b/src/main/java/com/banesco/module/security/domain/dto/request/SecurityRequest.java @@ -0,0 +1,38 @@ +package com.banesco.module.security.domain.dto.request; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.util.Date; + +@Getter +@ToString +@Builder +@NoArgsConstructor +@AllArgsConstructor +@RegisterForReflection +public class SecurityRequest { + private String codBan; + private String codMon; + private String codEve; + private String codEve2; + private String login; + private Date fecHor; + private String nacCli; + private Integer cedRifCli; + private String productoCli; + + private String objeto; + private Integer tipoRespuesta; + private String msgRespuesta; + private Integer tiempoRespuesta; + private String codFintech; + + private String cedRifFintech; + + private String tipoDispositivo; + private String desDispositivo; + private String ipCli; + + private String sp; +} diff --git a/src/main/java/com/banesco/module/security/infrastructure/client/RegisterSecurityClient.java b/src/main/java/com/banesco/module/security/infrastructure/client/RegisterSecurityClient.java new file mode 100644 index 0000000..1792dfc --- /dev/null +++ b/src/main/java/com/banesco/module/security/infrastructure/client/RegisterSecurityClient.java @@ -0,0 +1,85 @@ +package com.banesco.module.security.infrastructure.client; + +import com.banesco.common.application.helper.ErrorResponseHelper; +import com.banesco.common.application.usecase.HttpClientUseCase; +import com.banesco.common.domain.model.ApiResponse; +import com.banesco.common.domain.model.HttpRequest; +import com.banesco.common.infrastructure.config.RestClientConfig; +import com.banesco.module.security.application.usecase.SecurityUseCase; +import com.banesco.module.security.domain.dto.request.SecurityRequest; +import lombok.extern.slf4j.Slf4j; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@ApplicationScoped +public class RegisterSecurityClient implements SecurityUseCase { + private final HttpClientUseCase httpClientUseCase; + private final ErrorResponseHelper errorResponseHelper; + private final RestClientConfig.RegisterSecurityConfig securityConfig; + + @Inject + public RegisterSecurityClient( + HttpClientUseCase httpClientUseCase, + ErrorResponseHelper errorResponseHelper, + RestClientConfig restClientConfig + ) { + this.httpClientUseCase = httpClientUseCase; + this.errorResponseHelper = errorResponseHelper; + this.securityConfig = restClientConfig.getRegisterSecurityConfig(); + log.info("Configuración cargada para register-security: {}", securityConfig); + } + + @Override + public ApiResponse execute(Class responseType) { + log.info("Registrando traza de seguridad"); + + try { + SecurityRequest requestBody = createSecurityRequestBody(); + Map headers = createHeaders(); + HttpRequest request = buildHttpRequest(requestBody, headers, responseType); + + log.debug("Request body: {}", requestBody); + log.debug("Request configurado: {}", request); + + return httpClientUseCase.execute(request); + + } catch (Exception e) { + log.error("Error registrando traza de seguridad: {}", e.getMessage(), e); + return errorResponseHelper.buildServiceUnavailableResponse(); + } + } + + private SecurityRequest createSecurityRequestBody() { + return SecurityRequest.builder() + .codBan(securityConfig.getConfig().getBankCod()) + .codMon(securityConfig.getConfig().getCurCod()) + .codEve(securityConfig.getConfig().getEventCod()) + .codEve2(securityConfig.getConfig().getEventCod()) + .sp(securityConfig.getConfig().getSp()) + .build(); + } + + private Map createHeaders() { + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + return headers; + } + + private HttpRequest buildHttpRequest(SecurityRequest body, Map headers, Class responseType) { + return HttpRequest.builder() + .url(securityConfig.getUrl()) + .method(HttpRequest.HttpMethod.POST) + .body(body) + .headers(headers) + .responseType(responseType) + .connectTimeout(securityConfig.getTimeout().getConnect()) + .readTimeout(securityConfig.getTimeout().getResponse()) + .build(); + } + + +} \ 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..4a1612d --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,26 @@ +quarkus: + http: + port: 8081 + idle-timeout: 30s + thread-pool: + max-threads: 100 + core-threads: 1 + +api: + source-id: LCPD + allowed: + request-validation: + customer-ibs-number: '\d+' + account-status: '^(A|O|ACTBSUSD)$' + product-cv-code: '^(CV|CVFL)$' + limit-type: '^(PAG|REC)$' + cachea-indicator: '^(SI|NO)$' + read-messages: + from-props: true + recLogalCustomerProductDirectory: + messages: + key: 'recLogalCustomerProductDirectory' + 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"}]' + rest-client: + bus-legal-customer-product-directory: '{"url":"http://localhost:8082/bus-legal-customer-product-directory/retrieve/{customerIbsNumber}","timeout":{"connect":20000,"response":20000},"config":{}}' + register-security: '{"url":"http://api-register-security-route-apis-banesco-dev.apps.desplakur3.desintra.banesco.com/register-security/save","timeout":{"connect":20000,"response":20000},"config":{"sp":"spAPI_Traza","eventCod":"CANCTARJ","bankCod":"01","curCod":"BS"}}' \ 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..afba652 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,35 @@ +quarkus: + application: + name: rec-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" + debug: + print-startup-times: true + reflection: false \ No newline at end of file