diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..ab1f416
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Ignored default folder with query files
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..e19a1fa
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..63e9001
--- /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 0000000..712ab9d
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/lab-java-springboot-rest-api.iml b/.idea/lab-java-springboot-rest-api.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/lab-java-springboot-rest-api.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..de5c651
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b13f7bd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,69 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 4.0.6
+
+
+ com.ironhack
+ lab-springboot-api
+ 0.0.1-SNAPSHOT
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 25
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-webmvc
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-validation-test
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-webmvc-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/com/ironhack/lab_springboot_api/LabSpringbootApiApplication.java b/src/main/java/com/ironhack/lab_springboot_api/LabSpringbootApiApplication.java
new file mode 100644
index 0000000..be9df3d
--- /dev/null
+++ b/src/main/java/com/ironhack/lab_springboot_api/LabSpringbootApiApplication.java
@@ -0,0 +1,13 @@
+package com.ironhack.lab_springboot_api;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class LabSpringbootApiApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(LabSpringbootApiApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/ironhack/lab_springboot_api/controller/CustomerController.java b/src/main/java/com/ironhack/lab_springboot_api/controller/CustomerController.java
new file mode 100644
index 0000000..03e3e06
--- /dev/null
+++ b/src/main/java/com/ironhack/lab_springboot_api/controller/CustomerController.java
@@ -0,0 +1,57 @@
+package com.ironhack.lab_springboot_api.controller;
+
+import com.ironhack.lab_springboot_api.model.Customer;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RestController
+@RequestMapping("/customers")
+public class CustomerController {
+
+ private List customers = new ArrayList<>();
+
+ @PostMapping
+ public ResponseEntity createCustomer(@Valid @RequestBody Customer customer) {
+ customers.add(customer);
+ return new ResponseEntity<>(customer, HttpStatus.CREATED);
+ }
+
+ @GetMapping
+ public List getAll() {
+ return customers;
+ }
+
+ @GetMapping("/{email}")
+ public ResponseEntity getByEmail(@PathVariable String email) {
+ for (Customer c : customers) {
+ if (c.getEmail().equalsIgnoreCase(email)) {
+ return ResponseEntity.ok(c);
+ }
+ }
+ return ResponseEntity.notFound().build();
+ }
+
+ @PutMapping("/{email}")
+ public ResponseEntity updateCustomer(@PathVariable String email, @Valid @RequestBody Customer updated) {
+ for (Customer c : customers) {
+ if (c.getEmail().equalsIgnoreCase(email)) {
+ c.setName(updated.getName());
+ c.setAge(updated.getAge());
+ c.setAddress(updated.getAddress());
+ return ResponseEntity.ok(c);
+ }
+ }
+ return ResponseEntity.notFound().build();
+ }
+
+ @DeleteMapping("/{email}")
+ public ResponseEntity deleteCustomer(@PathVariable String email) {
+ customers.removeIf(c -> c.getEmail().equalsIgnoreCase(email));
+ return ResponseEntity.noContent().build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ironhack/lab_springboot_api/controller/ProductController.java b/src/main/java/com/ironhack/lab_springboot_api/controller/ProductController.java
new file mode 100644
index 0000000..9ca91fc
--- /dev/null
+++ b/src/main/java/com/ironhack/lab_springboot_api/controller/ProductController.java
@@ -0,0 +1,101 @@
+package com.ironhack.lab_springboot_api.controller;
+
+import com.ironhack.lab_springboot_api.model.Product;
+import com.ironhack.lab_springboot_api.service.ProductService;
+import jakarta.validation.Valid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/products")
+public class ProductController {
+
+ private final ProductService productService;
+
+ public ProductController(ProductService productService) {
+ this.productService = productService;
+ }
+
+ @PostMapping
+ public ResponseEntity> createProduct(
+ @RequestHeader(value = "API-Key", required = false) String apiKey,
+ @Valid @RequestBody Product product) {
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+ return new ResponseEntity<>(productService.addProduct(product), HttpStatus.CREATED);
+ }
+ @GetMapping
+ public ResponseEntity> getProducts(@RequestHeader String apiKey) {
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+ return ResponseEntity.ok(productService.getProductList());
+ }
+
+ @GetMapping("/{name}")
+ public ResponseEntity> getProduct(@PathVariable String name,
+ @RequestHeader("API-Key") String apiKey) {
+
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+
+ Product product = productService.getProductByName(name);
+
+
+ if (product == null) {
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Producto no encontrado");
+ }
+
+ return ResponseEntity.ok(product);
+ }
+
+ @GetMapping("/price")
+ public ResponseEntity> getProductPrice(
+ @RequestHeader("API-Key") String apiKey,
+ @RequestParam double min,
+ @RequestParam double max) {
+
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+ return ResponseEntity.ok(productService.getByPriceRange(min, max));
+ }
+
+ // 1. Obtener por categoría
+ @GetMapping("/category/{category}")
+ public ResponseEntity> getProductByCategory(@RequestHeader("API-Key") String apiKey, @PathVariable String category) {
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+ return ResponseEntity.ok(productService.getByCategory(category));
+ }
+
+ @PutMapping("/{name}")
+ public ResponseEntity> updateProduct(@RequestHeader("API-Key") String apiKey,
+ @PathVariable String name,
+ @Valid @RequestBody Product product) {
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+ Product updated = productService.updateProduct(name, product);
+ if (updated == null) return ResponseEntity.notFound().build();
+ return ResponseEntity.ok(updated);
+ }
+
+ @DeleteMapping("/{name}")
+ public ResponseEntity> deleteProduct(@RequestHeader("API-Key") String apiKey, @PathVariable String name) {
+ if (apiKey == null || !apiKey.equals("123456")) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API Key");
+ }
+ productService.deleteProduct(name);
+ return ResponseEntity.noContent().build();
+ }
+
+
+
+}
diff --git a/src/main/java/com/ironhack/lab_springboot_api/exception/GlobalExceptionHandler.java b/src/main/java/com/ironhack/lab_springboot_api/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..f7e593f
--- /dev/null
+++ b/src/main/java/com/ironhack/lab_springboot_api/exception/GlobalExceptionHandler.java
@@ -0,0 +1,31 @@
+package com.ironhack.lab_springboot_api.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.bind.MissingRequestHeaderException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResponseEntity