first commit

This commit is contained in:
Ramon Ramirez 2026-01-29 20:06:39 -04:00
commit caab0f4943
79 changed files with 4836 additions and 0 deletions

5
.dockerignore Normal file
View File

@ -0,0 +1,5 @@
*
!target/*-runner
!target/*-runner.jar
!target/lib/*
!target/quarkus-app/*

45
.gitignore vendored Normal file
View File

@ -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/

3
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -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

54
README.md Normal file
View File

@ -0,0 +1,54 @@
# dom-service-order-status-control
This project uses Quarkus, the Supersonic Subatomic Java Framework.
If you want to learn more about Quarkus, please visit its website: <https://quarkus.io/>.
## 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 <http://localhost:8080/q/dev/>.
## 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 its 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/dom-service-order-status-control-1.0-native-quarkus-jdk17-runner`
If you want to learn more about building native executables, please consult <https://quarkus.io/guides/maven-tooling>.

295
mvnw vendored Normal file
View File

@ -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-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -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 "$@"

189
mvnw.cmd vendored Normal file
View File

@ -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-<version>,maven-mvnd-<version>-<platform>}/<hash>
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"

160
pom.xml Normal file
View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.banesco</groupId>
<artifactId>dom-service-order-status-control</artifactId>
<version>1.0-native-quarkus-jdk17</version>
<name>dom-service-order-status-control</name>
<description>API Domain - Active/Deactive service status</description>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<compiler-plugin.version>3.14.1</compiler-plugin.version>
<compiler-plugin-openshift.version>1.18.2</compiler-plugin-openshift.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.22.3</quarkus.platform.version>
<lombok.version>1.18.42</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-camel-bom</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jkube</groupId>
<artifactId>openshift-maven-plugin</artifactId>
<version>${compiler-plugin-openshift.version}</version>
<configuration>
<resources>
<volumeMounts>
<volumeMount>
<name>maven-volumen</name>
<mountPath>/root/.m2</mountPath>
</volumeMount>
</volumeMounts>
</resources>
</configuration>
</plugin>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>native-image-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<quarkus.package.type>native</quarkus.package.type>
<quarkus.native.enabled>true</quarkus.native.enabled>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application-dev.yaml</exclude>
<exclude>application-local.yaml</exclude>
</excludes>
</resource>
</resources>
</build>
</profile>
</profiles>
</project>

31
scripts/native/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
####
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
# It uses a micro base image, tuned for Quarkus native executables.
# It reduces the size of the resulting container image.
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
#
# Before building the container image run:
#
# ./mvnw package -Dnative
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/rec-legal-customer-product-directory .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/rec-legal-customer-product-directory
#
###
FROM quay.io/quarkus/quarkus-micro-image:2.0
RUN mkdir -p /work
ENV TZ="America/Caracas"
ENV LANGUAGE='en_US:en'
VOLUME /tmp
COPY /file/*-runner /work/app
RUN chmod -R 775 /work
RUN ls -ltra /work/
EXPOSE 8080
WORKDIR /work/
ENTRYPOINT ["./app", "-Dquarkus.http.host=0.0.0.0"]

View File

@ -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/dom-service-order-status-control-jvm .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/dom-service-order-status-control-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/dom-service-order-status-control-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" ]

View File

@ -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/dom-service-order-status-control-legacy-jar .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/dom-service-order-status-control-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/dom-service-order-status-control-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" ]

View File

@ -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/dom-service-order-status-control .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/dom-service-order-status-control
#
# 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"]

View File

@ -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/dom-service-order-status-control .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/dom-service-order-status-control
#
# 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"]

View File

@ -0,0 +1,234 @@
package com.banesco.common.application.helper;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.ApiResponse;
import com.banesco.common.domain.model.ErrorMapping;
import com.banesco.common.domain.model.StatusResponse;
import com.banesco.common.infrastructure.config.MessagesConfig;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@ApplicationScoped
@RegisterForReflection
public class MessageHelper {
private final MessagesConfig messagesConfig;
private final Map<String, ErrorMapping> 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 MessageHelper(
ObjectMapper objectMapper,
MessagesConfig messagesConfig
) {
this.objectMapper = objectMapper;
this.messagesConfig = messagesConfig;
this.errorMappings = initializeErrorMappings();
}
public Response handleSuccess(Object data, String statusCode) {
log.info(
"Respuesta exitosa controlada: {}",
statusCode
);
return buildResponse(data, statusCode);
}
public Response handleException(HttpStatusCodeException exception) {
log.error(
"Error interno controlado: {} -> {}",
exception.getStatusCode(),
exception.getErrorCode()
);
return buildErrorResponse(exception);
}
public Response handleGenericException(Exception exception) {
log.error("Error interno no controlado: {}", exception.getMessage());
return buildErrorResponse(HttpStatusCodeException.internalServer("500"));
}
public StatusResponse createStatusResponse(String code) {
ErrorMapping successMapping = getError(code);
return StatusResponse.builder()
.statusCode(successMapping.getStatusCode())
.message(successMapping.getDescription())
.build();
}
public StatusResponse createError(
ErrorMapping mapping,
String fieldPath
) {
String message = mapping.getDescription();
if (fieldPath != null && message != null && message.contains("%s")) {
message = String.format(message, fieldPath);
}
return StatusResponse.builder()
.statusCode(mapping.getStatusCode())
.message(message)
.build();
}
public <T> ApiResponse<T> buildServiceUnavailableResponse() {
return new ApiResponse<>(createStatusResponse("503"));
}
private ErrorMapping getError(String errorCode) {
return errorMappings.getOrDefault(
errorCode, errorMappings.getOrDefault(ERROR_DEFAULT, createDefaultMapping())
);
}
private Response buildErrorResponse(HttpStatusCodeException exception) {
ErrorMapping mapping = errorMappings.getOrDefault(
exception.getErrorCode(), errorMappings.getOrDefault(
String.valueOf(exception.getStatusCode()),
createDefaultMapping()
)
);
StatusResponse status = createError(mapping, exception.getFieldPath());
log.error(
"[{}] Message {} -> {}",
exception.getExceptionType(),
exception.getErrorCode(),
status.getMessage()
);
return Response.status(mapping.getHttpCode())
.entity(new ApiResponse<>(status))
.build();
}
private Response buildResponse(Object data, String statusCode) {
ErrorMapping mapping = errorMappings.getOrDefault(
statusCode, errorMappings.getOrDefault(
statusCode, createDefaultMapping()
)
);
StatusResponse status = createError(mapping, null);
log.error(
"[Success] Message {} -> {}",
statusCode,
status.getMessage()
);
return Response.status(mapping.getHttpCode())
.entity(new ApiResponse<>(data, status))
.build();
}
private Map<String, ErrorMapping> initializeErrorMappings() {
try {
String json;
if (isReadingFromProps()) {
json = messagesConfig.getErrorMessagesJson();
log.info("Cargando mensajes de errores desde properties");
} else {
json = loadFromJsonFile();
log.info("Cargando mensajes de errores desdel cliente JSON");
}
if (json == null || json.isEmpty()) {
log.warn("No se encontro JSON de errores");
return createDefaultMappings();
}
List<ErrorMapping> mappings = objectMapper.readValue(json, new TypeReference<List<ErrorMapping>>() {});
Map<String, ErrorMapping> result = new ConcurrentHashMap<>();
for (ErrorMapping mapping : mappings) {
if (mapping.getBackendCode() != null && !mapping.getBackendCode().trim().isEmpty()) {
if (result.containsKey(mapping.getBackendCode())) {
log.warn("Clave duplicada encontrada en mappings de errores: {}", mapping.getBackendCode());
} else {
result.put(mapping.getBackendCode(), mapping);
}
} else {
log.warn("Ignorando mapping sin backendCode valido: {}", mapping.getDescription());
}
}
log.info("Mappings de errores cargados exitosamente, total validos: {}", result.size());
return result;
} catch (Exception e) {
log.error("Error cargando mappings de errores: {}", e.getMessage());
return createDefaultMappings();
}
}
private String loadFromJsonFile() {
try (InputStream is = MessageHelper.class.getClassLoader().getResourceAsStream(ERROR_FILE_PATH)) {
if (is == null) {
log.warn("No se encontro 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<String, ErrorMapping> createDefaultMappings() {
Map<String, ErrorMapping> defaults = new ConcurrentHashMap<>();
defaults.put(ERROR_DEFAULT, createDefaultMapping());
defaults.put(SUCCESS_DEFAULT, createSuccessMapping());
return defaults;
}
private ErrorMapping createDefaultMapping() {
ErrorMapping mapping = new ErrorMapping();
mapping.setBackendCode(ERROR_DEFAULT);
mapping.setHttpCode(409);
mapping.setStatusCode("409");
mapping.setDescription("Conflicto");
return mapping;
}
private ErrorMapping createSuccessMapping() {
ErrorMapping mapping = new ErrorMapping();
mapping.setBackendCode(SUCCESS_DEFAULT);
mapping.setHttpCode(200);
mapping.setStatusCode("200");
mapping.setDescription("Operacion exitosa");
return mapping;
}
public boolean isReadingFromProps() {
return messagesConfig.isReadFromProps();
}
public boolean isSuccessStatusCode(StatusResponse statusResponse) {
if (statusResponse == null || statusResponse.getStatusCode() == null) {
return false;
}
try {
int statusCode = Integer.parseInt(statusResponse.getStatusCode());
return statusCode >= 200 && statusCode < 300;
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -0,0 +1,172 @@
package com.banesco.common.application.helper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.util.internal.StringUtil;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.util.*;
@Slf4j
@ApplicationScoped
@RegisterForReflection
public class SerializationHelper {
private final ObjectMapper objectMapper;
@Inject
public SerializationHelper(
ObjectMapper objectMapper
) {
this.objectMapper = objectMapper;
}
public String encodeBase64(Object object) {
try {
String jsonString = objectMapper.writeValueAsString(object);
return Base64.getEncoder().encodeToString(jsonString.getBytes());
} catch (Exception e) {
log.error("Error al codificar a Base64: {}", e.getMessage());
return null;
}
}
public String encodeStringToBase64(String text) {
if (StringUtil.isNullOrEmpty(text)) {
return null;
}
return Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8));
}
public <T> T decodeBase64(String base64String, Class<T> clazz) {
try {
byte[] decodedBytes = Base64.getDecoder().decode(base64String);
String jsonString = new String(decodedBytes);
return objectMapper.readValue(jsonString, clazz);
} catch (Exception e) {
log.error("Error al decodificar desde Base64: {}", e.getMessage());
return null;
}
}
public String encodeSha256(String json) {
return DigestUtils.sha256Hex(json);
}
public <T> String toJsonString(T element) {
if (element == null) {
return "";
}
try {
return objectMapper.writeValueAsString(element);
} catch (JsonProcessingException e) {
log.error("Error al convertir objeto a Json String: {}", e.getMessage());
return "";
}
}
public <T> Map<String, Object> toMap(T element) {
return toMap(element, null);
}
public <T> Map<String, Object> toMap(
T element,
List<String> excludedFields
) {
if (element == null) {
return new HashMap<>();
}
try {
Map<String, Object> map = objectMapper.convertValue(
element, new TypeReference<>() {}
);
if (excludedFields != null && !excludedFields.isEmpty()) {
Set<String> excludedSet = new HashSet<>(excludedFields);
excludedSet.forEach(map::remove);
}
return map;
} catch (Exception e) {
log.error("Error al convertir objeto a Map: {}", e.getMessage());
return new HashMap<>();
}
}
public <T> Map<String, Object> toTreeMap(T element) {
return toTreeMap(element, null);
}
public <T> Map<String, Object> toTreeMap(
T element,
List<String> excludedFields
) {
if (element == null) {
return new TreeMap<>();
}
try {
Map<String, Object> tempMap = objectMapper.convertValue(
element, new TypeReference<>() {}
);
Map<String, Object> treeMap = new TreeMap<>(tempMap);
if (excludedFields != null && !excludedFields.isEmpty()) {
Set<String> excludedSet = new HashSet<>(excludedFields);
excludedSet.forEach(treeMap::remove);
}
return treeMap;
} catch (Exception e) {
log.error("Error al convertir objeto a TreeMap: {}", e.getMessage());
return new TreeMap<>();
}
}
public String generateSignature(
Object operation,
String channelOrigin
) {
Map<String, Object> operationSortedMap = toTreeMap(operation);
StringBuilder concatenatedValues = new StringBuilder();
String channelBase64 = encodeStringToBase64(channelOrigin);
for (Object value : operationSortedMap.values()) {
if(!Objects.isNull(value)) {
concatenatedValues.append(value);
}
}
String finalString = concatenatedValues + channelBase64;
log.info("1. Operation concatenando valores: {}", concatenatedValues);
log.info("2. Channel Origin codificado: {}", channelBase64);
return encodeSha256(finalString);
}
public String generateSignature(
Long id,
String channelOrigin
) {
String channelBase64 = encodeStringToBase64(channelOrigin);
String finalString = id + channelBase64;
log.info("1. Operation concatenando valores: {}", finalString);
log.info("2. Channel Origin codificado: {}", channelBase64);
return encodeSha256(finalString);
}
}

View File

@ -0,0 +1,87 @@
package com.banesco.common.application.helper;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.module.msg_response_header.domain.model.AdditionalStatus;
import com.banesco.module.msg_response_header.domain.model.MsgRsStatus;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Slf4j
public class ServiceStatusValidateHelper {
private ServiceStatusValidateHelper() {}
public static void validateStatus(MsgRsStatus status) {
if(Objects.isNull(status)) {
return;
}
List<String> errors = extractErrorCodes(status);
if(!errors.isEmpty()) {
AdditionalStatus additionalStatus = getFirstAdditionalStatus(status);
String error = String.join("-", errors);
String desc = (!Objects.isNull(additionalStatus))
? additionalStatus.getStatusDesc()
: "";
log.error(
"Error asignado desde la lista concatenada de los errores: {} -> {}",
error,
desc
);
throw HttpStatusCodeException.badRequest(error, desc);
} else {
log.error(
"Error asignado desde el status principal: {} -> {}",
status.getStatusCode(),
status.getStatusDesc()
);
throw HttpStatusCodeException.internalServer(
status.getStatusCode(),
status.getStatusDesc()
);
}
}
private static List<String> extractErrorCodes(MsgRsStatus status) {
List<String> errors = new ArrayList<>();
List<AdditionalStatus> additionalStatuses = status.getAdditionalStatus();
if(Objects.isNull(additionalStatuses) || additionalStatuses.isEmpty()) {
return errors;
}
for (AdditionalStatus item: additionalStatuses) {
if (
!Objects.equals(item.getStatusCode(), "000") &&
!errors.contains(item.getStatusCode())
) {
errors.add(item.getStatusCode());
}
}
return errors;
}
private static AdditionalStatus getFirstAdditionalStatus(MsgRsStatus status) {
List<AdditionalStatus> additionalStatuses = status.getAdditionalStatus();
if(Objects.isNull(additionalStatuses) || additionalStatuses.isEmpty()) {
return null;
}
for (AdditionalStatus item: additionalStatuses) {
if (!Objects.equals(item.getStatusCode(), "000")) {
return item;
}
}
return null;
}
}

View File

@ -0,0 +1,561 @@
package com.banesco.common.application.service;
import com.banesco.common.application.usecase.HttpClientUseCase;
import com.banesco.common.domain.exception.HttpApiResponseException;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.client.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Slf4j
@ApplicationScoped
public class HttpClientService implements HttpClientUseCase {
private final ObjectMapper objectMapper;
@Inject
public HttpClientService(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public <T> T execute(HttpRequest request) {
return executeRequest(request);
}
@Override
public <T, R> Either<T, R> executeEither(HttpRequest request) {
return executeEitherInternal(request, false);
}
@Override
public <T, R> Either<List<T>, R> executeEitherList(HttpRequest request) {
return executeEitherInternal(request, true);
}
@Override
public <T> ApiResponse<T> executeApiResponse(HttpRequest request) {
return executeRequest(request);
}
@Override
public <T> ApiResponse<List<T>> executeApiResponseList(HttpRequest request) {
return executeRequest(request);
}
@Override
public <T> ApiPrivateResponse<Either<T, ApiPrivateError>> executeApiPrivateResponse(HttpRequest request) {
return executeRequest(request);
}
@Override
public <T> ApiPrivateResponse<Either<List<T>, ApiPrivateError>> executeApiPrivateResponseList(HttpRequest request) {
return executeRequest(request);
}
private <T, R> Either<T, R> executeEitherInternal(HttpRequest request, boolean isList) {
try (Client client = createClient(request.getConnectTimeout(), request.getReadTimeout())) {
WebTarget target = client.target(buildFinalUrl(request));
Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON);
if (request.getHeaders() != null) {
request.getHeaders().forEach(builder::header);
}
Response response = buildRequest(builder, request);
return handleEitherResponse(request, response, isList);
} catch (HttpStatusCodeException | HttpApiResponseException e) {
throw e;
} catch (Exception e) {
throw handleConnectionError(request, e);
}
}
private <T> T executeRequest(HttpRequest request) {
try (Client client = createClient(request.getConnectTimeout(), request.getReadTimeout())) {
WebTarget target = client.target(buildFinalUrl(request));
Invocation.Builder builder = target.request(MediaType.APPLICATION_JSON);
if (request.getHeaders() != null) {
request.getHeaders().forEach(builder::header);
}
Response response = buildRequest(builder, request);
return handleResponse(request, response);
} catch (HttpStatusCodeException | HttpApiResponseException e) {
throw e;
} catch (Exception e) {
throw handleConnectionError(request, e);
}
}
@SuppressWarnings("unchecked")
private <T, R> Either<T, R> handleEitherResponse(HttpRequest request, Response response, boolean isList) {
int statusCode = response.getStatus();
try (response) {
String responseBody = response.readEntity(String.class);
logResponse(request, statusCode, responseBody);
if (statusCode >= 200 && statusCode < 300) {
Object successData = isList
? parseSuccessListResponse(request, responseBody)
: parseSuccessResponse(request, responseBody);
return Either.left((T) successData);
} else {
logErrorResponse(request, statusCode, responseBody);
R errorData = tryParseErrorResponse(request, responseBody);
if (errorData != null) {
return Either.right(errorData);
}
throw mapHttpStatusToException(statusCode, responseBody);
}
} catch (HttpStatusCodeException | HttpApiResponseException e) {
throw e;
} catch (Exception e) {
throw handleProcessingError(request, e);
}
}
@SuppressWarnings("unchecked")
private <T> T parseSuccessResponse(HttpRequest request, String responseBody) throws JsonProcessingException {
Type successType = extractSuccessType(request);
if (successType != null) {
if (successType instanceof Class) {
return objectMapper.readValue(responseBody, (Class<T>) successType);
} else if (successType instanceof ParameterizedType) {
JavaType javaType = objectMapper.getTypeFactory().constructType(successType);
return objectMapper.readValue(responseBody, javaType);
}
}
if (request.getResponseType() != null && request.getResponseType() != Object.class) {
return objectMapper.readValue(responseBody, objectMapper.getTypeFactory().constructType(request.getResponseType()));
}
return (T) objectMapper.readValue(responseBody, Object.class);
}
@SuppressWarnings("unchecked")
private <T> List<T> parseSuccessListResponse(HttpRequest request, String responseBody) throws JsonProcessingException {
Type successType = extractSuccessType(request);
if (
successType instanceof ParameterizedType paramType &&
paramType.getRawType() == List.class &&
paramType.getActualTypeArguments().length > 0
) {
Type elementType = paramType.getActualTypeArguments()[0];
if (elementType instanceof Class) {
JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(
List.class, (Class<T>) elementType
);
return objectMapper.readValue(responseBody, javaType);
}
}
return objectMapper.readValue(responseBody, List.class);
}
private Type extractSuccessType(HttpRequest request) {
if (
request.getComplexType() != null &&
request.getComplexType() instanceof ParameterizedType paramType &&
paramType.getRawType() == Either.class &&
paramType.getActualTypeArguments().length > 0
) {
return paramType.getActualTypeArguments()[0];
}
if (request.getGenericType() != null) {
return request.getGenericType();
}
return request.getResponseType();
}
@SuppressWarnings("unchecked")
private <R> R tryParseErrorResponse(HttpRequest request, String responseBody) {
if (responseBody == null || responseBody.trim().isEmpty()) {
return null;
}
try {
if (request.getErrorType() != null) {
return (R) objectMapper.readValue(responseBody, request.getErrorType());
}
if (request.getComplexType() != null && request.getComplexType() instanceof ParameterizedType paramType) {
Type[] typeArgs = paramType.getActualTypeArguments();
if (typeArgs.length >= 2 && typeArgs[1] instanceof Class) {
return objectMapper.readValue(responseBody, (Class<R>) typeArgs[1]);
}
}
} catch (Exception e) {
log.error("No se pudo parsear la respuesta como error type: {}", e.getMessage());
}
return null;
}
private String buildFinalUrl(HttpRequest request) {
String finalUrl = request.getUrl();
if (request.getPathParams() != null && !request.getPathParams().isEmpty()) {
for (Map.Entry<String, String> entry : request.getPathParams().entrySet()) {
String placeholder = "{" + entry.getKey() + "}";
finalUrl = finalUrl.replace(placeholder, entry.getValue());
}
}
String url = appendQueryParams(finalUrl, request.getQueryParams());
log.info("Url Final: {}", url);
return url;
}
private String appendQueryParams(String url, Map<String, String> queryParams) {
if (queryParams == null || queryParams.isEmpty()) {
return url;
}
StringBuilder urlBuilder = new StringBuilder(url);
boolean firstParam = !url.contains("?");
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
if (firstParam) {
urlBuilder.append("?");
firstParam = false;
} else {
urlBuilder.append("&");
}
String encodedKey = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8);
String encodedValue = entry.getValue() != null
? URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)
: "";
urlBuilder.append(encodedKey).append("=").append(encodedValue);
}
return urlBuilder.toString();
}
private Response buildRequest(Invocation.Builder builder, HttpRequest request) {
log.info("Metodo HTTP: {}", request.getMethod().name());
if(request.getBody() != null) {
log.info("Peticion Cuerpo: {}", request.getBody());
}
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("Metodo HTTP no soportado: " + request.getMethod());
};
}
private Client createClient(int connectTimeout, int readTimeout) {
return ClientBuilder.newBuilder()
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
.build();
}
private <T> T handleResponse(HttpRequest request, Response response) {
int statusCode = response.getStatus();
try (response) {
String responseBody = response.readEntity(String.class);
logResponse(request, statusCode, responseBody);
if (statusCode >= 200 && statusCode < 300) {
if (request.getResponseType() == Void.class || request.getResponseType() == void.class) {
return null;
}
return responseResult(request, responseBody);
} else {
logErrorResponse(request, statusCode, responseBody);
if (isApiResponseFormat(responseBody)) {
ApiResponse<?> apiResponse = deserializeApiResponse(responseBody, request);
throw new HttpApiResponseException(statusCode, apiResponse);
} else {
throw mapHttpStatusToException(statusCode, responseBody);
}
}
} catch (HttpStatusCodeException | HttpApiResponseException e) {
throw e;
} catch (Exception e) {
throw handleProcessingError(request, e);
}
}
private void logResponse(HttpRequest request, int statusCode, String responseBody) {
if (request.isLogResponseBody()) {
log.info("Respuesta {} - Status: {}", request.getMethod(), statusCode);
log.info("Respuesta Cuerpo: {}", responseBody);
}
}
private void logErrorResponse(HttpRequest request, int statusCode, String responseBody) {
log.error(
"Error HTTP {} {} - Status: {} - Body: {}",
request.getMethod(),
request.getUrl(),
statusCode,
responseBody
);
}
private HttpStatusCodeException handleConnectionError(HttpRequest request, Exception e) {
log.error("Error de conexion {}: {}", request.getMethod(), e.getMessage());
return HttpStatusCodeException.serviceUnavailable(
"503", "Error de conexion con el servicio externo: " + e.getMessage()
);
}
private HttpStatusCodeException handleProcessingError(HttpRequest request, Exception e) {
log.error(
"Error procesando respuesta {} {}: {}",
request.getMethod(),
request.getUrl(),
e.getMessage()
);
return HttpStatusCodeException.internalServer(
"500", "Error procesando respuesta del servicio externo: " + e.getMessage()
);
}
private <T> T responseResult(HttpRequest request, String responseBody) throws JsonProcessingException {
if (request.isApiPrivateResponse() && request.isEitherResponse()) {
return handleApiPrivateResponseWithEither(request, responseBody);
}
if (request.getResponseType() == ApiResponse.class) {
return deserializeApiResponse(responseBody, request);
} else if (request.getComplexType() != null) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(
request.getResponseType(), objectMapper.getTypeFactory().constructType(request.getComplexType())
);
return objectMapper.readValue(responseBody, javaType);
} else if (request.getGenericType() != null) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(
request.getResponseType(), objectMapper.getTypeFactory().constructType(request.getGenericType())
);
return objectMapper.readValue(responseBody, javaType);
} else {
return objectMapper.readValue(
responseBody, objectMapper.getTypeFactory().constructType(request.getResponseType())
);
}
}
private <T> T handleApiPrivateResponseWithEither(HttpRequest request, String responseBody) throws JsonProcessingException {
JsonNode rootNode = objectMapper.readTree(responseBody);
String status = rootNode.has("estatus") ? rootNode.get("estatus").asText() : null;
String message = rootNode.has("mensaje") ? rootNode.get("mensaje").asText() : null;
JsonNode detailNode = rootNode.get("detalle");
if (request.getStatusSuccess().equals(status)) {
return handleSuccessResponse(request, status, message, detailNode);
} else {
return handleErrorResponse(status, message, detailNode);
}
}
@SuppressWarnings("unchecked")
private <T> T handleSuccessResponse(HttpRequest request, String status, String message, JsonNode detailNode) {
Object successData;
if (request.isListResponse()) {
successData = handleListSuccess(request, detailNode);
ApiPrivateResponse<Either<List<Object>, ApiPrivateError>> response = new ApiPrivateResponse<>();
response.setEstatus(status);
response.setMensaje(message);
response.setDetalle(Either.left((List<Object>) successData));
return (T) response;
} else {
successData = handleObjectSuccess(request, detailNode);
ApiPrivateResponse<Either<Object, ApiPrivateError>> response = new ApiPrivateResponse<>();
response.setEstatus(status);
response.setMensaje(message);
response.setDetalle(Either.left(successData));
return (T) response;
}
}
private Object handleListSuccess(HttpRequest request, JsonNode detailNode) {
Class<?> elementType = getElementTypeFromRequest(request);
JavaType listType = objectMapper.getTypeFactory().constructCollectionType(List.class, elementType);
if (detailNode != null && !detailNode.isNull()) {
return objectMapper.convertValue(detailNode, listType);
}
return List.of();
}
private Object handleObjectSuccess(HttpRequest request, JsonNode detailNode) {
Class<?> elementType = getElementTypeFromRequest(request);
if (detailNode != null && !detailNode.isNull()) {
return objectMapper.convertValue(detailNode, elementType);
}
return null;
}
@SuppressWarnings("unchecked")
private <T> T handleErrorResponse(String status, String message, JsonNode detailNode) {
ApiPrivateError error = buildApiPrivateError(detailNode, message);
ApiPrivateResponse<Either<Object, ApiPrivateError>> response = new ApiPrivateResponse<>();
response.setEstatus(status);
response.setMensaje(message);
response.setDetalle(Either.right(error));
return (T) response;
}
private ApiPrivateError buildApiPrivateError(JsonNode detailNode, String message) {
if (detailNode != null && !detailNode.isNull()) {
try {
return objectMapper.convertValue(detailNode, ApiPrivateError.class);
} catch (Exception e) {
log.warn("Cannot map detail to ApiPrivateError, creating default error. Detail: {}", detailNode);
}
}
return ApiPrivateError.builder()
.codError(null)
.mensajeError(message)
.constraintName(null)
.build();
}
private Class<?> getElementTypeFromRequest(HttpRequest request) {
if (request.getGenericType() != null) {
return request.getGenericType();
}
if (request.getComplexType() instanceof ParameterizedType pt) {
Type[] typeArgs = pt.getActualTypeArguments();
if (typeArgs.length > 0) {
Type firstArg = typeArgs[0];
if (firstArg instanceof ParameterizedType innerPt) {
Type[] innerArgs = innerPt.getActualTypeArguments();
if (innerArgs.length > 0 && innerArgs[0] instanceof Class<?> innerClass) {
return innerClass;
}
} else if (firstArg instanceof Class<?> elementClass) {
return elementClass;
}
}
}
log.warn("No se pudo determinar el elementType del request, usando Object.class");
return Object.class;
}
@SuppressWarnings("unchecked")
private <T> T deserializeApiResponse(String responseBody, HttpRequest request) {
try {
if (request.getGenericType() != null) {
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(
ApiResponse.class, objectMapper.getTypeFactory().constructType(request.getGenericType())
);
return objectMapper.readValue(responseBody, javaType);
} else {
return (T) objectMapper.readValue(responseBody, ApiResponse.class);
}
} catch (JsonProcessingException e) {
log.error("Error deserializando respuesta JSON: {}", e.getMessage());
throw HttpStatusCodeException.internalServer(
"500", "Error deserializando respuesta JSON: " + e.getMessage()
);
} catch (Exception e) {
log.error("Error desconocido al deserializar respuesta: {}", e.getMessage());
throw HttpStatusCodeException.internalServer(
"500", "Error desconocido al deserializar respuesta: " + e.getMessage()
);
}
}
private boolean isApiResponseFormat(String responseBody) {
try {
if (responseBody == null || responseBody.trim().isEmpty()) {
return false;
}
return responseBody.contains("\"data\"") &&
responseBody.contains("\"statusResponse\"") &&
responseBody.contains("\"statusCode\"") &&
responseBody.contains("\"message\"");
} catch (Exception e) {
return false;
}
}
private HttpStatusCodeException mapHttpStatusToException(int statusCode, String errorBody) {
String errorCode = "HTTP_" + statusCode;
String defaultMessage = "Error en servicio externo: HTTP " + statusCode;
String message = errorBody != null && !errorBody.isEmpty()
? errorBody
: defaultMessage;
return switch (statusCode) {
case 400 -> HttpStatusCodeException.badRequest(errorCode, message);
case 401 -> HttpStatusCodeException.unauthorized(errorCode, message);
case 403 -> HttpStatusCodeException.forbidden(errorCode, message);
case 404 -> HttpStatusCodeException.notFound(errorCode, message);
case 405 -> HttpStatusCodeException.methodNotAllowed(errorCode, message);
case 408 -> HttpStatusCodeException.fromStatusCode(408, errorCode, message);
case 409 -> HttpStatusCodeException.conflict(errorCode, message);
case 410 -> HttpStatusCodeException.gone(errorCode, message);
case 412 -> HttpStatusCodeException.preconditionFailed(errorCode, message);
case 415 -> HttpStatusCodeException.unsupportedMediaType(errorCode, message);
case 422 -> HttpStatusCodeException.unprocessableEntity(errorCode, message);
case 429 -> HttpStatusCodeException.tooManyRequests(errorCode, message);
case 500 -> HttpStatusCodeException.internalServer(errorCode, message);
case 501 -> HttpStatusCodeException.notImplemented(errorCode, message);
case 502 -> HttpStatusCodeException.badGateway(errorCode, message);
case 503 -> HttpStatusCodeException.serviceUnavailable(errorCode, message);
case 504 -> HttpStatusCodeException.gatewayTimeout(errorCode, message);
default -> HttpStatusCodeException.fromStatusCode(statusCode, errorCode, message);
};
}
}

View File

@ -0,0 +1,22 @@
package com.banesco.common.application.usecase;
import com.banesco.common.domain.model.*;
import java.util.List;
public interface HttpClientUseCase {
<T> T execute(HttpRequest request);
<T, R> Either<T, R> executeEither(HttpRequest request);
<T, R> Either<List<T>, R> executeEitherList(HttpRequest request);
<T> ApiResponse<T> executeApiResponse(HttpRequest request);
<T> ApiResponse<List<T>> executeApiResponseList(HttpRequest request);
<T> ApiPrivateResponse<Either<T, ApiPrivateError>> executeApiPrivateResponse(HttpRequest request);
<T> ApiPrivateResponse<Either<List<T>, ApiPrivateError>> executeApiPrivateResponseList(HttpRequest request);
}

View File

@ -0,0 +1,22 @@
package com.banesco.common.domain.exception;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class ApiPrivateException extends RuntimeException {
private final String statusCode;
private final String message;
public ApiPrivateException(
String statusCode,
String message
) {
super(message != null ? message : "Operacion de negocio fallida");
this.statusCode = statusCode;
this.message = message;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,20 @@
package com.banesco.common.domain.exception;
import com.banesco.common.domain.model.ApiResponse;
import lombok.Getter;
@Getter
public class HttpApiResponseException extends RuntimeException {
private final int statusCode;
private final transient ApiResponse<?> apiResponse;
public HttpApiResponseException(int statusCode, ApiResponse<?> apiResponse) {
super(String.format(
"HTTP %d: %s", statusCode,
apiResponse.getStatusResponse() != null ?
apiResponse.getStatusResponse().getMessage() : "Error sin mensaje"
));
this.statusCode = statusCode;
this.apiResponse = apiResponse;
}
}

View File

@ -0,0 +1,11 @@
package com.banesco.common.domain.exception;
public class HttpClientException extends RuntimeException {
public HttpClientException(String message) {
super(message);
}
public HttpClientException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,322 @@
package com.banesco.common.domain.exception;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
import java.util.HashMap;
import java.util.Map;
@Getter
@ToString
@Builder
public class HttpStatusCodeException extends BaseApiException {
private final int statusCode;
private static final Map<Integer, String> STATUS_CATEGORIES = new HashMap<>();
static {
STATUS_CATEGORIES.put(100, "continue");
STATUS_CATEGORIES.put(101, "switching-protocols");
STATUS_CATEGORIES.put(102, "processing");
STATUS_CATEGORIES.put(103, "early-hints");
STATUS_CATEGORIES.put(200, "ok");
STATUS_CATEGORIES.put(201, "created");
STATUS_CATEGORIES.put(202, "accepted");
STATUS_CATEGORIES.put(203, "non-authoritative-information");
STATUS_CATEGORIES.put(204, "no-content");
STATUS_CATEGORIES.put(205, "reset-content");
STATUS_CATEGORIES.put(206, "partial-content");
STATUS_CATEGORIES.put(207, "multi-status");
STATUS_CATEGORIES.put(208, "already-reported");
STATUS_CATEGORIES.put(226, "im-used");
STATUS_CATEGORIES.put(300, "multiple-choices");
STATUS_CATEGORIES.put(301, "moved-permanently");
STATUS_CATEGORIES.put(302, "found");
STATUS_CATEGORIES.put(303, "see-other");
STATUS_CATEGORIES.put(304, "not-modified");
STATUS_CATEGORIES.put(305, "use-proxy");
STATUS_CATEGORIES.put(307, "temporary-redirect");
STATUS_CATEGORIES.put(308, "permanent-redirect");
STATUS_CATEGORIES.put(400, "bad-request");
STATUS_CATEGORIES.put(401, "unauthorized");
STATUS_CATEGORIES.put(402, "payment-required");
STATUS_CATEGORIES.put(403, "forbidden");
STATUS_CATEGORIES.put(404, "not-found");
STATUS_CATEGORIES.put(405, "method-not-allowed");
STATUS_CATEGORIES.put(406, "not-acceptable");
STATUS_CATEGORIES.put(407, "proxy-authentication-required");
STATUS_CATEGORIES.put(408, "request-timeout");
STATUS_CATEGORIES.put(409, "conflict");
STATUS_CATEGORIES.put(410, "gone");
STATUS_CATEGORIES.put(411, "length-required");
STATUS_CATEGORIES.put(412, "precondition-failed");
STATUS_CATEGORIES.put(413, "payload-too-large");
STATUS_CATEGORIES.put(414, "uri-too-long");
STATUS_CATEGORIES.put(415, "unsupported-media-type");
STATUS_CATEGORIES.put(416, "range-not-satisfiable");
STATUS_CATEGORIES.put(417, "expectation-failed");
STATUS_CATEGORIES.put(418, "im-a-teapot");
STATUS_CATEGORIES.put(421, "misdirected-request");
STATUS_CATEGORIES.put(422, "unprocessable-entity");
STATUS_CATEGORIES.put(423, "locked");
STATUS_CATEGORIES.put(424, "failed-dependency");
STATUS_CATEGORIES.put(425, "too-early");
STATUS_CATEGORIES.put(426, "upgrade-required");
STATUS_CATEGORIES.put(428, "precondition-required");
STATUS_CATEGORIES.put(429, "too-many-requests");
STATUS_CATEGORIES.put(431, "request-header-fields-too-large");
STATUS_CATEGORIES.put(451, "unavailable-for-legal-reasons");
STATUS_CATEGORIES.put(500, "internal-server-error");
STATUS_CATEGORIES.put(501, "not-implemented");
STATUS_CATEGORIES.put(502, "bad-gateway");
STATUS_CATEGORIES.put(503, "service-unavailable");
STATUS_CATEGORIES.put(504, "gateway-timeout");
STATUS_CATEGORIES.put(505, "http-version-not-supported");
STATUS_CATEGORIES.put(506, "variant-also-negotiates");
STATUS_CATEGORIES.put(507, "insufficient-storage");
STATUS_CATEGORIES.put(508, "loop-detected");
STATUS_CATEGORIES.put(510, "not-extended");
STATUS_CATEGORIES.put(511, "network-authentication-required");
}
public HttpStatusCodeException(int statusCode, String errorCode, String message, String fieldPath) {
super(errorCode, message, fieldPath, getHttpStatusCategory(statusCode));
this.statusCode = statusCode;
}
public HttpStatusCodeException(int statusCode, String errorCode, String message) {
this(statusCode, errorCode, message, null);
}
public HttpStatusCodeException(int statusCode, String errorCode) {
this(statusCode, errorCode, getDefaultMessage(statusCode), null);
}
public HttpStatusCodeException(int statusCode) {
this(statusCode, "HTTP_" + statusCode);
}
private static String getHttpStatusCategory(int statusCode) {
String category = STATUS_CATEGORIES.get(statusCode);
if (category != null) {
return category;
}
if (statusCode >= 100 && statusCode < 200) return "informational";
if (statusCode >= 200 && statusCode < 300) return "success";
if (statusCode >= 300 && statusCode < 400) return "redirection";
if (statusCode >= 400 && statusCode < 500) return "client-error";
if (statusCode >= 500 && statusCode < 600) return "server-error";
return "unknown";
}
private static String getDefaultMessage(int statusCode) {
return switch (statusCode) {
case 200 -> "OK";
case 201 -> "Created";
case 202 -> "Accepted";
case 204 -> "No Content";
case 400 -> "Bad Request";
case 401 -> "Unauthorized";
case 403 -> "Forbidden";
case 404 -> "Not Found";
case 405 -> "Method Not Allowed";
case 408 -> "Request Timeout";
case 409 -> "Conflict";
case 410 -> "Gone";
case 422 -> "Unprocessable Entity";
case 429 -> "Too Many Requests";
case 500 -> "Internal Server Error";
case 502 -> "Bad Gateway";
case 503 -> "Service Unavailable";
case 504 -> "Gateway Timeout";
default -> "HTTP " + statusCode;
};
}
public static HttpStatusCodeException badRequest(String errorCode) {
return new HttpStatusCodeException(400, errorCode);
}
public static HttpStatusCodeException badRequest(String errorCode, String fieldPath) {
return new HttpStatusCodeException(400, errorCode, getDefaultMessage(400), fieldPath);
}
public static HttpStatusCodeException unauthorized(String errorCode) {
return new HttpStatusCodeException(401, errorCode);
}
public static HttpStatusCodeException unauthorized(String errorCode, String fieldPath) {
return new HttpStatusCodeException(401, errorCode, getDefaultMessage(401), fieldPath);
}
public static HttpStatusCodeException forbidden(String errorCode) {
return new HttpStatusCodeException(403, errorCode);
}
public static HttpStatusCodeException forbidden(String errorCode, String fieldPath) {
return new HttpStatusCodeException(403, errorCode, getDefaultMessage(403), fieldPath);
}
public static HttpStatusCodeException notFound(String errorCode) {
return new HttpStatusCodeException(404, errorCode);
}
public static HttpStatusCodeException notFound(String errorCode, String fieldPath) {
return new HttpStatusCodeException(404, errorCode, getDefaultMessage(404), fieldPath);
}
public static HttpStatusCodeException methodNotAllowed(String errorCode) {
return new HttpStatusCodeException(405, errorCode);
}
public static HttpStatusCodeException methodNotAllowed(String errorCode, String fieldPath) {
return new HttpStatusCodeException(405, errorCode, getDefaultMessage(405), fieldPath);
}
public static HttpStatusCodeException conflict(String errorCode) {
return new HttpStatusCodeException(409, errorCode);
}
public static HttpStatusCodeException conflict(String errorCode, String fieldPath) {
return new HttpStatusCodeException(409, errorCode, getDefaultMessage(409), fieldPath);
}
public static HttpStatusCodeException unprocessableEntity(String errorCode) {
return new HttpStatusCodeException(422, errorCode);
}
public static HttpStatusCodeException unprocessableEntity(String errorCode, String fieldPath) {
return new HttpStatusCodeException(422, errorCode, getDefaultMessage(422), fieldPath);
}
public static HttpStatusCodeException tooManyRequests(String errorCode) {
return new HttpStatusCodeException(429, errorCode);
}
public static HttpStatusCodeException tooManyRequests(String errorCode, String fieldPath) {
return new HttpStatusCodeException(429, errorCode, getDefaultMessage(429), fieldPath);
}
public static HttpStatusCodeException internalServer(String errorCode) {
return new HttpStatusCodeException(500, errorCode);
}
public static HttpStatusCodeException internalServer(String errorCode, String fieldPath) {
return new HttpStatusCodeException(500, errorCode, getDefaultMessage(500), fieldPath);
}
public static HttpStatusCodeException badGateway(String errorCode) {
return new HttpStatusCodeException(502, errorCode);
}
public static HttpStatusCodeException badGateway(String errorCode, String fieldPath) {
return new HttpStatusCodeException(502, errorCode, getDefaultMessage(502), fieldPath);
}
public static HttpStatusCodeException serviceUnavailable(String errorCode) {
return new HttpStatusCodeException(503, errorCode);
}
public static HttpStatusCodeException serviceUnavailable(String errorCode, String fieldPath) {
return new HttpStatusCodeException(503, errorCode, getDefaultMessage(503), fieldPath);
}
public static HttpStatusCodeException gatewayTimeout(String errorCode) {
return new HttpStatusCodeException(504, errorCode);
}
public static HttpStatusCodeException gatewayTimeout(String errorCode, String fieldPath) {
return new HttpStatusCodeException(504, errorCode, getDefaultMessage(504), fieldPath);
}
public static HttpStatusCodeException paymentRequired(String errorCode) {
return new HttpStatusCodeException(402, errorCode);
}
public static HttpStatusCodeException paymentRequired(String errorCode, String fieldPath) {
return new HttpStatusCodeException(402, errorCode, getDefaultMessage(402), fieldPath);
}
public static HttpStatusCodeException gone(String errorCode) {
return new HttpStatusCodeException(410, errorCode);
}
public static HttpStatusCodeException gone(String errorCode, String fieldPath) {
return new HttpStatusCodeException(410, errorCode, getDefaultMessage(410), fieldPath);
}
public static HttpStatusCodeException preconditionFailed(String errorCode) {
return new HttpStatusCodeException(412, errorCode);
}
public static HttpStatusCodeException preconditionFailed(String errorCode, String fieldPath) {
return new HttpStatusCodeException(412, errorCode, getDefaultMessage(412), fieldPath);
}
public static HttpStatusCodeException unsupportedMediaType(String errorCode) {
return new HttpStatusCodeException(415, errorCode);
}
public static HttpStatusCodeException unsupportedMediaType(String errorCode, String fieldPath) {
return new HttpStatusCodeException(415, errorCode, getDefaultMessage(415), fieldPath);
}
public static HttpStatusCodeException notImplemented(String errorCode) {
return new HttpStatusCodeException(501, errorCode);
}
public static HttpStatusCodeException notImplemented(String errorCode, String fieldPath) {
return new HttpStatusCodeException(501, errorCode, getDefaultMessage(501), fieldPath);
}
public static HttpStatusCodeException ok(String errorCode) {
return new HttpStatusCodeException(200, errorCode);
}
public static HttpStatusCodeException ok(String errorCode, String fieldPath) {
return new HttpStatusCodeException(200, errorCode, getDefaultMessage(200), fieldPath);
}
public static HttpStatusCodeException created(String errorCode) {
return new HttpStatusCodeException(201, errorCode);
}
public static HttpStatusCodeException created(String errorCode, String fieldPath) {
return new HttpStatusCodeException(201, errorCode, getDefaultMessage(201), fieldPath);
}
public static HttpStatusCodeException accepted(String errorCode) {
return new HttpStatusCodeException(202, errorCode);
}
public static HttpStatusCodeException accepted(String errorCode, String fieldPath) {
return new HttpStatusCodeException(202, errorCode, getDefaultMessage(202), fieldPath);
}
public static HttpStatusCodeException noContent(String errorCode) {
return new HttpStatusCodeException(204, errorCode);
}
public static HttpStatusCodeException noContent(String errorCode, String fieldPath) {
return new HttpStatusCodeException(204, errorCode, getDefaultMessage(204), fieldPath);
}
public static HttpStatusCodeException requestTimeout(String errorCode) {
return new HttpStatusCodeException(408, errorCode);
}
public static HttpStatusCodeException requestTimeout(String errorCode, String fieldPath) {
return new HttpStatusCodeException(408, errorCode, getDefaultMessage(408), fieldPath);
}
public static HttpStatusCodeException fromStatusCode(int statusCode, String errorCode) {
return new HttpStatusCodeException(statusCode, errorCode);
}
public static HttpStatusCodeException fromStatusCode(int statusCode, String errorCode, String fieldPath) {
return new HttpStatusCodeException(statusCode, errorCode, getDefaultMessage(statusCode), fieldPath);
}
}

View File

@ -0,0 +1,16 @@
package com.banesco.common.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ApiPrivateError {
private Long codError;
private String mensajeError;
private String constraintName;
}

View File

@ -0,0 +1,17 @@
package com.banesco.common.domain.model;
import lombok.*;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ApiPrivateResponse<T> {
private String estatus;
private String mensaje;
private T detalle;
private String codeline;
private String __equalsCalc;
private Boolean __hashCodeCalc;
}

View File

@ -0,0 +1,18 @@
package com.banesco.common.domain.model;
import lombok.*;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
private T data;
private StatusResponse statusResponse;
public ApiResponse(StatusResponse statusResponse) {
this.statusResponse = statusResponse;
this.data = null;
}
}

View File

@ -0,0 +1,17 @@
package com.banesco.common.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Device {
private String deviceType;
private String deviceDescription;
private String deviceIp;
private String deviceSessionReference;
}

View File

@ -0,0 +1,33 @@
package com.banesco.common.domain.model;
import lombok.Getter;
@Getter
public class Either<L, R> {
private final L left;
private final R right;
private final boolean leftFlag;
private Either(L left, R right, boolean leftFlag) {
this.left = left;
this.right = right;
this.leftFlag = leftFlag;
}
public static <L, R> Either<L, R> left(L left) {
return new Either<>(left, null, true);
}
public static <L, R> Either<L, R> right(R right) {
return new Either<>(null, right, false);
}
public boolean isLeft() {
return leftFlag;
}
public boolean isRight() {
return !leftFlag;
}
}

View File

@ -0,0 +1,22 @@
package com.banesco.common.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
@RegisterForReflection
public class ErrorMapping {
@JsonProperty("backendCode")
String backendCode;
@JsonProperty("httpCode")
int httpCode;
@JsonProperty("statusCode")
String statusCode;
@JsonProperty("description")
String description;
}

View File

@ -0,0 +1,18 @@
package com.banesco.common.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Event {
private Identifier eventIdentification; // RequestId
private String eventSource; // AppName
private String eventType; // OperationType
private String eventStatus; // FinalStatusRequest
private String eventDescription; // CountDataOut
}

View File

@ -0,0 +1,237 @@
package com.banesco.common.domain.model;
import lombok.*;
import java.lang.reflect.Type;
import java.util.Map;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class HttpRequest {
private String url;
private HttpMethod method;
private Object body;
private Map<String, String> headers;
private Map<String, String> queryParams;
private Map<String, String> pathParams;
@Builder.Default
private Class<?> responseType = Object.class;
private Class<?> genericType;
private Type complexType;
private Class<?> errorType;
private String statusSuccess;
@Builder.Default
private boolean eitherResponse = false;
@Builder.Default
private boolean apiPrivateResponse = false;
@Builder.Default
private boolean apiResponse = false;
@Builder.Default
private boolean listResponse = false;
@Builder.Default
private int connectTimeout = 5000;
@Builder.Default
private int readTimeout = 10000;
@Builder.Default
private boolean returnFullResponse = false;
@Builder.Default
private boolean logRequestBody = true;
@Builder.Default
private boolean logResponseBody = true;
public static <T> HttpRequest forApiResponse(
String url,
HttpMethod method,
Class<T> dataType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(ApiResponse.class)
.genericType(dataType)
.apiResponse(true)
.build();
}
public static <T> HttpRequest forApiResponseList(
String url,
HttpMethod method,
Class<T> elementType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(ApiResponse.class)
.complexType(TypeBuilder.listOf(elementType))
.apiResponse(true)
.listResponse(true)
.build();
}
public static <T> HttpRequest forApiPrivateResponse(
String url,
String statusSuccess,
HttpMethod method,
Class<T> successType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(ApiPrivateResponse.class)
.complexType(TypeBuilder.parametricType(
Either.class,
successType,
ApiPrivateError.class
))
.apiPrivateResponse(true)
.eitherResponse(true)
.errorType(ApiPrivateError.class)
.statusSuccess(statusSuccess)
.build();
}
public static <T> HttpRequest forApiPrivateResponseList(
String url,
String statusSuccess,
HttpMethod method,
Class<T> elementType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(ApiPrivateResponse.class)
.complexType(TypeBuilder.parametricType(
Either.class,
TypeBuilder.listOf(elementType),
ApiPrivateError.class
))
.apiPrivateResponse(true)
.eitherResponse(true)
.listResponse(true)
.errorType(ApiPrivateError.class)
.statusSuccess(statusSuccess)
.build();
}
public static <T> HttpRequest forDirectResponse(
String url,
HttpMethod method,
Class<T> responseType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(responseType)
.build();
}
public static <T, R> HttpRequest forDirectResponse(
String url,
HttpMethod method,
Class<T> responseType,
Class<R> errorType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(responseType)
.errorType(errorType)
.build();
}
public static <T, U> HttpRequest forGenericResponse(
String url,
HttpMethod method,
Class<T> rawType,
Class<U> genericType
) {
return HttpRequest.builder()
.url(url)
.method(method)
.responseType(rawType)
.complexType(TypeBuilder.parametricType(rawType, genericType))
.build();
}
public HttpRequest withHeaders(Map<String, String> headers) {
this.headers = headers;
return this;
}
public HttpRequest withQueryParams(Map<String, String> queryParams) {
this.queryParams = queryParams;
return this;
}
public HttpRequest withPathParams(Map<String, String> pathParams) {
this.pathParams = pathParams;
return this;
}
public HttpRequest withBody(Object body) {
this.body = body;
return this;
}
public HttpRequest withTimeout(int connectTimeout, int readTimeout) {
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
return this;
}
public HttpRequest disableRequestLogging() {
this.logRequestBody = false;
return this;
}
public HttpRequest disableResponseLogging() {
this.logResponseBody = false;
return this;
}
public ResponseType getExpectedResponseType() {
if (apiPrivateResponse && eitherResponse) {
return listResponse ? ResponseType.API_PRIVATE_WITH_EITHER_LIST : ResponseType.API_PRIVATE_WITH_EITHER;
} else if (apiResponse) {
return listResponse ? ResponseType.API_RESPONSE_LIST : ResponseType.API_RESPONSE;
} else if (complexType != null) {
return ResponseType.COMPLEX_TYPE;
} else if (genericType != null) {
return ResponseType.GENERIC_TYPE;
} else {
return ResponseType.DIRECT_TYPE;
}
}
public enum ResponseType {
API_RESPONSE,
API_RESPONSE_LIST,
API_PRIVATE_WITH_EITHER,
API_PRIVATE_WITH_EITHER_LIST,
GENERIC_TYPE,
COMPLEX_TYPE,
DIRECT_TYPE
}
public enum HttpMethod {
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
}
}

View File

@ -0,0 +1,14 @@
package com.banesco.common.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Identifier {
private String identifierValue;
}

View File

@ -0,0 +1,15 @@
package com.banesco.common.domain.model;
import com.banesco.module.service_order_status.domain.dto.request.ServiceOrderStatusRequest;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@RegisterForReflection
public class ServiceOrderStatusConfig {
private String url;
private TimeoutConfig timeout;
private ServiceOrderStatusRequest request;
}

View File

@ -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;
}

View File

@ -0,0 +1,13 @@
package com.banesco.common.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@RegisterForReflection
public class TimeoutConfig {
private int connect;
private int response;
}

View File

@ -0,0 +1,67 @@
package com.banesco.common.domain.model;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class TypeBuilder {
private TypeBuilder() {}
public static Type listOf(Class<?> elementType) {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[]{elementType};
}
@Override
public Type getRawType() {
return java.util.List.class;
}
@Override
public Type getOwnerType() {
return null;
}
};
}
public static Type parametricType(Class<?> rawType, Type... typeArguments) {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return typeArguments;
}
@Override
public Type getRawType() {
return rawType;
}
@Override
public Type getOwnerType() {
return null;
}
};
}
public static Type apiPrivateResponseWithEither(Class<?> successType, Class<?> errorType) {
Type eitherType = parametricType(Either.class, successType, errorType);
return parametricType(ApiPrivateResponse.class, eitherType);
}
public static Type apiPrivateResponseWithListEither(Class<?> successType, Class<?> errorType) {
Type listType = listOf(successType);
Type eitherType = parametricType(Either.class, listType, errorType);
return parametricType(ApiPrivateResponse.class, eitherType);
}
public static Type apiResponseType(Class<?> dataType) {
return parametricType(ApiResponse.class, dataType);
}
public static Type apiResponseListType(Class<?> elementType) {
Type listType = listOf(elementType);
return parametricType(ApiResponse.class, listType);
}
}

View File

@ -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 WorkProduct {
private Identifier productIdentification; // BankId
private String productReference; // accountId
}

View File

@ -0,0 +1,32 @@
package com.banesco.common.infrastructure.config;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.Getter;
import org.eclipse.microprofile.config.Config;
@ApplicationScoped
@Getter
public class MessagesConfig {
private final boolean readFromProps;
private final String errorMessagesJson;
private final String messagesKey;
private static final String KEY = "dom-service-order-status-control";
@Inject
public MessagesConfig(Config config) {
this.readFromProps = config.getValue("api.read-messages.from-props", Boolean.class);
this.errorMessagesJson = config.getValue("api." + KEY + ".messages.content", String.class);
this.messagesKey = config.getValue("api." + KEY + ".messages.key", String.class);
}
public String getErrorMessagesJson() {
if (readFromProps) {
return errorMessagesJson != null ? errorMessagesJson : "";
} else {
return "";
}
}
}

View File

@ -0,0 +1,60 @@
package com.banesco.common.infrastructure.config;
import com.banesco.common.domain.model.ServiceOrderStatusConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.runtime.annotations.RegisterForReflection;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.Config;
import java.util.Map;
@Slf4j
@ApplicationScoped
@RegisterForReflection
public class RestClientConfig {
private final Config config;
private final ObjectMapper objectMapper;
private static final String API_BASE = "api.rest-client.";
private static final String API_SERVICE_ORDER_STATUS_NAME = "service-order-status";
@Inject
public RestClientConfig(
Config config,
ObjectMapper objectMapper
) {
this.config = config;
this.objectMapper = objectMapper;
}
public ServiceOrderStatusConfig getServiceOrderStatusConfig() {
return getConfig(API_SERVICE_ORDER_STATUS_NAME, ServiceOrderStatusConfig.class);
}
private <T> T getConfig(
String configName,
Class<T> configType
) {
try {
String fullConfigName = API_BASE + configName;
String json = config.getValue(fullConfigName, String.class);
log.info("Configurando {}: {}", fullConfigName, json);
if (json == null || json.trim().isEmpty()) {
throw new IllegalStateException("Configuracion no encontrada para: " + fullConfigName);
}
Map<String, Object> configMap = objectMapper.readValue(json, new TypeReference<>() {});
return objectMapper.convertValue(configMap, configType);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Formato JSON invalido para " + configName + ": " + e.getMessage(), e);
} catch (Exception e) {
throw new IllegalStateException("Error cargando configuracion del servicio " + configName + ": " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,24 @@
package com.banesco.common.infrastructure.context;
import org.slf4j.MDC;
public class RequestContext {
private RequestContext() {}
public static final String REQUEST_ID = "requestId";
public static final String DEVICE = "device";
public static final String DEVICE_SESSION_REFERENCE = "deviceSessionReference";
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);
}
}

View File

@ -0,0 +1,104 @@
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 lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Slf4j
@Provider
public class RequestIdFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext) {
String requestId = requestContext.getHeaderString(RequestContext.DEVICE_SESSION_REFERENCE);
if (isEmpty(requestId)) {
requestId = requestContext.getUriInfo()
.getQueryParameters()
.getFirst(RequestContext.DEVICE_SESSION_REFERENCE);
}
if (isEmpty(requestId) && hasJsonBody(requestContext)) {
requestId = extractRequestIdFromBody(requestContext);
}
if (isEmpty(requestId)) {
requestId = UUID.randomUUID().toString().substring(0, 13);
}
RequestContext.setRequestId(requestId);
}
private boolean isEmpty(String value) {
return value == null || value.trim().isEmpty();
}
private boolean hasJsonBody(ContainerRequestContext context) {
try {
String method = context.getMethod();
String contentType = context.getHeaderString("Content-Type");
return ("POST".equals(method) || "PUT".equals(method))
&& contentType != null
&& contentType.contains("application/json");
} catch (Exception e) {
log.warn("La peticion no es un POST o PUT: {}", e.getMessage());
return false;
}
}
private String extractRequestIdFromBody(ContainerRequestContext context) {
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
context.getEntityStream().transferTo(buffer);
byte[] bodyBytes = buffer.toByteArray();
context.setEntityStream(new ByteArrayInputStream(bodyBytes));
String bodyString = new String(bodyBytes, StandardCharsets.UTF_8);
io.vertx.core.json.JsonObject jsonObject = new io.vertx.core.json.JsonObject(bodyString);
if (jsonObject.containsKey(RequestContext.DEVICE)) {
io.vertx.core.json.JsonObject device = jsonObject.getJsonObject(RequestContext.DEVICE);
if (device.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) {
return device.getString(RequestContext.DEVICE_SESSION_REFERENCE);
}
}
if (jsonObject.containsKey(RequestContext.REQUEST_ID)) {
return jsonObject.getString(RequestContext.REQUEST_ID);
}
if (jsonObject.containsKey(RequestContext.DEVICE_SESSION_REFERENCE)) {
return jsonObject.getString(RequestContext.DEVICE_SESSION_REFERENCE);
}
return null;
} catch (Exception e) {
log.error("Error extrayendo el requestId del cuerpo de la peticion: {}", e.getMessage());
return null;
}
}
@Override
public void filter(
ContainerRequestContext requestContext,
ContainerResponseContext responseContext
) {
try {
RequestContext.clear();
} catch (Exception e) {
log.error("Error limpiando el filtro: {}", e.getMessage());
}
}
}

View File

@ -0,0 +1,15 @@
package com.banesco.module.instruction.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Instruction {
private InstructionIdentification instructionIdentifier; // SupervisorCode
private String instructionDescription; // CountDataOut
}

View File

@ -0,0 +1,15 @@
package com.banesco.module.instruction.domain.model;
import com.banesco.common.domain.model.Identifier;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class InstructionIdentification {
private Identifier instructionIdentification; // SupervisorCode
}

View File

@ -0,0 +1,44 @@
package com.banesco.module.msg_request_header.domain.model;
import com.banesco.common.domain.model.Device;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ApplicantData {
@JsonProperty("EnterpriseCode")
private String EnterpriseCode;
@JsonProperty("PrivateChannelId")
private String PrivateChannelId;
@JsonProperty("IpAddress")
private String IpAddress;
@JsonProperty("HostName")
private String HostName;
@JsonProperty("StadisticId")
private String StadisticId;
@JsonProperty("Application")
private String Application;
@JsonProperty("Nationality")
private String Nationality;
@JsonProperty("IdDocument")
private String IdDocument;
@JsonProperty("RolType")
private String RolType;
@JsonProperty("PlanCode")
private String PlanCode;
@JsonProperty("Channel")
private String Channel;
@JsonIgnore
public ApplicantData withOptions(Device device) {
this.IpAddress = device.getDeviceIp();
return this;
}
}

View File

@ -0,0 +1,84 @@
package com.banesco.module.msg_request_header.domain.model;
import com.banesco.common.domain.model.Device;
import com.banesco.common.infrastructure.context.RequestContext;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class MsgRqHdr {
@JsonProperty("MessageDate")
private String MessageDate;
@JsonProperty("MessageTime")
private String MessageTime;
@JsonProperty("RequestId")
private String RequestId;
@JsonProperty("LastStatusRequest")
private String LastStatusRequest;
@JsonProperty("FinalStatusRequest")
private String FinalStatusRequest;
@JsonProperty("ResumeId")
private String ResumeId;
@JsonProperty("ReverseId")
private String ReverseId;
@JsonProperty("LineId")
private String LineId;
@JsonProperty("SourceChannelCode")
private String SourceChannelCode;
@JsonProperty("SupervisorCode")
private String SupervisorCode;
@JsonProperty("OperatorCode")
private String OperatorCode;
@JsonProperty("RequestedOperationType")
private String RequestedOperationType;
@JsonProperty("NetworkTrnInfo")
private NetworkTrnInfo NetworkTrnInfo;
@JsonProperty("ApplicantData")
private ApplicantData ApplicantData;
@JsonProperty("RecCtrlIn")
private RecCtrlIn RecCtrlIn;
@JsonProperty("ReturnValue")
private String ReturnValue;
@JsonProperty("MessageId")
private String MessageId;
@JsonProperty("Priority")
private String Priority;
@JsonProperty("VBProtocol")
private VbProtocol VBProtocol;
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@JsonIgnore
public MsgRqHdr withOptions(Device device) {
if(Objects.isNull(this.getApplicantData())) {
this.ApplicantData = com.banesco.module.msg_request_header.domain.model.ApplicantData.builder()
.IpAddress(device.getDeviceIp())
.build();
} else {
this.ApplicantData = this.ApplicantData.withOptions(device);
}
String[] dateTime = LocalDateTime.now().format(dateTimeFormatter).split(" ");
String date = dateTime[0];
String time = dateTime[1];
this.RequestId = RequestContext.getRequestId();
this.MessageDate = date;
this.MessageTime = time;
this.NetworkTrnInfo = this.NetworkTrnInfo.withOptions(date, time);
return this;
}
}

View File

@ -0,0 +1,59 @@
package com.banesco.module.msg_request_header.domain.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class NetworkTrnInfo {
@JsonProperty("TransactionConsecutive")
private String TransactionConsecutive;
@JsonProperty("RegisterNumber")
private String RegisterNumber;
@JsonProperty("OriginatorName")
private String OriginatorName;
@JsonProperty("OperationType")
private String OperationType;
@JsonProperty("TransactionType")
private String TransactionType;
@JsonProperty("TransactionCode")
private String TransactionCode;
@JsonProperty("TransactionDate")
private String TransactionDate;
@JsonProperty("TransactionTime")
private String TransactionTime;
@JsonProperty("BankId")
private String BankId;
@JsonProperty("AgencyCode")
private String AgencyCode;
@JsonProperty("ChannelId")
private String ChannelId;
@JsonProperty("ChannelUserId")
private String ChannelUserId;
@JsonProperty("OperationExecIndicator")
private String OperationExecIndicator;
@JsonProperty("ConfiguredTransactionCode")
private String ConfiguredTransactionCode;
@JsonProperty("Desc")
private String Desc;
@JsonProperty("UserId")
private String UserId;
@JsonProperty("UserType")
private String UserType;
@JsonIgnore
public NetworkTrnInfo withOptions(
String date,
String time
) {
this.TransactionDate = date;
this.TransactionTime = time;
return this;
}
}

View File

@ -0,0 +1,16 @@
package com.banesco.module.msg_request_header.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class RecCtrlIn {
@JsonProperty("MaxRec")
private String MaxRec;
}

View File

@ -0,0 +1,18 @@
package com.banesco.module.msg_request_header.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class VbProtocol {
@JsonProperty("VBProtocolInd")
private Boolean VBProtocolInd;
@JsonProperty("TransactionInd")
private Boolean TransactionInd;
}

View File

@ -0,0 +1,26 @@
package com.banesco.module.msg_response_header.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class AdditionalStatus {
@JsonProperty("StatusType")
private String StatusType;
@JsonProperty("StatusCode")
private String StatusCode;
@JsonProperty("StatusDesc")
private String StatusDesc;
@JsonProperty("ValidationType")
private String ValidationType;
@JsonProperty("Severity")
private String Severity;
@JsonProperty("LineNumber")
private String LineNumber;
}

View File

@ -0,0 +1,18 @@
package com.banesco.module.msg_response_header.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class BlackList {
private BlackListRec BlackListRec;
}

View File

@ -0,0 +1,19 @@
package com.banesco.module.msg_response_header.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class BlackListRec {
private String BlackListBanesco;
private String BlackListCbn;
}

View File

@ -0,0 +1,62 @@
package com.banesco.module.msg_response_header.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.util.List;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class MsgRsHdr {
@JsonProperty("PrivateChannelId")
private String PrivateChannelId;
@JsonProperty("FinalStatusRequest")
private String FinalStatusRequest;
@JsonProperty("CountDataOut")
private String CountDataOut;
@JsonProperty("HeaderTypeReg")
private String HeaderTypeReg;
@JsonProperty("DetailTypeReg")
private String DetailTypeReg;
@JsonProperty("TransactionCode")
private String TransactionCode;
@JsonProperty("SupervisorCode")
private String SupervisorCode;
@JsonProperty("OperationType")
private String OperationType;
@JsonProperty("TransactionConsecutive")
private String TransactionConsecutive;
@JsonProperty("DetailReg")
private String DetailReg;
@JsonProperty("RegisterNumber")
private List<String> RegisterNumber;
@JsonProperty("TransactionType")
private String TransactionType;
@JsonProperty("RequestId")
private String RequestId;
@JsonProperty("RequestedOperationType")
private String RequestedOperationType;
@JsonProperty("MessageDate")
private String MessageDate;
@JsonProperty("TransactionTime")
private String TransactionTime;
@JsonProperty("LastStatusRequest")
private String LastStatusRequest;
@JsonProperty("ReturnValue")
private String ReturnValue;
@JsonProperty("RequestStatus")
private String RequestStatus;
@JsonProperty("MessageTime")
private String MessageTime;
@JsonProperty("TransacctionDate")
private String TransacctionDate;
@JsonProperty("RequestNumber")
private String RequestNumber;
@JsonProperty("AppName")
private String AppName;
}

View File

@ -0,0 +1,34 @@
package com.banesco.module.msg_response_header.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.util.List;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class MsgRsStatus {
@JsonProperty("StatusType")
private String StatusType;
@JsonProperty("StatusCode")
private String StatusCode;
@JsonProperty("StatusDesc")
private String StatusDesc;
@JsonProperty("ApplicationName")
private String ApplicationName;
@JsonProperty("LineNumber")
private int LineNumber;
@JsonProperty("AdditionalStatus")
private List<AdditionalStatus> AdditionalStatus;
@JsonProperty("Severity")
private String Severity;
@JsonProperty("StatusInd")
private String StatusInd;
@JsonProperty("LogId")
private String LogId;
}

View File

@ -0,0 +1,18 @@
package com.banesco.module.party.domain.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.util.List;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Party {
private List<PartyIdentification> partyIdentification;
}

View File

@ -0,0 +1,15 @@
package com.banesco.module.party.domain.model;
import com.banesco.common.domain.model.Identifier;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class PartyIdentification {
private Identifier partyIdentification;
}

View File

@ -0,0 +1,12 @@
package com.banesco.module.service_order_status.application.usecase;
import com.banesco.common.domain.model.Either;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
public interface ServiceOrderStatusUseCase {
<T, R> Either<T, R> execute(
ServiceOrderStatusControlRequest apiRequest,
Class<T> responseType,
Class<R> errorType
);
}

View File

@ -0,0 +1,22 @@
package com.banesco.module.service_order_status.domain.dto.request;
import com.banesco.module.service_order_status.domain.model.Account;
import com.banesco.module.msg_request_header.domain.model.MsgRqHdr;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ServiceOrderStatusRequest {
@JsonProperty("MsgRqHdr")
private MsgRqHdr MsgRqHdr;
@JsonProperty("Account")
private Account Account;
}

View File

@ -0,0 +1,25 @@
package com.banesco.module.service_order_status.domain.dto.response;
import com.banesco.module.msg_response_header.domain.model.MsgRsHdr;
import com.banesco.module.msg_response_header.domain.model.MsgRsStatus;
import com.banesco.module.service_order_status.domain.model.Card;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.util.List;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ServiceOrderStatusResponse {
@JsonProperty("MsgRsHdr")
private MsgRsHdr MsgRsHdr;
@JsonProperty("Status")
private List<MsgRsStatus> Status;
@JsonProperty("Card")
private List<Card> Card;
}

View File

@ -0,0 +1,18 @@
package com.banesco.module.service_order_status.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Account {
@JsonProperty("Bankinfo")
private Bankinfo Bankinfo;
@JsonProperty("Service")
private Service Service;
}

View File

@ -0,0 +1,16 @@
package com.banesco.module.service_order_status.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Bankinfo {
@JsonProperty("BankId")
private String BankId;
}

View File

@ -0,0 +1,18 @@
package com.banesco.module.service_order_status.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.util.List;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Card {
@JsonProperty("CardStatus")
private List<CardStatus> CardStatus;
}

View File

@ -0,0 +1,16 @@
package com.banesco.module.service_order_status.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class CardStatus {
@JsonProperty("StatusDesc")
private String StatusDesc;
}

View File

@ -0,0 +1,22 @@
package com.banesco.module.service_order_status.domain.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Service {
@JsonProperty("SvcCode")
private String SvcCode;
@JsonProperty("SvcType")
private String SvcType;
@JsonProperty("SvcDesc")
private String SvcDesc;
@JsonProperty("SvcObser")
private String SvcObser;
}

View File

@ -0,0 +1,107 @@
package com.banesco.module.service_order_status.infrastructure.client;
import com.banesco.common.application.usecase.HttpClientUseCase;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.Either;
import com.banesco.common.domain.model.HttpRequest;
import com.banesco.common.infrastructure.config.RestClientConfig;
import com.banesco.module.service_order_status.application.usecase.ServiceOrderStatusUseCase;
import com.banesco.module.service_order_status.domain.dto.request.ServiceOrderStatusRequest;
import com.banesco.common.domain.model.ServiceOrderStatusConfig;
import com.banesco.module.service_order_status.domain.model.Account;
import com.banesco.module.service_order_status.domain.model.Bankinfo;
import com.banesco.module.msg_request_header.domain.model.MsgRqHdr;
import com.banesco.module.service_order_status.domain.model.Service;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ApplicationScoped
public class ServiceOrderStatusClient implements ServiceOrderStatusUseCase {
private final HttpClientUseCase httpClientUseCase;
private final ServiceOrderStatusConfig serviceOrderStatusConfig;
@Inject
public ServiceOrderStatusClient(
HttpClientUseCase httpClientUseCase,
RestClientConfig restClientConfig
) {
this.httpClientUseCase = httpClientUseCase;
this.serviceOrderStatusConfig = restClientConfig.getServiceOrderStatusConfig();
log.info("Configuracion cargada para security-trace: {}", serviceOrderStatusConfig);
}
@Override
public <T, R> Either<T, R> execute(
ServiceOrderStatusControlRequest apiRequest,
Class<T> responseType,
Class<R> errorType
) {
ServiceOrderStatusRequest body = getRequest(apiRequest);
HttpRequest request = HttpRequest.forDirectResponse(
serviceOrderStatusConfig.getUrl(),
HttpRequest.HttpMethod.POST,
responseType,
errorType
)
.withBody(body)
.withTimeout(
serviceOrderStatusConfig.getTimeout().getConnect(),
serviceOrderStatusConfig.getTimeout().getResponse()
);
try {
Either<T, R> response = httpClientUseCase.executeEither(request);
log.info(
"Solicitud de lista negra exitosa: {}",
response.isLeft() && response.getLeft().toString().contains("msgRsHdr=")
);
return response;
} catch (HttpStatusCodeException e) {
log.error(
"Error HTTP ejecutando lista negra: {} - {}",
e.getStatusCode(),
e.getMessage()
);
throw e;
} catch (Exception e) {
log.error("Error ejecutando lista negra: {}", e.getMessage());
throw HttpStatusCodeException.serviceUnavailable("503");
}
}
private ServiceOrderStatusRequest getRequest(
ServiceOrderStatusControlRequest apiRequest
) {
MsgRqHdr msgRqHdr = serviceOrderStatusConfig
.getRequest()
.getMsgRqHdr()
.withOptions(apiRequest.getDevice());
return ServiceOrderStatusRequest.builder()
.MsgRqHdr(msgRqHdr)
.Account(
Account.builder()
.Bankinfo(
Bankinfo.builder()
.BankId(apiRequest.getBankId())
.build()
)
.Service(
Service.builder()
.SvcCode(apiRequest.getServiceCode())
.SvcType(apiRequest.getServiceType())
.SvcDesc(apiRequest.getServiceDescription())
.SvcObser(apiRequest.getServiceObserver())
.build()
)
.build()
)
.build();
}
}

View File

@ -0,0 +1,10 @@
package com.banesco.module.service_order_status_control.application.repository;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import com.banesco.module.service_order_status_control.domain.dto.response.ServiceOrderStatusControlResponse;
public interface ApiPrivateRepository {
ServiceOrderStatusControlResponse execute(
ServiceOrderStatusControlRequest request
);
}

View File

@ -0,0 +1,70 @@
package com.banesco.module.service_order_status_control.application.service;
import com.banesco.common.application.helper.MessageHelper;
import com.banesco.common.domain.exception.*;
import com.banesco.common.domain.model.*;
import com.banesco.module.service_order_status_control.application.repository.ApiPrivateRepository;
import com.banesco.module.service_order_status_control.application.usecase.ServiceOrderStatusControlUseCase;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import com.banesco.module.service_order_status_control.domain.dto.response.ServiceOrderStatusControlResponse;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ApplicationScoped
public class ServiceOrderStatusControlService implements ServiceOrderStatusControlUseCase {
private final MessageHelper messageHelper;
private final ApiPrivateRepository apiPrivateRepository;
@Inject
public ServiceOrderStatusControlService(
MessageHelper messageHelper,
ApiPrivateRepository apiPrivateRepository
) {
this.messageHelper = messageHelper;
this.apiPrivateRepository = apiPrivateRepository;
}
@Override
public Response execute(
ServiceOrderStatusControlRequest request
) {
log.info("Iniciando ejecucion para el cliente: {}", request.getClientId());
Response response;
try {
ApiResponse<ServiceOrderStatusControlResponse> apiResponse = apiPrivate(request);
response = messageHelper.handleSuccess(
apiResponse.getData(),
apiResponse.getStatusResponse().getStatusCode()
);
} catch (ApiPrivateException e) {
log.warn("Excepcion de la api privada: {} -> {}", e.getStatusCode(), e.getMessage());
response = messageHelper.handleException(HttpStatusCodeException.badRequest("400"));
} catch (HttpStatusCodeException e) {
log.error("Excepcion HTTP del api privada: {} - {}", e.getStatusCode(), e.getErrorCode());
response = messageHelper.handleException(e);
} catch (Exception e) {
log.error("Excepcion generica del api privada: {}", e.getMessage());
response = messageHelper.handleGenericException(e);
}
return response;
}
private ApiResponse<ServiceOrderStatusControlResponse> apiPrivate(
ServiceOrderStatusControlRequest request
) {
log.info("Ejecutando llamada al api privada: {}", request.getClientId());
return new ApiResponse<>(
apiPrivateRepository.execute(request),
messageHelper.createStatusResponse("200")
);
}
}

View File

@ -0,0 +1,10 @@
package com.banesco.module.service_order_status_control.application.usecase;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import jakarta.ws.rs.core.Response;
public interface ServiceOrderStatusControlUseCase {
Response execute(
ServiceOrderStatusControlRequest request
);
}

View File

@ -0,0 +1,114 @@
package com.banesco.module.service_order_status_control.domain.dto.request;
import com.banesco.common.domain.model.Device;
import com.banesco.common.infrastructure.context.RequestContext;
import com.banesco.module.service_order_status_control.domain.model.ServicingOrderProcedureInstanceRecord;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.netty.util.internal.StringUtil;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ServiceOrderStatusControlRequest {
@NonNull
private String customerReferenceFintechId;
@NonNull
private String appId;
@NonNull
private ServicingOrderProcedureInstanceRecord servicingOrderProcedureInstanceRecord;
@NonNull
private Device device;
@JsonIgnore
public String getBankId() {
return servicingOrderProcedureInstanceRecord
.getWorkProduct()
.getProductIdentification()
.getIdentifierValue();
}
@JsonIgnore
public String getServiceCode() {
return servicingOrderProcedureInstanceRecord
.getServiceOrderDescription();
}
@JsonIgnore
public String getServiceType() {
return servicingOrderProcedureInstanceRecord
.getServiceOrderType();
}
@JsonIgnore
public String getServiceDescription() {
String accountId = servicingOrderProcedureInstanceRecord
.getWorkProduct()
.getProductReference();
String customerId = servicingOrderProcedureInstanceRecord
.getCustomerReference()
.getPartyIdentification()
.get(0)
.getPartyIdentification()
.getIdentifierValue();
if(!StringUtil.isNullOrEmpty(accountId)) {
customerId = customerId + accountId;
}
return customerId;
}
@JsonIgnore
public String getServiceObserver() {
return servicingOrderProcedureInstanceRecord
.getServicingOrderTask()
.getTask();
}
@JsonIgnore
public String getClientId() {
return servicingOrderProcedureInstanceRecord
.getCustomerReference()
.getPartyIdentification()
.get(0)
.getPartyIdentification()
.getIdentifierValue();
}
@JsonIgnore
public String getAccountNumber() {
return servicingOrderProcedureInstanceRecord
.getWorkProduct()
.getProductReference();
}
@JsonIgnore
public static ServiceOrderStatusControlRequest fromResource(
String customerReferenceFintechId,
String appId,
ServiceOrderStatusControlRequest request
) {
return ServiceOrderStatusControlRequest.builder()
.customerReferenceFintechId(customerReferenceFintechId)
.appId(appId)
.servicingOrderProcedureInstanceRecord(request.getServicingOrderProcedureInstanceRecord())
.device(
Device.builder()
.deviceType(request.getDevice().getDeviceType())
.deviceDescription(request.getDevice().getDeviceDescription())
.deviceIp(request.getDevice().getDeviceIp())
.deviceSessionReference(
(!StringUtil.isNullOrEmpty(request.getDevice().getDeviceSessionReference()))
? request.getDevice().getDeviceSessionReference()
: RequestContext.getRequestId()
)
.build()
)
.build();
}
}

View File

@ -0,0 +1,21 @@
package com.banesco.module.service_order_status_control.domain.dto.response;
import com.banesco.module.service_order_status_control.domain.model.ServiceOrderWorkTaskResult;
import com.banesco.module.transaction.domain.model.Transaction;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
@Schema(description = "Respuesta de la solicitud de cambio de estatus del servicio")
public class ServiceOrderStatusControlResponse {
@Schema(description = "Instancia de la solicitud de cambio de estatus del servicio")
private ServiceOrderWorkTaskResult servicingOrderWorkTaskResult;
@Schema(description = "Instancia de la transaccion de cambio de estatus del servicio")
private Transaction transaction;
}

View File

@ -0,0 +1,14 @@
package com.banesco.module.service_order_status_control.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ServiceOrderWorkTaskResult {
private String task; // StatusDesc
}

View File

@ -0,0 +1,20 @@
package com.banesco.module.service_order_status_control.domain.model;
import com.banesco.common.domain.model.WorkProduct;
import com.banesco.module.party.domain.model.Party;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ServicingOrderProcedureInstanceRecord {
private WorkProduct workProduct; // BankId
private Party customerReference; // SvcDesc
private String serviceOrderType; // SvcType
private String serviceOrderDescription; // SvcCode
private ServicingOrderTask servicingOrderTask; // SvcObser
}

View File

@ -0,0 +1,14 @@
package com.banesco.module.service_order_status_control.domain.model;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class ServicingOrderTask {
private String task; // SvcObser
}

View File

@ -0,0 +1,68 @@
package com.banesco.module.service_order_status_control.infrastructure.adapter;
import com.banesco.common.application.helper.ServiceStatusValidateHelper;
import com.banesco.common.domain.exception.HttpStatusCodeException;
import com.banesco.common.domain.model.Either;
import com.banesco.module.service_order_status.application.usecase.ServiceOrderStatusUseCase;
import com.banesco.module.service_order_status.domain.dto.response.ServiceOrderStatusResponse;
import com.banesco.module.service_order_status_control.application.repository.ApiPrivateRepository;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import com.banesco.module.service_order_status_control.domain.dto.response.ServiceOrderStatusControlResponse;
import com.banesco.module.service_order_status_control.infrastructure.mapper.ServicingIssueMapper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import java.util.Objects;
@Slf4j
@ApplicationScoped
public class ApiPrivateAdapter implements ApiPrivateRepository {
private final ServiceOrderStatusUseCase serviceOrderStatusUseCase;
@Inject
public ApiPrivateAdapter(
ServiceOrderStatusUseCase serviceOrderStatusUseCase
) {
this.serviceOrderStatusUseCase = serviceOrderStatusUseCase;
}
@Override
public ServiceOrderStatusControlResponse execute(
ServiceOrderStatusControlRequest request
) {
Either<ServiceOrderStatusResponse, ServiceOrderStatusResponse> responseEither =
serviceOrderStatusUseCase.execute(
request,
ServiceOrderStatusResponse.class,
ServiceOrderStatusResponse.class
);
if(!Objects.isNull(responseEither)) {
ServiceOrderStatusResponse response = (responseEither.isLeft())
? responseEither.getLeft()
: responseEither.getRight();
if(!Objects.isNull(response)) {
validateStatus(response);
return ServicingIssueMapper.toModel(request, response);
}
}
throw HttpStatusCodeException.badRequest("400");
}
private void validateStatus(
ServiceOrderStatusResponse response
) {
if(
!Objects.isNull(response.getStatus()) &&
!response.getStatus().isEmpty()
) {
ServiceStatusValidateHelper.validateStatus(
response.getStatus().get(0)
);
}
}
}

View File

@ -0,0 +1,98 @@
package com.banesco.module.service_order_status_control.infrastructure.mapper;
import com.banesco.common.domain.model.Event;
import com.banesco.common.domain.model.Identifier;
import com.banesco.module.instruction.domain.model.Instruction;
import com.banesco.module.instruction.domain.model.InstructionIdentification;
import com.banesco.module.service_order_status.domain.dto.response.ServiceOrderStatusResponse;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import com.banesco.module.service_order_status_control.domain.dto.response.ServiceOrderStatusControlResponse;
import com.banesco.module.service_order_status_control.domain.model.ServiceOrderWorkTaskResult;
import com.banesco.module.transaction.domain.model.Transaction;
import java.util.List;
import java.util.Objects;
public class ServicingIssueMapper {
private ServicingIssueMapper() {}
public static ServiceOrderStatusControlResponse toModel(
ServiceOrderStatusControlRequest request,
ServiceOrderStatusResponse response
) {
return ServiceOrderStatusControlResponse.builder()
.servicingOrderWorkTaskResult(buildWorkTaskResult(request, response))
.transaction(buildTransaction(response))
.build();
}
private static ServiceOrderWorkTaskResult buildWorkTaskResult(
ServiceOrderStatusControlRequest request,
ServiceOrderStatusResponse response
) {
String task = request.getServiceObserver();
if(
!Objects.isNull(response.getCard()) &&
!response.getCard().isEmpty() &&
!Objects.isNull(response.getCard().get(0).getCardStatus()) &&
!response.getCard().get(0).getCardStatus().isEmpty()
) {
task = response.getCard().get(0).getCardStatus().get(0).getStatusDesc();
}
return ServiceOrderWorkTaskResult.builder()
.task(task)
.build();
}
private static Transaction buildTransaction(
ServiceOrderStatusResponse response
) {
if(Objects.isNull(response.getMsgRsHdr())) {
return null;
}
return Transaction.builder()
.transactionIdentification(
Identifier.builder()
.identifierValue(response.getMsgRsHdr().getTransactionCode())
.build()
)
.transactionType(response.getMsgRsHdr().getTransactionType())
.transactionConsecutive(response.getMsgRsHdr().getTransactionConsecutive())
.transactionDescription(response.getMsgRsHdr().getDetailReg())
.event(
Event.builder()
.eventIdentification(
Identifier.builder()
.identifierValue(response.getMsgRsHdr().getRequestId())
.build()
)
.eventSource(response.getMsgRsHdr().getAppName())
.eventType(response.getMsgRsHdr().getOperationType())
.eventStatus(response.getMsgRsHdr().getFinalStatusRequest())
.eventDescription(response.getMsgRsHdr().getCountDataOut())
.build()
)
.instruction(List.of(
Instruction.builder()
.instructionIdentifier(
InstructionIdentification.builder()
.instructionIdentification(
Identifier.builder()
.identifierValue(response.getMsgRsHdr().getSupervisorCode())
.build()
)
.build()
)
.instructionDescription((!Objects.isNull(response.getMsgRsHdr().getRegisterNumber()))
? String.join(", ", response.getMsgRsHdr().getRegisterNumber())
: null
)
.build()
))
.build();
}
}

View File

@ -0,0 +1,285 @@
package com.banesco.module.service_order_status_control.infrastructure.resource;
import com.banesco.common.domain.model.ApiResponse;
import com.banesco.common.domain.model.StatusResponse;
import com.banesco.module.service_order_status_control.application.usecase.ServiceOrderStatusControlUseCase;
import com.banesco.module.service_order_status_control.domain.dto.request.ServiceOrderStatusControlRequest;
import com.banesco.module.service_order_status_control.domain.dto.response.ServiceOrderStatusControlResponse;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.ExampleObject;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.media.SchemaProperty;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
@Slf4j
@Path("/service-order-status-control")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ServiceOrderStatusControlResource {
private final ServiceOrderStatusControlUseCase useCase;
@Inject
public ServiceOrderStatusControlResource(
ServiceOrderStatusControlUseCase useCase
) {
this.useCase = useCase;
}
@PATCH
@Path("/control")
@Operation(
summary = "Cambia el estatus del servicio",
description = "Envía el nuevo estatus del servicio"
)
@APIResponses(value = {
@APIResponse(
responseCode = "200",
description = "Operacion exitosa",
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(
implementation = ApiResponse.class,
properties = {
@SchemaProperty(
name = "data",
oneOf = {ServiceOrderStatusControlResponse.class}
),
@SchemaProperty(
name = "statusResponse",
implementation = StatusResponse.class
)
}
),
examples = @ExampleObject(
name = "Ejemplo exitoso",
value = """
{
"data": {
"servicingOrderWorkTaskResult": {
"task": "A"
},
"transaction": {
"transactionIdentification": {
"identifierValue": "1966"
},
"transactionType": "0",
"transactionConsecutive": "0000",
"transactionDescription": "Activacion y Desactivacion TDD cuenta verde ME",
"event": {
"eventIdentification": {
"identifierValue": "12345678901304"
},
"eventSource": "AS400",
"eventType": "Simple",
"eventStatus": "1232",
"eventDescription": "1"
},
"instruction": [
{
"instructionIdentifier": {
"instructionIdentification": {
"identifierValue": "BAN1738A01"
}
},
"instructionDescription": "1232"
}
]
}
},
"statusResponse": {
"statusCode": "200",
"message": "Operacion exitosa"
}
}
"""
)
)
),
@APIResponse(
responseCode = "400",
description = "Error en formato o campo requerido",
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(
implementation = ApiResponse.class,
properties = {
@SchemaProperty(
name = "data",
oneOf = {ServiceOrderStatusControlResponse.class}
),
@SchemaProperty(
name = "statusResponse",
implementation = StatusResponse.class
)
}
),
examples = {
@ExampleObject(
name = "Error VDE01 - Campo obligatorio",
value = """
{
"data": null,
"statusResponse": {
"statusCode": "VDE01",
"message": "El campo document.documentName 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 channelCode. Valores permitidos: BOLE"
}
}
"""
)
}
)
),
@APIResponse(
responseCode = "401",
description = "No autorizado",
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(
implementation = ApiResponse.class,
properties = {
@SchemaProperty(
name = "data",
oneOf = {ServiceOrderStatusControlResponse.class}
),
@SchemaProperty(
name = "statusResponse",
implementation = StatusResponse.class
)
}
),
examples = {
@ExampleObject(
name = "Error VRN08 - Identificacion del fintechId no coincide",
value = """
{
"data": null,
"statusResponse": {
"statusCode": "VRN08",
"message": "El fintechId proporcionado no coincide con el registrado para este cliente"
}
}
"""
)
}
)
),
@APIResponse(
responseCode = "503",
description = "Servicio no disponible",
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(
implementation = ApiResponse.class,
properties = {
@SchemaProperty(
name = "data",
oneOf = {ServiceOrderStatusControlResponse.class}
),
@SchemaProperty(
name = "statusResponse",
implementation = StatusResponse.class
)
}
),
examples = {
@ExampleObject(
name = "Error VRN04 - Fuera de horario",
value = """
{
"data": null,
"statusResponse": {
"statusCode": "VRN04",
"message": "El servicio no esta disponible fuera del horario operativo"
}
}
"""
),
@ExampleObject(
name = "Error VDR13 - OSB no disponible",
value = """
{
"data": null,
"statusResponse": {
"statusCode": "VDR13",
"message": "El servicio OSB no esta disponible en este momento"
}
}
"""
)
}
)
)
})
public Response control(
@RequestBody(
description = "Request de la solicitud de cambio de estatus del servicio",
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = ServiceOrderStatusControlRequest.class),
examples = @ExampleObject(
name = "Ejemplo de request",
value = """
{
"customerReferenceFintechId": "provider-test",
"appId": "DANIAPP",
"servicingOrderProcedureInstanceRecord": {
"workProduct": {
"productIdentification": {
"identifierValue": "01"
},
"productReference": "01340000000000000000"
},
"customerReference": {
"partyIdentification": [
{
"partyIdentification": {
"identifierValue": "204058941"
}
}
]
},
"serviceOrderType": "CLT",
"serviceOrderDescription": "SDP",
"servicingOrderTask": {
"task": "A"
}
},
"device": {
"deviceType": "Mobile",
"deviceDescription": "Xiaomi Note 11 PRO",
"deviceIp": "127.0.0.1",
"deviceSessionReference": "12345678901304"
}
}
"""
)
)
)
ServiceOrderStatusControlRequest request
) {
log.info("Iniciando consulta para instruccion del cliente: {}", request.getClientId());
return useCase.execute(request);
}
}

View File

@ -0,0 +1,24 @@
package com.banesco.module.transaction.domain.model;
import com.banesco.common.domain.model.Event;
import com.banesco.common.domain.model.Identifier;
import com.banesco.module.instruction.domain.model.Instruction;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.*;
import java.util.List;
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@RegisterForReflection
public class Transaction {
private Identifier transactionIdentification; // TransactionCode
private String transactionType; // TransactionType
private String transactionConsecutive; // TransactionConsecutive
private String transactionDescription; // DetailReg
private Event event; // RequestId, AppName, OperationType, FinalStatusRequest, CountDataOut
private List<Instruction> instruction; // SupervisorCode, RegisterNumber
}

View File

@ -0,0 +1,18 @@
quarkus:
http:
port: 8083
idle-timeout: 30s
thread-pool:
max-threads: 100
core-threads: 1
api:
source-id: DPIR
read-messages:
from-props: true
dom-service-order-status-control:
messages:
key: 'dom-service-order-status-control'
content: '[{"backendCode":"200","httpCode":200,"statusCode":"200","description":"Operacion exitosa"},{"backendCode":"R404","httpCode":404,"statusCode":"404","description":"Datos de validacion 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"},{"backendCode":"VRN02","httpCode":"204","statusCode":"VRN02","description":"Cliente sin productos"}]'
rest-client:
service-order-status: '{"url":"http://10.135.5.29:8001/APIDebitCardOutAppSvc/api/v1/blockUnblockDebitCard","timeout":{"connect":10000,"response":10000},"request":{"MsgRqHdr":{"SourceChannelCode":"API","SupervisorCode":"BAN1738A01","OperatorCode":"BAN1738A02","NetworkTrnInfo":{"TransactionCode":"1966","BankId":"01","AgencyCode":"1738","ChannelId":"string","ChannelUserId":"APP"},"ApplicantData":{"Application":"APIMGMT"},"VBProtocol":{"TransactionInd":false,"VBProtocolInd":true}}}}'

View File

@ -0,0 +1,41 @@
quarkus:
application:
name: dom-service-order-status-control
version: 1.0.0
## Profile
profile: dev
http:
non-application-root-path: actuator
native:
file-encoding: UTF-8
container-build: true
builder-image: quay.io/quarkus/ubi-quarkus-mandrel-builder-image:23.0.5.0-Final-java17
container-runtime: docker
additional-build-args:
- -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime
native-image-xmx: 6G
resources:
excludes: resources/*.yaml
## OpenApi
smallrye-openapi:
path: /openapi
enable: 'true'
## Swagger IU
swagger-ui:
path: /swagger-ui
always-include: 'true'
## Logs
log:
level: INFO
console:
enable: true
format: "%d{HH:mm:ss.SSS} %-5p [%t] [%X{requestId}] %c{1} - %s%e%n"
smallrye-health:
root-path: /actuator/health
liveness-path: /actuator/health/live
readiness-path: /actuator/health/ready
ui:
enable: false
debug:
print-startup-times: true
reflection: false

View File

@ -0,0 +1,86 @@
kind: ConfigMap
apiVersion: v1
metadata:
name: configmap-dom-service-order-status-control
namespace: proyecto-prueba-ja
uid: 3da345a5-97e8-4969-9e85-df937c78d17b
resourceVersion: '3493420059'
creationTimestamp: '2026-01-19T23:00:03Z'
labels:
app: dom-service-order-status-control
business_group: Accounts
annotations:
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"api.dom-service-order-status-control.messages.content":"[{\"backendCode\":\"200\",\"httpCode\":200,\"statusCode\":\"200\",\"description\":\"Operacion exitosa\"},{\"backendCode\":\"R404\",\"httpCode\":404,\"statusCode\":\"404\",\"description\":\"Datos de validacion no encontrado.\"},{\"backendCode\":\"503\",\"httpCode\":503,\"statusCode\":\"503\",\"description\":\"Uso interno\"},{\"backendCode\":\"422\",\"httpCode\":422,\"statusCode\":\"422\",\"description\":\"Uso interno\"},{\"backendCode\":\"500\",\"httpCode\":500,\"statusCode\":\"500\",\"description\":\"Uso interno\"},{\"backendCode\":\"100\",\"httpCode\":503,\"statusCode\":\"503\",\"description\":\"VDR13 - OSB Disponible\"},{\"backendCode\":\"OSB-382505\",\"httpCode\":503,\"statusCode\":\"503\",\"description\":\"VDR13 - OSB Disponible\"},{\"backendCode\":\"OSB-380002\",\"httpCode\":503,\"statusCode\":\"503\",\"description\":\"VDR13 - OSB Disponible\"},{\"backendCode\":\"ERROR\",\"httpCode\":400,\"statusCode\":\"400\",\"description\":\"Uso interno\"},{\"backendCode\":\"400\",\"httpCode\":400,\"statusCode\":\"400\",\"description\":\"Uso interno\"},{\"backendCode\":\"401\",\"httpCode\":401,\"statusCode\":\"401\",\"description\":\"Uso interno\"},{\"backendCode\":\"403\",\"httpCode\":403,\"statusCode\":\"403\",\"description\":\"Uso interno\"},{\"backendCode\":\"404\",\"httpCode\":404,\"statusCode\":\"404\",\"description\":\"Uso interno\"},{\"backendCode\":\"default\",\"httpCode\":409,\"statusCode\":\"409\",\"description\":\"Conflicto\"},{\"backendCode\":\"424\",\"httpCode\":424,\"statusCode\":\"424\",\"description\":\"Error de dependencia\"},{\"backendCode\":\"VDE01\",\"httpCode\":400,\"statusCode\":\"VDE01\",\"description\":\"VDE01 - Error en dato de entrada obligatorio: %s\"},{\"backendCode\":\"VDE02\",\"httpCode\":400,\"statusCode\":\"VDE02\",\"description\":\"VDE02 - Error en valor permitido para campo: %s\"},{\"backend_code\":\"204\",\"http_code\":\"200\",\"status_code\":\"200\",\"description\":\"Cliente sin productos\",\"status\":\"ok\"}]","api.dom-service-order-status-control.messages.key":"dom-service-order-status-control","api.read-messages.from-props":"true","api.rest-client.service-status":"{\"url\":\"http://10.135.193.156:8080/RequestPayment/notificationRequestPayment/v1/generate/createRequestFile\",\"timeout\":{\"connect\":10000,\"response\":10000},\"statusSuccess\":\"00\"}","api.source-id":"DPIR","quarkus.application.name":"dom-service-order-status-control","quarkus.application.version":"1.0.0","quarkus.debug.print-startup-times":"true","quarkus.debug.reflection":"false","quarkus.http.idle-timeout":"30s","quarkus.http.non-application-root-path":"actuator","quarkus.http.port":"8080","quarkus.log.console.enable":"true","quarkus.log.console.format":"%d{HH:mm:ss.SSS} %-5p [%t] [%X{requestId}] %c{1} - %s%e%n","quarkus.log.level":"INFO","quarkus.profile":"dev","quarkus.smallrye-health.liveness-path":"/actuator/health/live","quarkus.smallrye-health.readiness-path":"/actuator/health/ready","quarkus.smallrye-health.root-path":"/actuator/health","quarkus.smallrye-health.ui.enable":"false","quarkus.smallrye-openapi.enable":"true","quarkus.smallrye-openapi.path":"/openapi","quarkus.swagger-ui.always-include":"true","quarkus.swagger-ui.path":"/swagger-ui","quarkus.thread-pool.core-threads":"1","quarkus.thread-pool.max-threads":"100"},"kind":"ConfigMap","metadata":{"annotations":{"argocd.argoproj.io/sync-options":"SkipDryRunOnMissingResource=true"},"app":"dom-service-order-status-control","labels":{"app":"dom-service-order-status-control","business_group":"Accounts"},"name":"configmap-dom-service-order-status-control","namespace":"proyecto-prueba-ja"}}
managedFields:
- manager: kubectl-client-side-apply
operation: Update
apiVersion: v1
time: '2026-01-19T23:00:03Z'
fieldsType: FieldsV1
fieldsV1:
'f:data':
'f:api.read-messages.from-props': {}
'f:quarkus.http.idle-timeout': {}
'f:quarkus.thread-pool.max-threads': {}
'f:quarkus.smallrye-health.liveness-path': {}
'f:api.source-id': {}
'f:quarkus.log.console.format': {}
'f:quarkus.smallrye-health.root-path': {}
'f:api.dom-service-order-status-control.messages.content': {}
'f:quarkus.smallrye-openapi.enable': {}
'f:quarkus.smallrye-openapi.path': {}
'f:quarkus.application.version': {}
'f:quarkus.log.level': {}
'f:api.dom-service-order-status-control.messages.key': {}
'f:quarkus.debug.print-startup-times': {}
.: {}
'f:quarkus.swagger-ui.path': {}
'f:quarkus.smallrye-health.readiness-path': {}
'f:quarkus.log.console.enable': {}
'f:quarkus.smallrye-health.ui.enable': {}
'f:quarkus.swagger-ui.always-include': {}
'f:quarkus.debug.reflection': {}
'f:api.rest-client.service-status': {}
'f:quarkus.application.name': {}
'f:quarkus.thread-pool.core-threads': {}
'f:quarkus.http.port': {}
'f:quarkus.http.non-application-root-path': {}
'f:quarkus.profile': {}
'f:metadata':
'f:annotations':
.: {}
'f:argocd.argoproj.io/sync-options': {}
'f:kubectl.kubernetes.io/last-applied-configuration': {}
'f:labels':
.: {}
'f:app': {}
'f:business_group': {}
data:
quarkus.application.version: 1.0.0
quarkus.log.level: INFO
quarkus.debug.reflection: 'false'
api.rest-client.service-status: '{"url":"http://10.135.193.156:8080/RequestPayment/notificationRequestPayment/v1/generate/createRequestPay","timeout":{"connect":10000,"response":10000},"statusSuccess":"00"}'
quarkus.debug.print-startup-times: 'true'
quarkus.swagger-ui.path: /swagger-ui
quarkus.application.name: dom-service-order-status-control
quarkus.thread-pool.core-threads: '1'
quarkus.smallrye-health.ui.enable: 'false'
quarkus.swagger-ui.always-include: 'true'
quarkus.http.non-application-root-path: actuator
api.read-messages.from-props: 'true'
quarkus.http.port: '8080'
quarkus.profile: dev
api.dom-service-order-status-control.messages.content: '[{"backendCode":"200","httpCode":200,"statusCode":"200","description":"Operacion exitosa"},{"backendCode":"R404","httpCode":404,"statusCode":"404","description":"Datos de validacion no encontrado."},{"backendCode":"503","httpCode":503,"statusCode":"503","description":"Uso interno"},{"backendCode":"422","httpCode":422,"statusCode":"422","description":"Uso interno"},{"backendCode":"500","httpCode":500,"statusCode":"500","description":"Uso interno"},{"backendCode":"100","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-382505","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"OSB-380002","httpCode":503,"statusCode":"503","description":"VDR13 - OSB Disponible"},{"backendCode":"ERROR","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"400","httpCode":400,"statusCode":"400","description":"Uso interno"},{"backendCode":"401","httpCode":401,"statusCode":"401","description":"Uso interno"},{"backendCode":"403","httpCode":403,"statusCode":"403","description":"Uso interno"},{"backendCode":"404","httpCode":404,"statusCode":"404","description":"Uso interno"},{"backendCode":"default","httpCode":409,"statusCode":"409","description":"Conflicto"},{"backendCode":"424","httpCode":424,"statusCode":"424","description":"Error de dependencia"},{"backendCode":"VDE01","httpCode":400,"statusCode":"VDE01","description":"VDE01 - Error en dato de entrada obligatorio: %s"},{"backendCode":"VDE02","httpCode":400,"statusCode":"VDE02","description":"VDE02 - Error en valor permitido para campo: %s"},{"backend_code":"204","http_code":"200","status_code":"200","description":"Cliente sin productos","status":"ok"}]'
quarkus.log.console.format: '%d{HH:mm:ss.SSS} %-5p [%t] [%X{requestId}] %c{1} - %s%e%n'
quarkus.http.idle-timeout: 30s
quarkus.thread-pool.max-threads: '100'
api.dom-service-order-status-control.messages.key: dom-service-order-status-control
quarkus.smallrye-health.liveness-path: /actuator/health/live
api.source-id: DPIR
quarkus.smallrye-health.root-path: /actuator/health
quarkus.smallrye-openapi.enable: 'true'
quarkus.smallrye-health.readiness-path: /actuator/health/ready
quarkus.log.console.enable: 'true'
quarkus.smallrye-openapi.path: /openapi