diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ca5b2a6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+backend/target/
+backend/.classpath
+backend/.project
+backend/.settings/
+*.iml
+.idea/
diff --git a/README.md b/README.md
index 0ef43ea..dfc8136 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,13 @@
-# tesffullstack
+# tesffullstack — Cristian Joya
+
+Prueba técnica full stack. Entrega por capas en ramas independientes:
+
+| Rama | Contenido |
+|------|-----------|
+| `joya_bd` | Esquema MySQL `bd_joya` |
+| `joya_backend` | API REST Spring Boot |
+| `joya_frontend` | Cliente Angular `frontend_joya` |
+
+## Rama actual: `joya_backend`
+
+Servicio REST para gestión de personas. Documentación técnica, contrato de API y evidencias Postman en [`backend/README.md`](backend/README.md).
diff --git a/backend/.mvn/wrapper/maven-wrapper.jar b/backend/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..cb28b0e
Binary files /dev/null and b/backend/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/backend/.mvn/wrapper/maven-wrapper.properties b/backend/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..5f0536e
--- /dev/null
+++ b/backend/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/backend/README.md b/backend/README.md
new file mode 100644
index 0000000..cc69481
--- /dev/null
+++ b/backend/README.md
@@ -0,0 +1,130 @@
+# API REST — rama `joya_backend`
+
+**Autor:** Cristian Joya
+**Stack:** Java 17 · Spring Boot 3.2 · Spring Data JPA · Maven · MySQL 8
+**Base URL:** `http://localhost:8080`
+
+## Contexto
+
+Servicio REST que implementa el CRUD de personas sobre la base `bd_joya` (rama `joya_bd`). Centraliza la lógica de acceso a la tabla `person` y expone un contrato JSON uniforme para el cliente Angular.
+
+## Contrato de respuesta
+
+Todas las operaciones responden con la estructura definida en la prueba:
+
+| Campo | Tipo | Descripción |
+|-------|------|-------------|
+| `status` | boolean | Resultado de la operación |
+| `msg` | string | Mensaje descriptivo |
+| `data` | array \| null | Registros devueltos o `null` en operaciones sin cuerpo |
+
+```json
+{
+ "status": true,
+ "msg": "Personas obtenidas correctamente",
+ "data": [
+ {
+ "id": 1,
+ "nombre": "Cristian",
+ "apellido": "Joya",
+ "fechaNacimiento": "1995-06-15",
+ "puesto": "Desarrollador Full Stack",
+ "sueldo": 45000.00
+ }
+ ]
+}
+```
+
+## Endpoints
+
+| Método | Ruta | Descripción |
+|--------|------|-------------|
+| `GET` | `/api/persons` | Listado completo |
+| `GET` | `/api/persons/{id}` | Consulta por identificador |
+| `POST` | `/api/persons` | Registro de persona |
+| `PUT` | `/api/persons/{id}` | Actualización |
+| `DELETE` | `/api/persons/{id}` | Eliminación |
+
+## Persistencia
+
+| Parámetro | Valor |
+|-----------|-------|
+| JDBC URL | `jdbc:mysql://localhost:3306/bd_joya` |
+| Usuario | `conexion` |
+| Esquema | Gestionado por scripts SQL (`joya_bd`) |
+| Hibernate DDL | `none` |
+
+La entidad `Person` mapea la tabla `person` con `GenerationType.IDENTITY`, alineado al `AUTO_INCREMENT` del script de base de datos. La estrategia de nombres físicos respeta las columnas camelCase del esquema (`fechaNacimiento`).
+
+## Integración frontend
+
+CORS habilitado para `http://localhost:4200` (rama `joya_frontend`).
+
+## Estructura
+
+```
+backend/
+├── src/main/java/com/joya/testfullstack/
+│ ├── controller/PersonController.java
+│ ├── model/Person.java, ApiResponse.java
+│ └── repository/PersonRepository.java
+├── src/test/java/com/joya/testfullstack/
+│ ├── controller/PersonControllerTest.java
+│ └── model/ApiResponseTest.java
+└── src/main/resources/application.properties
+```
+
+## Pruebas unitarias
+
+Cobertura del contrato REST sin dependencia de MySQL, mediante `@WebMvcTest` y repositorio simulado:
+
+| Clase | Alcance |
+|-------|---------|
+| `PersonControllerTest` | CRUD completo: respuestas exitosas y errores por id inexistente |
+| `ApiResponseTest` | Fabrica de respuestas `ok` / `error` |
+
+Ejecución:
+
+```bash
+mvnw.cmd test
+```
+
+## Despliegue
+
+Requiere MySQL con `bd_joya` provisionada y el servicio levantado en el puerto `8080`. El proyecto incluye Maven Wrapper (`mvnw` / `mvnw.cmd`) para compilar y ejecutar sin dependencia de Maven global.
+
+## Evidencia Postman
+
+Colección: [`postman/tesffullstack-joya.postman_collection.json`](../postman/tesffullstack-joya.postman_collection.json)
+
+### Operaciones API
+
+**GET — Listado de personas**
+
+
+
+**GET — Persona por identificador**
+
+
+
+**POST — Alta de persona**
+
+
+
+**PUT — Actualización de persona**
+
+
+
+**DELETE — Eliminación de persona**
+
+
+
+### Validación en base de datos
+
+Comprobación en MySQL tras operaciones de escritura:
+
+
+
+
+
+
diff --git a/backend/mvnw b/backend/mvnw
new file mode 100644
index 0000000..66df285
--- /dev/null
+++ b/backend/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ 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"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/backend/mvnw.cmd b/backend/mvnw.cmd
new file mode 100644
index 0000000..95ba6f5
--- /dev/null
+++ b/backend/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/backend/pom.xml b/backend/pom.xml
new file mode 100644
index 0000000..378091d
--- /dev/null
+++ b/backend/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ com.joya
+ test-full-stack
+ 0.0.1-SNAPSHOT
+ test-full-stack
+ API REST - prueba tecnica full stack Joya
+
+
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/backend/src/main/java/com/joya/testfullstack/TestFullStackApplication.java b/backend/src/main/java/com/joya/testfullstack/TestFullStackApplication.java
new file mode 100644
index 0000000..1c90838
--- /dev/null
+++ b/backend/src/main/java/com/joya/testfullstack/TestFullStackApplication.java
@@ -0,0 +1,12 @@
+package com.joya.testfullstack;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TestFullStackApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestFullStackApplication.class, args);
+ }
+}
diff --git a/backend/src/main/java/com/joya/testfullstack/controller/PersonController.java b/backend/src/main/java/com/joya/testfullstack/controller/PersonController.java
new file mode 100644
index 0000000..05ee2ab
--- /dev/null
+++ b/backend/src/main/java/com/joya/testfullstack/controller/PersonController.java
@@ -0,0 +1,75 @@
+package com.joya.testfullstack.controller;
+
+import com.joya.testfullstack.model.ApiResponse;
+import com.joya.testfullstack.model.Person;
+import com.joya.testfullstack.repository.PersonRepository;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/persons")
+@CrossOrigin(origins = "http://localhost:4200")
+public class PersonController {
+
+ private final PersonRepository personRepository;
+
+ public PersonController(PersonRepository personRepository) {
+ this.personRepository = personRepository;
+ }
+
+ @GetMapping
+ public ApiResponse findAll() {
+ List persons = personRepository.findAll();
+ return ApiResponse.ok("Personas obtenidas correctamente", persons);
+ }
+
+ @GetMapping("/{id}")
+ public ApiResponse findById(@PathVariable Long id) {
+ return personRepository.findById(id)
+ .map(person -> ApiResponse.ok("Persona encontrada", List.of(person)))
+ .orElse(ApiResponse.error("Persona no encontrada con id: " + id));
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public ApiResponse create(@RequestBody Person person) {
+ Person saved = personRepository.save(person);
+ return ApiResponse.ok("Persona creada correctamente", List.of(saved));
+ }
+
+ @PutMapping("/{id}")
+ public ApiResponse update(@PathVariable Long id, @RequestBody Person person) {
+ return personRepository.findById(id)
+ .map(existing -> {
+ existing.setNombre(person.getNombre());
+ existing.setApellido(person.getApellido());
+ existing.setFechaNacimiento(person.getFechaNacimiento());
+ existing.setPuesto(person.getPuesto());
+ existing.setSueldo(person.getSueldo());
+ Person updated = personRepository.save(existing);
+ return ApiResponse.ok("Persona actualizada correctamente", List.of(updated));
+ })
+ .orElse(ApiResponse.error("Persona no encontrada con id: " + id));
+ }
+
+ @DeleteMapping("/{id}")
+ public ApiResponse delete(@PathVariable Long id) {
+ return personRepository.findById(id)
+ .map(person -> {
+ personRepository.delete(person);
+ return ApiResponse.ok("Persona eliminada correctamente", null);
+ })
+ .orElseGet(() -> ApiResponse.error("Persona no encontrada con id: " + id));
+ }
+}
diff --git a/backend/src/main/java/com/joya/testfullstack/model/ApiResponse.java b/backend/src/main/java/com/joya/testfullstack/model/ApiResponse.java
new file mode 100644
index 0000000..0d4ef09
--- /dev/null
+++ b/backend/src/main/java/com/joya/testfullstack/model/ApiResponse.java
@@ -0,0 +1,27 @@
+package com.joya.testfullstack.model;
+
+import java.util.List;
+
+public class ApiResponse {
+
+ public boolean status;
+ public String msg;
+ public List data;
+
+ public ApiResponse() {
+ }
+
+ public ApiResponse(boolean status, String msg, List data) {
+ this.status = status;
+ this.msg = msg;
+ this.data = data;
+ }
+
+ public static ApiResponse ok(String msg, List data) {
+ return new ApiResponse<>(true, msg, data);
+ }
+
+ public static ApiResponse error(String msg) {
+ return new ApiResponse<>(false, msg, null);
+ }
+}
diff --git a/backend/src/main/java/com/joya/testfullstack/model/Person.java b/backend/src/main/java/com/joya/testfullstack/model/Person.java
new file mode 100644
index 0000000..cc5aa32
--- /dev/null
+++ b/backend/src/main/java/com/joya/testfullstack/model/Person.java
@@ -0,0 +1,77 @@
+package com.joya.testfullstack.model;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Entity
+@Table(name = "person")
+public class Person {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String nombre;
+ private String apellido;
+
+ @Column(name = "fechaNacimiento")
+ private LocalDate fechaNacimiento;
+
+ private String puesto;
+ private BigDecimal sueldo;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getNombre() {
+ return nombre;
+ }
+
+ public void setNombre(String nombre) {
+ this.nombre = nombre;
+ }
+
+ public String getApellido() {
+ return apellido;
+ }
+
+ public void setApellido(String apellido) {
+ this.apellido = apellido;
+ }
+
+ public LocalDate getFechaNacimiento() {
+ return fechaNacimiento;
+ }
+
+ public void setFechaNacimiento(LocalDate fechaNacimiento) {
+ this.fechaNacimiento = fechaNacimiento;
+ }
+
+ public String getPuesto() {
+ return puesto;
+ }
+
+ public void setPuesto(String puesto) {
+ this.puesto = puesto;
+ }
+
+ public BigDecimal getSueldo() {
+ return sueldo;
+ }
+
+ public void setSueldo(BigDecimal sueldo) {
+ this.sueldo = sueldo;
+ }
+}
diff --git a/backend/src/main/java/com/joya/testfullstack/repository/PersonRepository.java b/backend/src/main/java/com/joya/testfullstack/repository/PersonRepository.java
new file mode 100644
index 0000000..f737848
--- /dev/null
+++ b/backend/src/main/java/com/joya/testfullstack/repository/PersonRepository.java
@@ -0,0 +1,7 @@
+package com.joya.testfullstack.repository;
+
+import com.joya.testfullstack.model.Person;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface PersonRepository extends JpaRepository {
+}
diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties
new file mode 100644
index 0000000..9aaf274
--- /dev/null
+++ b/backend/src/main/resources/application.properties
@@ -0,0 +1,13 @@
+spring.application.name=test-full-stack-joya
+
+spring.datasource.url=jdbc:mysql://localhost:3306/bd_joya
+spring.datasource.username=conexion
+spring.datasource.password=Joy@Bd2026!x7
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
+spring.jpa.hibernate.ddl-auto=none
+spring.jpa.show-sql=false
+spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+
+server.port=8080
diff --git a/backend/src/test/java/com/joya/testfullstack/controller/PersonControllerTest.java b/backend/src/test/java/com/joya/testfullstack/controller/PersonControllerTest.java
new file mode 100644
index 0000000..e837f86
--- /dev/null
+++ b/backend/src/test/java/com/joya/testfullstack/controller/PersonControllerTest.java
@@ -0,0 +1,179 @@
+package com.joya.testfullstack.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.joya.testfullstack.model.Person;
+import com.joya.testfullstack.repository.PersonRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@WebMvcTest(PersonController.class)
+class PersonControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockBean
+ private PersonRepository personRepository;
+
+ private Person samplePerson;
+
+ @BeforeEach
+ void setUp() {
+ samplePerson = new Person();
+ samplePerson.setId(1L);
+ samplePerson.setNombre("Cristian");
+ samplePerson.setApellido("Joya");
+ samplePerson.setFechaNacimiento(LocalDate.of(1995, 6, 15));
+ samplePerson.setPuesto("Desarrollador Full Stack");
+ samplePerson.setSueldo(new BigDecimal("45000.00"));
+ }
+
+ @Test
+ void findAll_returnsPersonList() throws Exception {
+ when(personRepository.findAll()).thenReturn(List.of(samplePerson));
+
+ mockMvc.perform(get("/api/persons"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(true))
+ .andExpect(jsonPath("$.msg").value("Personas obtenidas correctamente"))
+ .andExpect(jsonPath("$.data.length()").value(1))
+ .andExpect(jsonPath("$.data[0].nombre").value("Cristian"));
+ }
+
+ @Test
+ void findById_whenExists_returnsPerson() throws Exception {
+ when(personRepository.findById(1L)).thenReturn(Optional.of(samplePerson));
+
+ mockMvc.perform(get("/api/persons/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(true))
+ .andExpect(jsonPath("$.msg").value("Persona encontrada"))
+ .andExpect(jsonPath("$.data[0].id").value(1));
+ }
+
+ @Test
+ void findById_whenNotFound_returnsError() throws Exception {
+ when(personRepository.findById(99L)).thenReturn(Optional.empty());
+
+ mockMvc.perform(get("/api/persons/99"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(false))
+ .andExpect(jsonPath("$.msg").value("Persona no encontrada con id: 99"))
+ .andExpect(jsonPath("$.data").doesNotExist());
+ }
+
+ @Test
+ void create_persistsAndReturnsCreated() throws Exception {
+ Person request = new Person();
+ request.setNombre("Ana");
+ request.setApellido("Ruiz");
+ request.setFechaNacimiento(LocalDate.of(1992, 8, 10));
+ request.setPuesto("QA");
+ request.setSueldo(new BigDecimal("32000.00"));
+
+ Person saved = new Person();
+ saved.setId(2L);
+ saved.setNombre(request.getNombre());
+ saved.setApellido(request.getApellido());
+ saved.setFechaNacimiento(request.getFechaNacimiento());
+ saved.setPuesto(request.getPuesto());
+ saved.setSueldo(request.getSueldo());
+
+ when(personRepository.save(any(Person.class))).thenReturn(saved);
+
+ mockMvc.perform(post("/api/persons")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isCreated())
+ .andExpect(jsonPath("$.status").value(true))
+ .andExpect(jsonPath("$.msg").value("Persona creada correctamente"))
+ .andExpect(jsonPath("$.data[0].id").value(2))
+ .andExpect(jsonPath("$.data[0].nombre").value("Ana"));
+ }
+
+ @Test
+ void update_whenExists_updatesFields() throws Exception {
+ Person request = new Person();
+ request.setNombre("Cristian");
+ request.setApellido("Joya Martinez");
+ request.setFechaNacimiento(LocalDate.of(1995, 6, 15));
+ request.setPuesto("Full Stack");
+ request.setSueldo(new BigDecimal("52000.00"));
+
+ when(personRepository.findById(1L)).thenReturn(Optional.of(samplePerson));
+ when(personRepository.save(any(Person.class))).thenAnswer(invocation -> invocation.getArgument(0));
+
+ mockMvc.perform(put("/api/persons/1")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(true))
+ .andExpect(jsonPath("$.msg").value("Persona actualizada correctamente"))
+ .andExpect(jsonPath("$.data[0].apellido").value("Joya Martinez"));
+ }
+
+ @Test
+ void update_whenNotFound_returnsError() throws Exception {
+ Person request = new Person();
+ request.setNombre("Test");
+
+ when(personRepository.findById(99L)).thenReturn(Optional.empty());
+
+ mockMvc.perform(put("/api/persons/99")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(false))
+ .andExpect(jsonPath("$.msg").value("Persona no encontrada con id: 99"));
+
+ verify(personRepository, never()).save(any());
+ }
+
+ @Test
+ void delete_whenExists_removesPerson() throws Exception {
+ when(personRepository.findById(1L)).thenReturn(Optional.of(samplePerson));
+
+ mockMvc.perform(delete("/api/persons/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(true))
+ .andExpect(jsonPath("$.msg").value("Persona eliminada correctamente"));
+
+ verify(personRepository).delete(samplePerson);
+ }
+
+ @Test
+ void delete_whenNotFound_returnsError() throws Exception {
+ when(personRepository.findById(99L)).thenReturn(Optional.empty());
+
+ mockMvc.perform(delete("/api/persons/99"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status").value(false))
+ .andExpect(jsonPath("$.msg").value("Persona no encontrada con id: 99"));
+
+ verify(personRepository, never()).delete(any());
+ }
+}
diff --git a/backend/src/test/java/com/joya/testfullstack/model/ApiResponseTest.java b/backend/src/test/java/com/joya/testfullstack/model/ApiResponseTest.java
new file mode 100644
index 0000000..9b658f3
--- /dev/null
+++ b/backend/src/test/java/com/joya/testfullstack/model/ApiResponseTest.java
@@ -0,0 +1,36 @@
+package com.joya.testfullstack.model;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ApiResponseTest {
+
+ @Test
+ void ok_buildsSuccessResponse() {
+ Person person = new Person();
+ person.setId(1L);
+ person.setNombre("Cristian");
+
+ ApiResponse response = ApiResponse.ok("Operacion exitosa", List.of(person));
+
+ assertTrue(response.status);
+ assertEquals("Operacion exitosa", response.msg);
+ assertEquals(1, response.data.size());
+ assertEquals("Cristian", response.data.get(0).getNombre());
+ }
+
+ @Test
+ void error_buildsFailureResponse() {
+ ApiResponse response = ApiResponse.error("Recurso no encontrado");
+
+ assertFalse(response.status);
+ assertEquals("Recurso no encontrado", response.msg);
+ assertNull(response.data);
+ }
+}
diff --git a/postman/actualizar_persona.jpg b/postman/actualizar_persona.jpg
new file mode 100644
index 0000000..684331f
Binary files /dev/null and b/postman/actualizar_persona.jpg differ
diff --git a/postman/actualizar_persona_bd.jpg b/postman/actualizar_persona_bd.jpg
new file mode 100644
index 0000000..686b9fa
Binary files /dev/null and b/postman/actualizar_persona_bd.jpg differ
diff --git a/postman/crear_persona.jpg b/postman/crear_persona.jpg
new file mode 100644
index 0000000..f4620d8
Binary files /dev/null and b/postman/crear_persona.jpg differ
diff --git a/postman/crear_persona_bd.jpg b/postman/crear_persona_bd.jpg
new file mode 100644
index 0000000..a0e99df
Binary files /dev/null and b/postman/crear_persona_bd.jpg differ
diff --git a/postman/eliminar_persona.jpg b/postman/eliminar_persona.jpg
new file mode 100644
index 0000000..30a4156
Binary files /dev/null and b/postman/eliminar_persona.jpg differ
diff --git a/postman/eliminar_persona_bd.jpg b/postman/eliminar_persona_bd.jpg
new file mode 100644
index 0000000..c5efcd5
Binary files /dev/null and b/postman/eliminar_persona_bd.jpg differ
diff --git a/postman/persona_por_id.jpg b/postman/persona_por_id.jpg
new file mode 100644
index 0000000..8ea56cc
Binary files /dev/null and b/postman/persona_por_id.jpg differ
diff --git a/postman/tesffullstack-joya.postman_collection.json b/postman/tesffullstack-joya.postman_collection.json
new file mode 100644
index 0000000..a08d1c0
--- /dev/null
+++ b/postman/tesffullstack-joya.postman_collection.json
@@ -0,0 +1,53 @@
+{
+ "info": {
+ "name": "tesffullstack - joya",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
+ },
+ "item": [
+ {
+ "name": "GET todas las personas",
+ "request": {
+ "method": "GET",
+ "url": "http://localhost:8080/api/persons"
+ }
+ },
+ {
+ "name": "GET persona por id",
+ "request": {
+ "method": "GET",
+ "url": "http://localhost:8080/api/persons/1"
+ }
+ },
+ {
+ "name": "POST crear persona",
+ "request": {
+ "method": "POST",
+ "header": [{ "key": "Content-Type", "value": "application/json" }],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"nombre\": \"Ana\",\n \"apellido\": \"Ruiz\",\n \"fechaNacimiento\": \"1992-08-10\",\n \"puesto\": \"QA\",\n \"sueldo\": 32000.00\n}"
+ },
+ "url": "http://localhost:8080/api/persons"
+ }
+ },
+ {
+ "name": "PUT actualizar persona",
+ "request": {
+ "method": "PUT",
+ "header": [{ "key": "Content-Type", "value": "application/json" }],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"nombre\": \"Cristian\",\n \"apellido\": \"Joya\",\n \"fechaNacimiento\": \"1995-06-15\",\n \"puesto\": \"Full Stack\",\n \"sueldo\": 48000.00\n}"
+ },
+ "url": "http://localhost:8080/api/persons/1"
+ }
+ },
+ {
+ "name": "DELETE persona",
+ "request": {
+ "method": "DELETE",
+ "url": "http://localhost:8080/api/persons/2"
+ }
+ }
+ ]
+}
diff --git a/postman/todas_las_personas.jpg b/postman/todas_las_personas.jpg
new file mode 100644
index 0000000..3a9d194
Binary files /dev/null and b/postman/todas_las_personas.jpg differ