From 841f811754168ac8c63ce2e4a52e5f3c487e84c1 Mon Sep 17 00:00:00 2001 From: AlvaroGuGo303 <55746517+AlvaroGuGo303@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:14:34 +0100 Subject: [PATCH 1/6] deleted simulado, added mock-server. changed k6 to oficial version --- docker-compose.yaml | 73 ++++++++++++++++--------------- mock-server/.gitignore | 4 ++ mock-server/README.md | 16 +++++++ mock-server/package.json | 12 ++++++ mock-server/server.js | 93 ++++++++++++++++++++++++++++++++++++++++ readme.md | 20 ++++++--- readmeES.md | 41 ++++++++++++++++++ 7 files changed, 220 insertions(+), 39 deletions(-) create mode 100644 mock-server/.gitignore create mode 100644 mock-server/README.md create mode 100644 mock-server/package.json create mode 100644 mock-server/server.js create mode 100644 readmeES.md diff --git a/docker-compose.yaml b/docker-compose.yaml index 2b20a5d9..86164530 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,35 +1,40 @@ -version: "3.3" +version: '4.1' services: - influxdb: - image: influxdb:1.8.2 - ports: - - "8086:8086" - environment: - - INFLUXDB_DB=k6 - grafana: - image: grafana/grafana:8.1.2 - ports: - - "3000:3000" - environment: - - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin - - GF_AUTH_ANONYMOUS_ENABLED=true - - GF_AUTH_BASIC_ENABLED=false - volumes: - - ./shared/grafana:/etc/grafana/provisioning/ - simulado: - image : ldabiralai/simulado:latest - ports: - - "3001:80" - volumes: - - ./shared/simulado:/app - command: ./bin/simulado -f /app/mocks.json - k6: - image: loadimpact/k6:0.28.0 - ports: - - "6565:6565" - volumes: - - ./shared/k6:/scripts - environment: - - K6_OUT=influxdb=http://influxdb:8086/k6 - extra_hosts: - - "host.docker.internal:host-gateway" + influxdb: + image: influxdb:1.8.2 + ports: + - '8086:8086' + environment: + - INFLUXDB_DB=k6 + grafana: + image: grafana/grafana:8.1.2 + ports: + - '3000:3000' + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + volumes: + - ./shared/grafana:/etc/grafana/provisioning/ + mock-server: + image: node:18-alpine + ports: + - '3001:3001' + working_dir: /app + volumes: + - ./mock-server:/app + - ./shared/simulado:/shared/simulado + command: sh -c "npm install && npm start" + environment: + - NODE_ENV=production + k6: + # Usando grafana/k6 -> version oficial de Grafana + image: grafana/k6:latest + ports: + - '6565:6565' + volumes: + - ./shared/k6:/scripts + environment: + - K6_OUT=influxdb=http://influxdb:8086/k6 + extra_hosts: + - 'host.docker.internal:host-gateway' diff --git a/mock-server/.gitignore b/mock-server/.gitignore new file mode 100644 index 00000000..d6665a5c --- /dev/null +++ b/mock-server/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +package-lock.json +*.log + diff --git a/mock-server/README.md b/mock-server/README.md new file mode 100644 index 00000000..e498d6ab --- /dev/null +++ b/mock-server/README.md @@ -0,0 +1,16 @@ +# IMPORTANTE LEER: + +Por motivos de seguridad ya que desconozco que contiene simulado, he remplzado totalmente el contenedor simulado por este nuevo contenedor que es un express + +Se inicia en: `http://localhost:3001` +Simula las APIs según `shared/simulado/mocks.json`. + +Verificación + +```bash +curl http://localhost:3001/product/1/similarids +#[2,3,4] + +curl http://localhost:3001/product/1 +#{"id":"1","name":"Shirt","price":9.99,"availability":true} +``` diff --git a/mock-server/package.json b/mock-server/package.json new file mode 100644 index 00000000..de191445 --- /dev/null +++ b/mock-server/package.json @@ -0,0 +1,12 @@ +{ + "name": "mockServer", + "version": "1.0.0", + "description": "Mock server que remplaza a simulado para garantizar la seguridad", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/mock-server/server.js b/mock-server/server.js new file mode 100644 index 00000000..db111a16 --- /dev/null +++ b/mock-server/server.js @@ -0,0 +1,93 @@ +/** + * MockServer -> Remplazo de simulado + * Ver mocks.json + */ + +const express = require('express'); +const fs = require('fs'); +const path = require('path'); + +const app = express(); +const PORT = 3001; + +// Cargar mocks desde el archivo JSON +// Funciona tanto en Docker como en ejecución local +const mocksPath = + process.env.MOCKS_PATH || path.join(__dirname, '..', 'shared', 'simulado', 'mocks.json'); +// Si estamos en Docker, usar la ruta montada +const dockerMocksPath = '/shared/simulado/mocks.json'; +const finalMocksPath = require('fs').existsSync(dockerMocksPath) ? dockerMocksPath : mocksPath; + +let mocks = []; + +try { + const mocksData = fs.readFileSync(finalMocksPath, 'utf8'); + mocks = JSON.parse(mocksData); + console.log(`✓ Cargados ${mocks.length} mocks desde ${finalMocksPath}`); +} catch (error) { + console.error('✗ Error cargando mocks:', error.message); + console.error(` Intentó cargar desde: ${finalMocksPath}`); + process.exit(1); +} + +// Middleware para parsear JSON +app.use(express.json()); + +// Función para encontrar un mock por path +function findMock(path) { + return mocks.find((mock) => mock.path === path); +} + +// Endpoint genérico que maneja todos los paths +app.get('*', (req, res) => { + const mock = findMock(req.path); + + if (!mock) { + console.log(`⚠️ Mock no encontrado para: ${req.path}`); + return res.status(404).json({ error: 'Not found' }); + } + + // Aplicar delay si existe + if (mock.delay) { + setTimeout(() => { + sendResponse(mock, res); + }, mock.delay); + } else { + sendResponse(mock, res); + } +}); + +function sendResponse(mock, res) { + // Establecer headers + if (mock.headers) { + Object.keys(mock.headers).forEach((key) => { + res.setHeader(key, mock.headers[key]); + }); + } + + // Establecer status code (default 200) + const status = mock.status || 200; + + // Enviar respuesta + if (mock.body) { + // Si el body es un string JSON, parsearlo primero + try { + const body = typeof mock.body === 'string' ? JSON.parse(mock.body) : mock.body; + res.status(status).json(body); + } catch (e) { + // Si no es JSON válido, enviar como string + res.status(status).send(mock.body); + } + } else { + res.status(status).end(); + } +} + +// Iniciar servidor +app.listen(PORT, () => { + console.log(`🚀 Mock Server seguro corriendo en http://localhost:${PORT}`); + console.log(`📋 Endpoints disponibles:`); + mocks.forEach((mock) => { + console.log(` ${mock.path}${mock.delay ? ` (delay: ${mock.delay}ms)` : ''}`); + }); +}); diff --git a/readme.md b/readme.md index bc0c5cc7..8c517179 100644 --- a/readme.md +++ b/readme.md @@ -1,33 +1,43 @@ # Backend dev technical test + We want to offer a new feature to our customers showing similar products to the one they are currently seeing. To do this we agreed with our front-end applications to create a new REST API operation that will provide them the product detail of the similar products for a given one. [Here](./similarProducts.yaml) is the contract we agreed. We already have an endpoint that provides the product Ids similar for a given one. We also have another endpoint that returns the product detail by product Id. [Here](./existingApis.yaml) is the documentation of the existing APIs. **Create a Spring boot application that exposes the agreed REST API on port 5000.** -![Diagram](./assets/diagram.jpg "Diagram") +![Diagram](./assets/diagram.jpg 'Diagram') Note that _Test_ and _Mocks_ components are given, you must only implement _yourApp_. ## Testing and Self-evaluation + You can run the same test we will put through your application. You just need to have docker installed. First of all, you may need to enable file sharing for the `shared` folder on your docker dashboard -> settings -> resources -> file sharing. Then you can start the mocks and other needed infrastructure with the following command. + ``` -docker-compose up -d simulado influxdb grafana +docker-compose up -d mock-server influxdb grafana ``` + Check that mocks are working with a sample request to [http://localhost:3001/product/1/similarids](http://localhost:3001/product/1/similarids). +**Note:** Deleted simulado and created mock server that uses a secure Node.js for security reasons. + To execute the test run: + ``` docker-compose run --rm k6 run scripts/test.js ``` + Browse [http://localhost:3000/d/Le2Ku9NMk/k6-performance-test](http://localhost:3000/d/Le2Ku9NMk/k6-performance-test) to view the results. ## Evaluation + The following topics will be considered: -- Code clarity and maintainability -- Performance -- Resilience + +- Code clarity and maintainability +- Performance +- Resilience diff --git a/readmeES.md b/readmeES.md new file mode 100644 index 00000000..90d9ad8f --- /dev/null +++ b/readmeES.md @@ -0,0 +1,41 @@ +# Prueba técnica de desarrollo backend + +Queremos ofrecer a nuestros clientes una nueva función que les muestre productos similares al que están viendo actualmente. Para ello, hemos acordado con nuestras aplicaciones frontend crear una nueva operación API REST que les proporcione los detalles de los productos similares a uno determinado. [Aquí](./similarProducts.yaml) está el contrato que hemos acordado. + +Ya tenemos un endpoint que proporciona los ID de productos similares a uno determinado. También tenemos otro endpoint que devuelve los detalles del producto por ID de producto. [Aquí](./existingApis.yaml) está la documentación de las API existentes. + +**Crea una aplicación Spring Boot que exponga la API REST acordada en el puerto 5000.** + +![Diagrama](./assets/diagram.jpg «Diagrama») + +Ten en cuenta que se proporcionan los componentes _Test_ y _Mocks_, solo debes implementar _yourApp_. + +## Pruebas y autoevaluación + +Puedes ejecutar la misma prueba que realizaremos en tu aplicación. Solo necesitas tener instalado Docker. + +En primer lugar, es posible que tengas que habilitar el uso compartido de archivos para la carpeta `shared` en tu panel de control de Docker -> configuración -> recursos -> uso compartido de archivos. + +A continuación, puede iniciar las simulaciones y otra infraestructura necesaria con el siguiente comando: + +``` +docker-compose up -d mock-server influxdb grafana +``` + +Compruebe que las simulaciones funcionan con una solicitud de muestra a [http://localhost:3001/product/1/similarids](http://localhost:3001/product/1/similarids). + +Para ejecutar la prueba: + +``` +docker-compose run --rm k6 run scripts/test.js +``` + +Navega a [http://localhost:3000/d/Le2Ku9NMk/k6-performance-test](http://localhost:3000/d/Le2Ku9NMk/k6-performance-test) para ver los resultados. + +## Evaluación + +Se tendrán en cuenta los siguientes aspectos: + +- Claridad y facilidad de mantenimiento del código. +- Rendimiento. +- Resiliencia. From e20bc05380bf55772f4a12534f38596363f9e4e2 Mon Sep 17 00:00:00 2001 From: AlvaroGuGo303 <55746517+AlvaroGuGo303@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:36:32 +0100 Subject: [PATCH 2/6] using Spring Initializer add demo Java spring boot project --- .idea/.gitignore | 3 + .idea/backendDevTest.iml | 9 + .idea/compiler.xml | 21 ++ .idea/encodings.xml | 6 + .idea/jarRepositories.xml | 20 ++ .idea/misc.xml | 14 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + JavaSpringBoot/demo.zip | Bin 0 -> 15494 bytes JavaSpringBoot/demo/.gitattributes | 2 + JavaSpringBoot/demo/.gitignore | 33 ++ .../.mvn/wrapper/maven-wrapper.properties | 3 + JavaSpringBoot/demo/mvnw | 295 ++++++++++++++++++ JavaSpringBoot/demo/mvnw.cmd | 189 +++++++++++ JavaSpringBoot/demo/pom.xml | 88 ++++++ .../com/example/demo/DemoApplication.java | 13 + .../src/main/resources/application.properties | 1 + .../example/demo/DemoApplicationTests.java | 13 + 18 files changed, 724 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/backendDevTest.iml create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 JavaSpringBoot/demo.zip create mode 100644 JavaSpringBoot/demo/.gitattributes create mode 100644 JavaSpringBoot/demo/.gitignore create mode 100644 JavaSpringBoot/demo/.mvn/wrapper/maven-wrapper.properties create mode 100644 JavaSpringBoot/demo/mvnw create mode 100644 JavaSpringBoot/demo/mvnw.cmd create mode 100644 JavaSpringBoot/demo/pom.xml create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/DemoApplication.java create mode 100644 JavaSpringBoot/demo/src/main/resources/application.properties create mode 100644 JavaSpringBoot/demo/src/test/java/com/example/demo/DemoApplicationTests.java diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/backendDevTest.iml b/.idea/backendDevTest.iml new file mode 100644 index 00000000..d6ebd480 --- /dev/null +++ b/.idea/backendDevTest.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..da011822 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 00000000..bbb515b6 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..712ab9d9 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..8130ddda --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..59be48c8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/JavaSpringBoot/demo.zip b/JavaSpringBoot/demo.zip new file mode 100644 index 0000000000000000000000000000000000000000..49eb141dbedf4b9e72b640ddc7fd8f1bb678edb4 GIT binary patch literal 15494 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 literal 0 HcmV?d00001 diff --git a/JavaSpringBoot/demo/.gitattributes b/JavaSpringBoot/demo/.gitattributes new file mode 100644 index 00000000..3b41682a --- /dev/null +++ b/JavaSpringBoot/demo/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/JavaSpringBoot/demo/.gitignore b/JavaSpringBoot/demo/.gitignore new file mode 100644 index 00000000..667aaef0 --- /dev/null +++ b/JavaSpringBoot/demo/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/JavaSpringBoot/demo/.mvn/wrapper/maven-wrapper.properties b/JavaSpringBoot/demo/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..c0bcafe9 --- /dev/null +++ b/JavaSpringBoot/demo/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/JavaSpringBoot/demo/mvnw b/JavaSpringBoot/demo/mvnw new file mode 100644 index 00000000..bd8896bf --- /dev/null +++ b/JavaSpringBoot/demo/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/JavaSpringBoot/demo/mvnw.cmd b/JavaSpringBoot/demo/mvnw.cmd new file mode 100644 index 00000000..92450f93 --- /dev/null +++ b/JavaSpringBoot/demo/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/JavaSpringBoot/demo/pom.xml b/JavaSpringBoot/demo/pom.xml new file mode 100644 index 00000000..21a22220 --- /dev/null +++ b/JavaSpringBoot/demo/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.7 + + + com.example + demo + 0.0.1-SNAPSHOT + demo + Demo project for Spring Boot + + + + + + + + + + + + + + + 21 + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + io.projectreactor + reactor-test + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/DemoApplication.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 00000000..64b538a1 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/JavaSpringBoot/demo/src/main/resources/application.properties b/JavaSpringBoot/demo/src/main/resources/application.properties new file mode 100644 index 00000000..2109a440 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=demo diff --git a/JavaSpringBoot/demo/src/test/java/com/example/demo/DemoApplicationTests.java b/JavaSpringBoot/demo/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 00000000..2778a6a7 --- /dev/null +++ b/JavaSpringBoot/demo/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} From ac8f9258344eb7f3f02696aba5ba753a9f15cbd2 Mon Sep 17 00:00:00 2001 From: AlvaroGuGo303 <55746517+AlvaroGuGo303@users.noreply.github.com> Date: Thu, 20 Nov 2025 19:26:28 +0100 Subject: [PATCH 3/6] Create Hexagonal Files Architecture --- .../demo/products/application/crud/create/CreateProduct.java | 4 ++++ .../products/application/crud/create/CreateProductImp.java | 4 ++++ .../demo/products/application/crud/delete/DeleteProduct.java | 4 ++++ .../products/application/crud/delete/DeleteProductImp.java | 4 ++++ .../demo/products/application/crud/get/GetProduct.java | 4 ++++ .../demo/products/application/crud/get/GetProductImp.java | 4 ++++ .../demo/products/application/crud/patch/PatchProduct.java | 4 ++++ .../demo/products/application/crud/patch/PatchProductImp.java | 4 ++++ .../demo/products/application/crud/put/PutProduct.java | 4 ++++ .../demo/products/application/crud/put/PutProductImp.java | 4 ++++ .../demo/products/application/dto/ProductApplication.java | 4 ++++ .../products/application/mapper/ProductMapperApplication.java | 4 ++++ .../products/application/repository/ProductRepository.java | 4 ++++ .../com/example/demo/products/domain/dto/ProductDomain.java | 4 ++++ .../controller_inputAdapter/ProductControllerImp.java | 4 ++++ .../controller_inputPort/ProductController.java | 4 ++++ .../demo/products/infrastructure/entity/ProductEntity.java | 4 ++++ .../infrastructure/mapper/ProductMapperInfrastructure.java | 4 ++++ .../repository_outputAdapter/SQL/SqlProductRepositoryImp.java | 4 ++++ .../repository_outputPort/SQL/SqlProductRepository.java | 4 ++++ 20 files changed, 80 insertions(+) create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java new file mode 100644 index 00000000..ef900683 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.create; + +public interface CreateProduct { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java new file mode 100644 index 00000000..9bcf6e9a --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.create; + +public class CreateProductImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java new file mode 100644 index 00000000..2fa906c6 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.delete; + +public interface DeleteProduct { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java new file mode 100644 index 00000000..1e5cdea3 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.delete; + +public class DeleteProductImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java new file mode 100644 index 00000000..f9b87ccc --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.get; + +public class GetProduct { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java new file mode 100644 index 00000000..0f51e23c --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.get; + +public class GetProductImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java new file mode 100644 index 00000000..35d217df --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.patch; + +public interface PatchProduct { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java new file mode 100644 index 00000000..a21ff287 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.patch; + +public class PatchProductImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java new file mode 100644 index 00000000..7a5866cc --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.put; + +public interface PutProduct { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java new file mode 100644 index 00000000..0ff4de12 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.crud.put; + +public class PutProductImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java new file mode 100644 index 00000000..8c2e2d5e --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.dto; + +public class ProductApplication { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java new file mode 100644 index 00000000..a8b0f570 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.mapper; + +public class ProductMapperApplication { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java new file mode 100644 index 00000000..bade71fc --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java @@ -0,0 +1,4 @@ +package com.example.demo.products.application.repository; + +public interface ProductRepository { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java new file mode 100644 index 00000000..aaaff1b1 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java @@ -0,0 +1,4 @@ +package com.example.demo.products.domain.dto; + +public class ProductDomain { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java new file mode 100644 index 00000000..34879f0d --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.infrastructure.controller_inputAdapter; + +public class ProductControllerImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java new file mode 100644 index 00000000..14404e10 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java @@ -0,0 +1,4 @@ +package com.example.demo.products.infrastructure.controller_inputPort; + +public interface ProductController { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java new file mode 100644 index 00000000..1f2a0afd --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java @@ -0,0 +1,4 @@ +package com.example.demo.products.infrastructure.entity; + +public class ProductEntity { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java new file mode 100644 index 00000000..f7f80d1a --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java @@ -0,0 +1,4 @@ +package com.example.demo.products.infrastructure.mapper; + +public class ProductMapperInfrastructure { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java new file mode 100644 index 00000000..f8ea8a44 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java @@ -0,0 +1,4 @@ +package com.example.demo.products.infrastructure.repository_outputAdapter.SQL; + +public class SqlProductRepositoryImp { +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java new file mode 100644 index 00000000..391d9ee8 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java @@ -0,0 +1,4 @@ +package com.example.demo.products.infrastructure.repository_outputPort.SQL; + +public interface SqlProductRepository { +} From 542030ac08b3fd03220b5806ff69fb69602b1496 Mon Sep 17 00:00:00 2001 From: AlvaroGuGo303 <55746517+AlvaroGuGo303@users.noreply.github.com> Date: Fri, 21 Nov 2025 02:55:31 +0100 Subject: [PATCH 4/6] Clean files not used --- .idea/compiler.xml | 1 + JavaSpringBoot/demo.zip | Bin 15494 -> 0 bytes JavaSpringBoot/demo/pom.xml | 6 ++ .../crud/create/CreateProduct.java | 4 -- .../crud/create/CreateProductImp.java | 4 -- .../crud/delete/DeleteProduct.java | 4 -- .../crud/delete/DeleteProductImp.java | 4 -- .../application/crud/get/GetProduct.java | 4 -- .../application/crud/get/GetProductImp.java | 4 -- .../application/crud/patch/PatchProduct.java | 4 -- .../crud/patch/PatchProductImp.java | 4 -- .../application/crud/put/PutProduct.java | 4 -- .../application/crud/put/PutProductImp.java | 4 -- .../application/dto/ProductApplication.java | 13 ++++ .../mapper/ProductMapperApplication.java | 67 ++++++++++++++++++ .../repository/ProductRepository.java | 4 -- .../products/domain/dto/ProductDomain.java | 14 ++++ .../ProductControllerImp.java | 37 +++++++++- .../ProductController.java | 7 ++ .../infrastructure/entity/ProductEntity.java | 4 -- .../mapper/ProductMapperInfrastructure.java | 4 -- .../SQL/SqlProductRepositoryImp.java | 4 -- .../SQL/SqlProductRepository.java | 4 -- .../src/main/resources/application.properties | 11 +++ readme.md | 2 +- readmeES.md | 41 ----------- 26 files changed, 156 insertions(+), 103 deletions(-) delete mode 100644 JavaSpringBoot/demo.zip delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java delete mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java delete mode 100644 readmeES.md diff --git a/.idea/compiler.xml b/.idea/compiler.xml index da011822..2b6035b0 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -2,6 +2,7 @@ + diff --git a/JavaSpringBoot/demo.zip b/JavaSpringBoot/demo.zip deleted file mode 100644 index 49eb141dbedf4b9e72b640ddc7fd8f1bb678edb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15494 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 diff --git a/JavaSpringBoot/demo/pom.xml b/JavaSpringBoot/demo/pom.xml index 21a22220..bf58ef6e 100644 --- a/JavaSpringBoot/demo/pom.xml +++ b/JavaSpringBoot/demo/pom.xml @@ -54,6 +54,12 @@ reactor-test test + + + + jakarta.validation + jakarta.validation-api + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java deleted file mode 100644 index ef900683..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProduct.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.create; - -public interface CreateProduct { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java deleted file mode 100644 index 9bcf6e9a..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/create/CreateProductImp.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.create; - -public class CreateProductImp { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java deleted file mode 100644 index 2fa906c6..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProduct.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.delete; - -public interface DeleteProduct { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java deleted file mode 100644 index 1e5cdea3..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/delete/DeleteProductImp.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.delete; - -public class DeleteProductImp { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java deleted file mode 100644 index f9b87ccc..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProduct.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.get; - -public class GetProduct { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java deleted file mode 100644 index 0f51e23c..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/get/GetProductImp.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.get; - -public class GetProductImp { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java deleted file mode 100644 index 35d217df..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProduct.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.patch; - -public interface PatchProduct { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java deleted file mode 100644 index a21ff287..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/patch/PatchProductImp.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.patch; - -public class PatchProductImp { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java deleted file mode 100644 index 7a5866cc..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProduct.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.put; - -public interface PutProduct { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java deleted file mode 100644 index 0ff4de12..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/put/PutProductImp.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.crud.put; - -public class PutProductImp { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java index 8c2e2d5e..e69c3c01 100644 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/dto/ProductApplication.java @@ -1,4 +1,17 @@ package com.example.demo.products.application.dto; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ToString public class ProductApplication { + private String id; + private String name; + private Double price; + private Boolean availability; } diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java index a8b0f570..723770c1 100644 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/mapper/ProductMapperApplication.java @@ -1,4 +1,71 @@ package com.example.demo.products.application.mapper; +import com.example.demo.products.application.dto.ProductApplication; +import com.example.demo.products.domain.dto.ProductDomain; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Service public class ProductMapperApplication { + + private static ProductMapperApplication instance; + + private ProductMapperApplication(){} + + public static ProductMapperApplication getInstance() { + if(instance == null) { + instance = new ProductMapperApplication(); + } + return instance; + } + + public ProductDomain applicationToDomain(ProductApplication application){ + if(application==null) {return null;} + + ProductDomain.ProductDomainBuilder builder = ProductDomain.builder(); + + builder.id(application.getId()); + builder.name(application.getName()); + builder.price(application.getPrice()); + builder.availability(application.getAvailability()); + + return builder.build(); + } + + public ProductApplication domainToApplication(ProductDomain domain){ + if(domain==null) {return null;} + + ProductApplication.ProductApplicationBuilder builder = ProductApplication.builder(); + + builder.id(domain.getId()); + builder.name(domain.getName()); + builder.price(domain.getPrice()); + builder.availability(domain.getAvailability()); + + return builder.build(); + } + + public List listDomainToApplicationList(List domainList) { + if(domainList == null) return Collections.emptyList(); + return domainList.stream().map(this::domainToApplication).collect(Collectors.toList()); + } + + public List listApplicationToDomainList(List applicationList) { + if(applicationList == null) return Collections.emptyList(); + return applicationList.stream().map(this::applicationToDomain).collect(Collectors.toList()); + } + + public Set setDomainToApplicationSet(Set domainSet) { + if(domainSet == null) return Collections.emptySet(); + return domainSet.stream().map(this::domainToApplication).collect(Collectors.toSet()); + } + + public Set setApplicationToDomainSet(Set applicationSet) { + if(applicationSet == null) return Collections.emptySet(); + return applicationSet.stream().map(this::applicationToDomain).collect(Collectors.toSet()); + } } diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java deleted file mode 100644 index bade71fc..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/repository/ProductRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.application.repository; - -public interface ProductRepository { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java index aaaff1b1..ec8578cf 100644 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/domain/dto/ProductDomain.java @@ -1,4 +1,18 @@ package com.example.demo.products.domain.dto; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ToString + public class ProductDomain { + private String id; + private String name; + private Double price; + private Boolean availability; } diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java index 34879f0d..6b9d5df9 100644 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputAdapter/ProductControllerImp.java @@ -1,4 +1,39 @@ package com.example.demo.products.infrastructure.controller_inputAdapter; -public class ProductControllerImp { +import com.example.demo.products.application.crud.getSimilar.GetSimilarProducts; +import com.example.demo.products.application.dto.ProductApplication; +import com.example.demo.products.infrastructure.controller_inputPort.ProductController; +import com.example.demo.shared.service.ResponseHandlerService; +import com.example.demo.shared.valueObject.ResponseDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/product") +@CrossOrigin +@Slf4j +public class ProductControllerImp implements ProductController { + + private final ResponseHandlerService responseHandler; + private final GetSimilarProducts getSimilarProducts; + + @Autowired + public ProductControllerImp( + ResponseHandlerService responseHandler, + GetSimilarProducts getSimilarProducts){ + this.responseHandler = responseHandler; + this.getSimilarProducts = getSimilarProducts; + } + + @GetMapping("/{productId}/similar") + @Override + public ResponseDTO> getSimilarProducts(@PathVariable String productId) { + log.info("Received request for similar products of productId: {}", productId); + return responseHandler.execute("GET_SIMILAR_PRODUCTS", + () -> getSimilarProducts.getSimilarProductsById(productId)); + } + } diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java index 14404e10..0e196f15 100644 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/controller_inputPort/ProductController.java @@ -1,4 +1,11 @@ package com.example.demo.products.infrastructure.controller_inputPort; +import com.example.demo.products.application.dto.ProductApplication; +import com.example.demo.shared.valueObject.ResponseDTO; + +import java.util.List; + public interface ProductController { + + ResponseDTO> getSimilarProducts(String productId); } diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java deleted file mode 100644 index 1f2a0afd..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.infrastructure.entity; - -public class ProductEntity { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java deleted file mode 100644 index f7f80d1a..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.infrastructure.mapper; - -public class ProductMapperInfrastructure { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java deleted file mode 100644 index f8ea8a44..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputAdapter/SQL/SqlProductRepositoryImp.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.infrastructure.repository_outputAdapter.SQL; - -public class SqlProductRepositoryImp { -} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java deleted file mode 100644 index 391d9ee8..00000000 --- a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/repository_outputPort/SQL/SqlProductRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.example.demo.products.infrastructure.repository_outputPort.SQL; - -public interface SqlProductRepository { -} diff --git a/JavaSpringBoot/demo/src/main/resources/application.properties b/JavaSpringBoot/demo/src/main/resources/application.properties index 2109a440..79f4b83c 100644 --- a/JavaSpringBoot/demo/src/main/resources/application.properties +++ b/JavaSpringBoot/demo/src/main/resources/application.properties @@ -1 +1,12 @@ spring.application.name=demo +server.port=5000 + +# External API configuration +external.api.base-url=http://localhost:3001 +external.api.timeout.connect=2000 +external.api.timeout.read=5000 + +# Resilience configuration +resilience.circuit-breaker.failure-rate-threshold=50 +resilience.circuit-breaker.wait-duration=10000 +resilience.retry.max-attempts=3 diff --git a/readme.md b/readme.md index 8c517179..6e510820 100644 --- a/readme.md +++ b/readme.md @@ -40,4 +40,4 @@ The following topics will be considered: - Code clarity and maintainability - Performance -- Resilience +- Resilience \ No newline at end of file diff --git a/readmeES.md b/readmeES.md deleted file mode 100644 index 90d9ad8f..00000000 --- a/readmeES.md +++ /dev/null @@ -1,41 +0,0 @@ -# Prueba técnica de desarrollo backend - -Queremos ofrecer a nuestros clientes una nueva función que les muestre productos similares al que están viendo actualmente. Para ello, hemos acordado con nuestras aplicaciones frontend crear una nueva operación API REST que les proporcione los detalles de los productos similares a uno determinado. [Aquí](./similarProducts.yaml) está el contrato que hemos acordado. - -Ya tenemos un endpoint que proporciona los ID de productos similares a uno determinado. También tenemos otro endpoint que devuelve los detalles del producto por ID de producto. [Aquí](./existingApis.yaml) está la documentación de las API existentes. - -**Crea una aplicación Spring Boot que exponga la API REST acordada en el puerto 5000.** - -![Diagrama](./assets/diagram.jpg «Diagrama») - -Ten en cuenta que se proporcionan los componentes _Test_ y _Mocks_, solo debes implementar _yourApp_. - -## Pruebas y autoevaluación - -Puedes ejecutar la misma prueba que realizaremos en tu aplicación. Solo necesitas tener instalado Docker. - -En primer lugar, es posible que tengas que habilitar el uso compartido de archivos para la carpeta `shared` en tu panel de control de Docker -> configuración -> recursos -> uso compartido de archivos. - -A continuación, puede iniciar las simulaciones y otra infraestructura necesaria con el siguiente comando: - -``` -docker-compose up -d mock-server influxdb grafana -``` - -Compruebe que las simulaciones funcionan con una solicitud de muestra a [http://localhost:3001/product/1/similarids](http://localhost:3001/product/1/similarids). - -Para ejecutar la prueba: - -``` -docker-compose run --rm k6 run scripts/test.js -``` - -Navega a [http://localhost:3000/d/Le2Ku9NMk/k6-performance-test](http://localhost:3000/d/Le2Ku9NMk/k6-performance-test) para ver los resultados. - -## Evaluación - -Se tendrán en cuenta los siguientes aspectos: - -- Claridad y facilidad de mantenimiento del código. -- Rendimiento. -- Resiliencia. From bbc8bf239fb9dfe80ca83aed02162ea89dd6e71f Mon Sep 17 00:00:00 2001 From: AlvaroGuGo303 <55746517+AlvaroGuGo303@users.noreply.github.com> Date: Fri, 21 Nov 2025 02:56:12 +0100 Subject: [PATCH 5/6] Add files + logic --- .../crud/getSimilar/GetSimilarProducts.java | 10 ++ .../getSimilar/GetSimilarProductsImp.java | 66 +++++++++++++ .../config/WebClientConfig.java | 30 ++++++ .../infrastructure/entity/ProductEntity.java | 43 +++++++++ .../http_outputAdapter/ProductApiClient.java | 96 +++++++++++++++++++ .../mapper/ProductMapperInfrastructure.java | 72 ++++++++++++++ .../DateParserdFromDateToStringException.java | 9 ++ .../DateParserdFromStringToDateException.java | 7 ++ .../ExceptionXAlreadyExistException.java | 10 ++ .../ExceptionXNotExistException.java | 10 ++ .../shared/exceptions/GenericException.java | 53 ++++++++++ .../shared/exceptions/ModuleException.java | 10 ++ .../service/ResponseHandlerService.java | 36 +++++++ .../demo/shared/valueObject/GenericDTO.java | 63 ++++++++++++ .../demo/shared/valueObject/I18nMessage.java | 80 ++++++++++++++++ .../demo/shared/valueObject/JwtDTO.java | 15 +++ .../demo/shared/valueObject/MensajeDTO.java | 14 +++ .../demo/shared/valueObject/ResponseDTO.java | 16 ++++ readmeSolutionAlvaro.md | 14 +++ 19 files changed, 654 insertions(+) create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProducts.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProductsImp.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/config/WebClientConfig.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/http_outputAdapter/ProductApiClient.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromDateToStringException.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromStringToDateException.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXAlreadyExistException.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXNotExistException.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/GenericException.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ModuleException.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/service/ResponseHandlerService.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/GenericDTO.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/I18nMessage.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/JwtDTO.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/MensajeDTO.java create mode 100644 JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/ResponseDTO.java create mode 100644 readmeSolutionAlvaro.md diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProducts.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProducts.java new file mode 100644 index 00000000..fdbb2753 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProducts.java @@ -0,0 +1,10 @@ +package com.example.demo.products.application.crud.getSimilar; + +import com.example.demo.products.application.dto.ProductApplication; + +import java.util.List; + +public interface GetSimilarProducts { + List getSimilarProductsById(String productId); +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProductsImp.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProductsImp.java new file mode 100644 index 00000000..7f5a0875 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/application/crud/getSimilar/GetSimilarProductsImp.java @@ -0,0 +1,66 @@ +package com.example.demo.products.application.crud.getSimilar; + +import com.example.demo.products.application.dto.ProductApplication; +import com.example.demo.products.infrastructure.http_outputAdapter.ProductApiClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GetSimilarProductsImp implements GetSimilarProducts { + + private final ProductApiClient productApiClient; + + @Override + public List getSimilarProductsById(String productId) { + log.info("Fetching similar products for productId: {}", productId); + + try { + //1 Obtener IDs de productos similares (bloqueando para simplificar) + //Spring WebFlux -> reactivo -> usa Mono -> necesario blokear + List similarIds = productApiClient.getSimilarProductIds(productId) + .block(); + + if (similarIds == null || similarIds.isEmpty()) { + log.info("No similar products found for productId: {}", productId); + return List.of(); + } + + log.debug("Found {} similar product IDs for productId: {}", similarIds.size(), productId); + + // 2. Obtener detalles de cada producto en paralelo + Flux productsFlux = productApiClient.getProductDetails(similarIds); + + // 3. Recopilar todos los productos (bloqueando para simplificar) + List products = productsFlux + .collectList() + .block(); + + if (products == null) { + log.warn("Failed to fetch product details for productId: {}", productId); + return List.of(); + } + + // 4. Filtrar productos nulos (por si algún producto falló) + List validProducts = products.stream() + .filter(product -> product != null) + .toList(); + + log.info("Successfully retrieved {} similar products for productId: {}", + validProducts.size(), productId); + + // Los productos ya vienen ordenados por similitud según el mock server + return validProducts; + + } catch (Exception e) { + log.error("Error retrieving similar products for productId: {}", productId, e); + throw new RuntimeException("Error retrieving similar products: " + e.getMessage(), e); + } + } +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/config/WebClientConfig.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/config/WebClientConfig.java new file mode 100644 index 00000000..d9aaa1c8 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/config/WebClientConfig.java @@ -0,0 +1,30 @@ +package com.example.demo.products.infrastructure.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +@Configuration +public class WebClientConfig { + + @Value("${external.api.base-url}") + private String baseUrl; + + @Value("${external.api.timeout.connect:2000}") + private int connectTimeout; + + @Value("${external.api.timeout.read:5000}") + private int readTimeout; + + @Bean + public WebClient webClient() { + return WebClient.builder() + .baseUrl(baseUrl) + .codecs(configurer -> configurer + .defaultCodecs() + .maxInMemorySize(1024 * 1024)) + .build(); + } +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java new file mode 100644 index 00000000..5af04ef8 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/entity/ProductEntity.java @@ -0,0 +1,43 @@ +package com.example.demo.products.infrastructure.entity; + + +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@ToString + +//@Entity +//@Table(name="products", schema = "mydb") +public class ProductEntity { + + //@Id + @NotBlank(message = "ID is required and cannot be empty") + @Size(min = 1, message = "ID must have at least 1 character") + //@Column(unique = true, nullable = false, length = 255) + private String id; + + @NotBlank(message = "Name is required and cannot be empty") + @Size(min = 1, message = "Name must have at least 1 character") + //@Column(nullable = false, length = 255) + private String name; + + @NotNull(message = "Price is required") + @DecimalMin(value = "0.0", inclusive = true, message = "Price must be positive") + //@Column(nullable = false) + private Double price; + + @NotNull(message = "Availability is required") + //@Column(nullable = false) + private Boolean availability; + + +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/http_outputAdapter/ProductApiClient.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/http_outputAdapter/ProductApiClient.java new file mode 100644 index 00000000..d7b59f9f --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/http_outputAdapter/ProductApiClient.java @@ -0,0 +1,96 @@ +package com.example.demo.products.infrastructure.http_outputAdapter; + +import com.example.demo.products.application.dto.ProductApplication; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import java.time.Duration; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class ProductApiClient { + + private final WebClient webClient; + + @Value("${external.api.timeout.read:5000}") + private int readTimeout; + + + //Spring WebFlux -> reactivo -> usar Mono -> queremos 1 unico valor que es la lista + public Mono> getSimilarProductIds(String productId) { + log.debug("Fetching similar product IDs for productId: {}", productId); + return webClient.get() + .uri("/product/{productId}/similarids", productId) + .retrieve() + .bodyToFlux(String.class) + .collectList() + .timeout(Duration.ofMillis(readTimeout)) + .retryWhen(Retry.backoff(2, Duration.ofMillis(100)) + .filter(throwable -> { + if (throwable instanceof WebClientResponseException webClientError) { + return webClientError.getStatusCode().is5xxServerError(); + } + return false; + }) + .doBeforeRetry(retrySignal -> log.warn("Retrying request for similar IDs, productId: {}", productId))) + .doOnSuccess(ids -> log.debug("Successfully fetched {} similar product IDs for productId: {}", ids.size(), productId)) + .doOnError(error -> log.error("Error fetching similar product IDs for productId: {}", productId, error)) + .onErrorMap(this::mapToRuntimeException); + } + + //Spring WebFlux -> reactivo -> usar Mono -> queremos 1 unico valor + public Mono getProductDetail(String productId) { + log.debug("Fetching product detail for productId: {}", productId); + return webClient.get() + .uri("/product/{productId}", productId) + .retrieve() + .bodyToMono(ProductApplication.class) + .timeout(Duration.ofMillis(readTimeout)) + .retryWhen(Retry.backoff(2, Duration.ofMillis(100)) + .filter(throwable -> { + if (throwable instanceof WebClientResponseException webClientError) { + return webClientError.getStatusCode().is5xxServerError(); + } + return false; + }) + .doBeforeRetry(retrySignal -> log.warn("Retrying request for product detail, productId: {}", productId))) + .doOnSuccess(product -> log.debug("Successfully fetched product detail for productId: {}", productId)) + .doOnError(error -> log.error("Error fetching product detail for productId: {}", productId, error)) + .onErrorMap(this::mapToRuntimeException); + } + + //Spring WebFlux -> reactivo -> usar Flux -> queremos multiples valores distintos + public Flux getProductDetails(List productIds) { + log.debug("Fetching product details for {} products", productIds.size()); + return Flux.fromIterable(productIds) + .flatMap(this::getProductDetail, 5) // Concurrencia de 5 + .onErrorContinue((error, obj) -> log.warn("Failed to fetch product detail for: {}", obj, error)); + } + + //Centralizar logica para manejar excepciones + private Throwable mapToRuntimeException(Throwable error) { + if (error instanceof WebClientResponseException webClientError) { + if (webClientError.getStatusCode() == HttpStatus.NOT_FOUND) { + log.warn("Product not found in external API: {}", webClientError.getMessage()); + return new RuntimeException("Product not found in external API: " + webClientError.getMessage()); + } + return new RuntimeException("External API error: " + webClientError.getStatusCode(), error); + } + if (error instanceof java.util.concurrent.TimeoutException) { + log.warn("Timeout calling external API: {}", error.getMessage()); + return new RuntimeException("External API timeout", error); + } + return new RuntimeException("Error calling external API", error); + } +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java new file mode 100644 index 00000000..5247c165 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/products/infrastructure/mapper/ProductMapperInfrastructure.java @@ -0,0 +1,72 @@ +package com.example.demo.products.infrastructure.mapper; + +import com.example.demo.products.application.dto.ProductApplication; +import com.example.demo.products.infrastructure.entity.ProductEntity; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +public class ProductMapperInfrastructure { + private static ProductMapperInfrastructure instance; + + private ProductMapperInfrastructure(){} + + public static ProductMapperInfrastructure getInstance() { + if(instance == null) { + instance = new ProductMapperInfrastructure(); + } + return instance; + } + + public ProductApplication entityToApplication(ProductEntity entity){ + if(entity==null) {return null;} + + ProductApplication.ProductApplicationBuilder builder = ProductApplication.builder(); + + builder.id(entity.getId()); + builder.name(entity.getName()); + builder.price(entity.getPrice()); + builder.availability(entity.getAvailability()); + + return builder.build(); + } + + public ProductEntity applicationToEntity(ProductApplication application){ + if(application==null) {return null;} + + ProductEntity.ProductEntityBuilder builder = ProductEntity.builder(); + + builder.id(application.getId()); + builder.name(application.getName()); + builder.price(application.getPrice()); + builder.availability(application.getAvailability()); + + return builder.build(); + } + + public List listApplicationToEntityList(List applicationList) { + if(applicationList == null) return Collections.emptyList(); + return applicationList.stream().map(this::applicationToEntity).collect(Collectors.toList()); + } + + public List listEntityToApplicationList(List entityList) { + if(entityList == null) return Collections.emptyList(); + return entityList.stream().map(this::entityToApplication).collect(Collectors.toList()); + } + + public Set setApplicationToEntitySet(Set applicationSet) { + if(applicationSet == null) return Collections.emptySet(); + return applicationSet.stream().map(this::applicationToEntity).collect(Collectors.toSet()); + } + + public Set setEntityToApplicationSet(Set entitySet) { + if(entitySet == null) return Collections.emptySet(); + return entitySet.stream().map(this::entityToApplication).collect(Collectors.toSet()); + } + +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromDateToStringException.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromDateToStringException.java new file mode 100644 index 00000000..e4600a59 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromDateToStringException.java @@ -0,0 +1,9 @@ +package com.example.demo.shared.exceptions; + +import java.util.Date; + +public class DateParserdFromDateToStringException extends GenericException { + public DateParserdFromDateToStringException(Date date, String pattern){ + super("Cannot parse " + date + ". format this date with this pattern: " + pattern); + } +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromStringToDateException.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromStringToDateException.java new file mode 100644 index 00000000..66049708 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/DateParserdFromStringToDateException.java @@ -0,0 +1,7 @@ +package com.example.demo.shared.exceptions; + +public class DateParserdFromStringToDateException extends GenericException { + public DateParserdFromStringToDateException(String dateString, String pattern){ + super("Cannot parse " + dateString + ". format this date with this pattern: " + pattern); + } +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXAlreadyExistException.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXAlreadyExistException.java new file mode 100644 index 00000000..2109aef3 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXAlreadyExistException.java @@ -0,0 +1,10 @@ +package com.example.demo.shared.exceptions; + +import com.example.demo.shared.exceptions.GenericException; + +public class ExceptionXAlreadyExistException extends GenericException { + public ExceptionXAlreadyExistException(String msg){ + super(msg + " already exist"); + } +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXNotExistException.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXNotExistException.java new file mode 100644 index 00000000..c30471d1 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ExceptionXNotExistException.java @@ -0,0 +1,10 @@ +package com.example.demo.shared.exceptions; + +import com.example.demo.shared.exceptions.GenericException; + +public class ExceptionXNotExistException extends GenericException { + public ExceptionXNotExistException(String msg){ + super(msg+" not exist"); + } +} + diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/GenericException.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/GenericException.java new file mode 100644 index 00000000..f0ba4b0a --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/GenericException.java @@ -0,0 +1,53 @@ +package com.example.demo.shared.exceptions; + +import com.example.demo.shared.valueObject.I18nMessage; + +import java.util.*; + +public class GenericException extends RuntimeException { + private final List errorMessages = new ArrayList(); + + public GenericException(){ + } + + public GenericException(String key, Object... params){ + super(key); + this.addError(key,params); + } + + public GenericException(Throwable t, String key, Object... params){ + super(key, t); + this.addError(key,params); + } + + /** @deprecated **/ + @Deprecated + public Map> getErrorMap(){ + Map> map = new HashMap<>(); + Iterator iterator = this.errorMessages.iterator(); + + while (iterator.hasNext()){ + I18nMessage msg = (I18nMessage) iterator.next(); + map.put(msg.getKey(), msg.getParams()); + } + + return map; + } + + public void addErrors(Map> errorMap){ + Iterator iterator = errorMap.entrySet().iterator(); + + while (iterator.hasNext()){ + Map.Entry> e = (Map.Entry) iterator.next(); + this.addError((String) e.getKey(), e.getValue()); + } + } + + public void addError(String key, Object... params){ + this.errorMessages.add(new I18nMessage(key, Arrays.asList(params))); + } + + public List getErrorMessages(){return this.errorMessages;} + + public Boolean containsError(String key){return this.errorMessages.contains(new I18nMessage(key));} +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ModuleException.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ModuleException.java new file mode 100644 index 00000000..1ab71aab --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/exceptions/ModuleException.java @@ -0,0 +1,10 @@ +package com.example.demo.shared.exceptions; + +public class ModuleException extends GenericException { + public ModuleException(){ + } + + public ModuleException(String key, Object... params){super(key,params);} + + public ModuleException(Throwable t, String key, Object... params){super(key,params);} +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/service/ResponseHandlerService.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/service/ResponseHandlerService.java new file mode 100644 index 00000000..e236ea9a --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/service/ResponseHandlerService.java @@ -0,0 +1,36 @@ +package com.example.demo.shared.service; + +import com.example.demo.shared.exceptions.GenericException; +import com.example.demo.shared.valueObject.ResponseDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.function.Supplier; + +@Service +@Slf4j +public class ResponseHandlerService { + + /** + * Ejecuta una acción y devuelve un ResponseDTO con manejo de excepciones y logging. + * @param action nombre de la acción (para logs e infoMessages) + * @param supplier lógica de negocio que devuelve datos + * @param tipo de dato de la respuesta + * @return ResponseDTO + */ + public ResponseDTO execute(String action, Supplier supplier) { + ResponseDTO response = new ResponseDTO<>(); + try { + T data = supplier.get(); + response.setData(data); + response.addInfo(action + "_OK"); + } catch (GenericException e) { + response.setErrorMessages(e.getErrorMessages()); + log.warn("{} failed with business exception", action, e); + } catch (Exception e) { + response.addError(e.getMessage()); + log.error("{} failed", action, e); + } + return response; + } +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/GenericDTO.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/GenericDTO.java new file mode 100644 index 00000000..0c569d61 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/GenericDTO.java @@ -0,0 +1,63 @@ +package com.example.demo.shared.valueObject; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class GenericDTO implements Serializable { + protected List errorMessages = new ArrayList<>(); + protected List warningMessages = new ArrayList<>(); + protected List infoMessages = new ArrayList<>(); + + public GenericDTO() { + } + + public void addInfo(String key, Object... params) { + this.infoMessages.add(new I18nMessage(key, Arrays.asList(params))); + } + + public void addError(String key, Object... params) { + this.errorMessages.add(new I18nMessage(key, Arrays.asList(params))); + } + + public void addWarning(String key, Object... params) { + this.warningMessages.add(new I18nMessage(key, Arrays.asList(params))); + } + + public boolean hasErrors(){ + return !this.errorMessages.isEmpty(); + } + + public List getErrorMessages() { + return errorMessages; + } + + public void setErrorMessages(List errorMessages) { + this.errorMessages = errorMessages; + } + + public List getWarningMessages() { + return warningMessages; + } + + public void setWarningMessages(List warningMessages) { + this.warningMessages = warningMessages; + } + + public List getInfoMessages() { + return infoMessages; + } + + public void setInfoMessages(List infoMessages) { + this.infoMessages = infoMessages; + } + + public boolean hasObjectNotFoundException(){ + return this.getErrorMessages().stream().anyMatch((em) -> { + return em.getKey().equalsIgnoreCase("general.objectnotfound"); + }); + } + +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/I18nMessage.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/I18nMessage.java new file mode 100644 index 00000000..06a31925 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/I18nMessage.java @@ -0,0 +1,80 @@ +package com.example.demo.shared.valueObject; + +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +public class I18nMessage implements Serializable { + private String key; + private List params; + private String message; + + public I18nMessage(){ + this.key=""; + this.params= Collections.emptyList(); + } + + public I18nMessage(String key, List params){ + this.key=key; + this.params=params; + } + + public I18nMessage(String key){ + this.key=key; + this.params=Collections.emptyList(); + } + + public I18nMessage(String key, List params, String translatedMessage){ + this.key = key; + this.params = params; + this.message = translatedMessage; + } + + public String getKey() {return this.key;} + + public void setKey(String key) { + this.key = key; + } + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int hashCode(){ + int result = 1; + result = 31 * result + (this.key== null ? 0:this.key.hashCode()); + return result; + } + + public boolean equals(Object obj){ + if(this == obj){ + return true; + } else if (obj == null) { + return false; + } else if (!(obj instanceof I18nMessage)) { + return false; + }else { + I18nMessage other = (I18nMessage) obj; + if(this.key == null) { + if(other.getKey() != null){ + return false; + } + } else if (!this.key.equals(other.getKey())) { + return false; + } + return true; + } + } +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/JwtDTO.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/JwtDTO.java new file mode 100644 index 00000000..f2864d15 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/JwtDTO.java @@ -0,0 +1,15 @@ +package com.example.demo.shared.valueObject; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class JwtDTO { + private String token; + +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/MensajeDTO.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/MensajeDTO.java new file mode 100644 index 00000000..a43039ba --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/MensajeDTO.java @@ -0,0 +1,14 @@ +package com.example.demo.shared.valueObject; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class MensajeDTO { + private String mensaje; +} diff --git a/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/ResponseDTO.java b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/ResponseDTO.java new file mode 100644 index 00000000..45ada195 --- /dev/null +++ b/JavaSpringBoot/demo/src/main/java/com/example/demo/shared/valueObject/ResponseDTO.java @@ -0,0 +1,16 @@ +package com.example.demo.shared.valueObject; + + +public class ResponseDTO extends GenericDTO { + private T data; + public ResponseDTO(){ + } + + public ResponseDTO(T data){this.data=data;} + + public T getData(){return this.data;} + + public void setData(T data) { + this.data = data; + } +} diff --git a/readmeSolutionAlvaro.md b/readmeSolutionAlvaro.md new file mode 100644 index 00000000..5a94f5a6 --- /dev/null +++ b/readmeSolutionAlvaro.md @@ -0,0 +1,14 @@ +# Backend dev technical test +**Note:** Deleted simulado and created mock server that uses a secure Node.js for security reasons. + +# To execute the test run: + +docker-compose up -d mock-server influxdb grafana + +curl http://localhost:3001/product/1/similarids + +curl http://localhost:5000/product/1/similar + +docker-compose run --rm k6 run /scripts/test.js + +0% failed, 100% success \ No newline at end of file From 35f0227ce555be7a17f87f33b81867612bae52b6 Mon Sep 17 00:00:00 2001 From: AlvaroGuGo303 <55746517+AlvaroGuGo303@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:07:25 +0100 Subject: [PATCH 6/6] Update readmes --- JavaSpringBoot/readmeSolutionAlvaro.md | 16 ++++++++++++++++ JavaSpringBootAlvaro.zip | Bin 0 -> 82430 bytes readme.md | 4 ++-- readmeSolutionAlvaro.md | 5 ++++- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 JavaSpringBoot/readmeSolutionAlvaro.md create mode 100644 JavaSpringBootAlvaro.zip diff --git a/JavaSpringBoot/readmeSolutionAlvaro.md b/JavaSpringBoot/readmeSolutionAlvaro.md new file mode 100644 index 00000000..18f79d2c --- /dev/null +++ b/JavaSpringBoot/readmeSolutionAlvaro.md @@ -0,0 +1,16 @@ +Total time 3h aprox + +# To execute the test run in cmd the following: + +Start Java Spring boot application main + +docker-compose up -d simulado influxdb grafana + +curl http://localhost:3001/product/1/similarids + +curl http://localhost:5000/product/1/similar + +docker-compose run --rm k6 run /scripts/test.js + +0% failed, 100% success + diff --git a/JavaSpringBootAlvaro.zip b/JavaSpringBootAlvaro.zip new file mode 100644 index 0000000000000000000000000000000000000000..5b63fd5be685cbcaeb44d903f14517e01e7ddfbd GIT binary patch literal 82430 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 ZyMd1@jDVL?@LmW0MZ?!M&U{V&`akC&`%C}; literal 0 HcmV?d00001 diff --git a/readme.md b/readme.md index 6e510820..724365d7 100644 --- a/readme.md +++ b/readme.md @@ -19,7 +19,7 @@ First of all, you may need to enable file sharing for the `shared` folder on you Then you can start the mocks and other needed infrastructure with the following command. ``` -docker-compose up -d mock-server influxdb grafana +docker-compose up -d simulado influxdb grafana ``` Check that mocks are working with a sample request to [http://localhost:3001/product/1/similarids](http://localhost:3001/product/1/similarids). @@ -40,4 +40,4 @@ The following topics will be considered: - Code clarity and maintainability - Performance -- Resilience \ No newline at end of file +- Resilience diff --git a/readmeSolutionAlvaro.md b/readmeSolutionAlvaro.md index 5a94f5a6..2a280abb 100644 --- a/readmeSolutionAlvaro.md +++ b/readmeSolutionAlvaro.md @@ -1,7 +1,10 @@ # Backend dev technical test **Note:** Deleted simulado and created mock server that uses a secure Node.js for security reasons. +I'm not gona to execute an image that i dont know that does and have and it's not official. -# To execute the test run: +# To execute the test run in cmd the following: + +Start Java Spring boot application main docker-compose up -d mock-server influxdb grafana