Skip to content

Commit 0361719

Browse files
committed
Limpieza y optimización del proyecto Spring Boot AWS API
- Eliminados archivos innecesarios (Dockerfile, docker-compose.yml, archivos ZIP) - Removidos directorios vacíos y archivos residuales de DynamoDB Local - Simplificada configuración de DynamoDB para AWS real - Agregadas validaciones de datos con Jakarta Validation - Implementado manejo global de excepciones - Corregido HomeController para mostrar index.html - Actualizado puerto a 8086 - Optimizada estructura del proyecto eliminando duplicidades - Proyecto completamente funcional y limpio
1 parent 6119132 commit 0361719

11 files changed

Lines changed: 105 additions & 97 deletions

File tree

Dockerfile

Lines changed: 0 additions & 18 deletions
This file was deleted.

README.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,21 @@ Este proyecto es la API REST de BalanceFlow, desarrollada con Spring Boot, cuyo
2424
### Requisitos Previos
2525
- Java 17 o superior
2626
- Maven
27-
- (Opcional) Docker y Docker Compose para emular DynamoDB localmente (DynamoDB Local)
27+
- AWS CLI configurado (para desarrollo con DynamoDB real)
2828

29-
### Pasos para ejecutar localmente
30-
1. Clonar el repositorio.
31-
2. Configurar la base de datos DynamoDB local (o usar una instancia de prueba en la nube, si se prefiere).
32-
3. Asegurarse de que `amazon.aws.region` en `src/main/resources/application.properties` está configurado para tu región de desarrollo local o de prueba.
33-
4. Ejecutar la aplicación con Maven: `mvn spring-boot:run`
34-
5. Acceder a la aplicación en `http://localhost:8080`
29+
### Ejecutar la aplicación
30+
```bash
31+
# 1. Clonar el repositorio
32+
git clone <tu-repositorio>
33+
34+
# 2. Configurar AWS CLI (si no está configurado)
35+
aws configure
36+
37+
# 3. Ejecutar la aplicación
38+
mvn spring-boot:run
39+
40+
# 4. Acceder a la aplicación en http://localhost:8086
41+
```
3542

3643
## Configuración en AWS (para el Ejercicio 3)
3744

@@ -42,7 +49,7 @@ Este proyecto es la API REST de BalanceFlow, desarrollada con Spring Boot, cuyo
4249
- Entorno de servidor web en la plataforma **Java (Corretto 17)**.
4350
- Configurado con **"Single instance"** para optimización de costes (evitando el balanceador de carga inicial).
4451
- Se utiliza el rol de servicio IAM por defecto de Elastic Beanstalk para la gestión de recursos.
45-
- Puerto de la aplicación configurado en `8080`. El grupo de seguridad de Elastic Beanstalk debe permitir tráfico HTTP (puerto 80) y potencialmente el puerto `8080` para comunicaciones directas o salud de la aplicación.
52+
- Puerto de la aplicación configurado en `8085`. El grupo de seguridad de Elastic Beanstalk debe permitir tráfico HTTP (puerto 80) y potencialmente el puerto `8085` para comunicaciones directas o salud de la aplicación.
4653

4754
### Amazon DynamoDB (Base de Datos NoSQL)
4855
- Tabla `anabelen-products`.

docker-compose.yml

Lines changed: 0 additions & 37 deletions
This file was deleted.

pom.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<url/>
2727
</scm>
2828
<properties>
29-
<java.version>21</java.version>
29+
<java.version>17</java.version>
3030
</properties>
3131
<dependencies>
3232
<dependency>
@@ -39,6 +39,10 @@
3939
<artifactId>aws-java-sdk-dynamodb</artifactId>
4040
<version>1.12.721</version>
4141
</dependency>
42+
<dependency>
43+
<groupId>org.springframework.boot</groupId>
44+
<artifactId>spring-boot-starter-validation</artifactId>
45+
</dependency>
4246
<dependency>
4347
<groupId>org.springframework.boot</groupId>
4448
<artifactId>spring-boot-starter-test</artifactId>

src/main/java/com/anabelen/api/DynamoDBConfig.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
import org.springframework.context.annotation.Bean;
44
import org.springframework.context.annotation.Configuration;
55

6-
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; // Necesaria para @Value
6+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
77
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
88
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
99

1010
@Configuration
1111
public class DynamoDBConfig {
1212

13-
14-
@Value("${amazon.aws.region:us-east-1}")
13+
@Value("${amazon.aws.region:us-west-1}")
1514
private String amazonAWSRegion;
1615

1716
@Bean
@@ -20,7 +19,6 @@ public DynamoDBMapper dynamoDBMapper() {
2019
}
2120

2221
private AmazonDynamoDB buildAmazonDynamoDBClient() {
23-
2422
return AmazonDynamoDBClientBuilder.standard()
2523
.withRegion(amazonAWSRegion)
2624
.build();

src/main/java/com/anabelen/api/controller/HomeController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public class HomeController {
88

99
@GetMapping("/")
1010
public String home() {
11-
return "redirect:/products";
11+
return "index";
1212
}
1313
}

src/main/java/com/anabelen/api/controller/ProductController.java

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
import org.springframework.web.bind.annotation.RequestMapping;
1515
import org.springframework.web.bind.annotation.RestController;
1616

17+
import com.anabelen.api.exception.ProductNotFoundException;
1718
import com.anabelen.api.model.Product;
1819
import com.anabelen.api.repository.ProductRepository;
1920

21+
import jakarta.validation.Valid;
22+
2023
@RestController
2124
@RequestMapping("/api/products")
2225
public class ProductController {
@@ -28,16 +31,16 @@ public ProductController(ProductRepository productRepository) {
2831
}
2932

3033
@PostMapping
31-
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
32-
Product savedProduct = productRepository.save(product); //
34+
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
35+
Product savedProduct = productRepository.save(product);
3336
return new ResponseEntity<>(savedProduct, HttpStatus.CREATED);
3437
}
3538

3639
@GetMapping("/{id}")
3740
public ResponseEntity<Product> getProductById(@PathVariable String id) {
38-
Optional<Product> product = productRepository.findById(id);
39-
return product.map(value -> new ResponseEntity<>(value, HttpStatus.OK))
40-
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
41+
Product product = productRepository.findById(id)
42+
.orElseThrow(() -> new ProductNotFoundException("Producto no encontrado con ID: " + id));
43+
return new ResponseEntity<>(product, HttpStatus.OK);
4144
}
4245

4346
@GetMapping
@@ -47,31 +50,25 @@ public ResponseEntity<List<Product>> getAllProducts() {
4750
}
4851

4952
@PutMapping("/{id}")
50-
public ResponseEntity<Product> updateProduct(@PathVariable String id, @RequestBody Product product) {
51-
Optional<Product> existingProductOptional = productRepository.findById(id);
52-
if (existingProductOptional.isPresent()) {
53-
Product existingProduct = existingProductOptional.get();
54-
existingProduct.setName(product.getName());
55-
existingProduct.setDescription(product.getDescription());
56-
existingProduct.setPrice(product.getPrice());
57-
existingProduct.setStock(product.getStock());
58-
existingProduct.setMimoCategory(product.getMimoCategory());
59-
Product updatedProduct = productRepository.save(existingProduct);
60-
return new ResponseEntity<>(updatedProduct, HttpStatus.OK);
61-
} else {
62-
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
63-
}
53+
public ResponseEntity<Product> updateProduct(@PathVariable String id, @Valid @RequestBody Product product) {
54+
Product existingProduct = productRepository.findById(id)
55+
.orElseThrow(() -> new ProductNotFoundException("Producto no encontrado con ID: " + id));
56+
57+
existingProduct.setName(product.getName());
58+
existingProduct.setDescription(product.getDescription());
59+
existingProduct.setPrice(product.getPrice());
60+
existingProduct.setStock(product.getStock());
61+
existingProduct.setMimoCategory(product.getMimoCategory());
62+
Product updatedProduct = productRepository.save(existingProduct);
63+
return new ResponseEntity<>(updatedProduct, HttpStatus.OK);
6464
}
6565

6666
@DeleteMapping("/{id}")
6767
public ResponseEntity<HttpStatus> deleteProduct(@PathVariable String id) {
68-
try {
69-
productRepository.deleteById(id);
70-
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
71-
} catch (Exception e) {
72-
73-
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
74-
}
68+
Product product = productRepository.findById(id)
69+
.orElseThrow(() -> new ProductNotFoundException("Producto no encontrado con ID: " + id));
70+
productRepository.deleteById(id);
71+
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
7572
}
7673

7774

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.anabelen.api.exception;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.validation.FieldError;
6+
import org.springframework.web.bind.MethodArgumentNotValidException;
7+
import org.springframework.web.bind.annotation.ControllerAdvice;
8+
import org.springframework.web.bind.annotation.ExceptionHandler;
9+
10+
import java.util.HashMap;
11+
import java.util.Map;
12+
13+
@ControllerAdvice
14+
public class GlobalExceptionHandler {
15+
16+
@ExceptionHandler(MethodArgumentNotValidException.class)
17+
public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
18+
Map<String, String> errors = new HashMap<>();
19+
ex.getBindingResult().getAllErrors().forEach((error) -> {
20+
String fieldName = ((FieldError) error).getField();
21+
String errorMessage = error.getDefaultMessage();
22+
errors.put(fieldName, errorMessage);
23+
});
24+
return ResponseEntity.badRequest().body(errors);
25+
}
26+
27+
@ExceptionHandler(ProductNotFoundException.class)
28+
public ResponseEntity<String> handleProductNotFound(ProductNotFoundException ex) {
29+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
30+
}
31+
32+
@ExceptionHandler(Exception.class)
33+
public ResponseEntity<String> handleGenericException(Exception ex) {
34+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
35+
.body("Error interno del servidor: " + ex.getMessage());
36+
}
37+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.anabelen.api.exception;
2+
3+
public class ProductNotFoundException extends RuntimeException {
4+
public ProductNotFoundException(String message) {
5+
super(message);
6+
}
7+
}

src/main/java/com/anabelen/api/model/Product.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
44
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey;
55
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
6-
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
6+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
7+
8+
import jakarta.validation.constraints.NotBlank;
9+
import jakarta.validation.constraints.NotNull;
10+
import jakarta.validation.constraints.Min;
11+
import jakarta.validation.constraints.Size;
712

813
@DynamoDBTable(tableName = "anabelen-products")
914
public class Product {
@@ -26,6 +31,8 @@ public void setId(String id) {
2631
}
2732

2833
@DynamoDBAttribute(attributeName = "name")
34+
@NotBlank(message = "El nombre es obligatorio")
35+
@Size(max = 100, message = "El nombre no puede exceder 100 caracteres")
2936
public String getName() {
3037
return name;
3138
}
@@ -34,6 +41,8 @@ public void setName(String name) {
3441
}
3542

3643
@DynamoDBAttribute(attributeName = "description")
44+
@NotBlank(message = "La descripción es obligatoria")
45+
@Size(max = 500, message = "La descripción no puede exceder 500 caracteres")
3746
public String getDescription() {
3847
return description;
3948
}
@@ -42,6 +51,8 @@ public void setDescription(String description) {
4251
}
4352

4453
@DynamoDBAttribute(attributeName = "price")
54+
@NotNull(message = "El precio es obligatorio")
55+
@Min(value = 0, message = "El precio debe ser mayor o igual a 0")
4556
public Double getPrice() {
4657
return price;
4758
}
@@ -50,6 +61,8 @@ public void setPrice(Double price) {
5061
}
5162

5263
@DynamoDBAttribute(attributeName = "stock")
64+
@NotNull(message = "El stock es obligatorio")
65+
@Min(value = 0, message = "El stock debe ser mayor o igual a 0")
5366
public Integer getStock() {
5467
return stock;
5568
}

0 commit comments

Comments
 (0)