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> handleValidationExceptions(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getFieldErrors().forEach(error -> + errors.put(error.getField(), error.getDefaultMessage())); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(MissingRequestHeaderException.class) + public ResponseEntity handleMissingHeader(MissingRequestHeaderException ex) { + if (ex.getHeaderName().equals("API-Key")) { + return new ResponseEntity<>("Falta el encabezado API-Key obligatorio", HttpStatus.UNAUTHORIZED); + } + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } +} \ No newline at end of file diff --git a/src/main/java/com/ironhack/lab_springboot_api/model/Customer.java b/src/main/java/com/ironhack/lab_springboot_api/model/Customer.java new file mode 100644 index 0000000..f8a3e73 --- /dev/null +++ b/src/main/java/com/ironhack/lab_springboot_api/model/Customer.java @@ -0,0 +1,57 @@ +package com.ironhack.lab_springboot_api.model; + +import jakarta.validation.constraints.*; + +public class Customer { + @NotBlank(message = "El nombre es obligatorio") + private String name; + + @Email(message = "Formato de email inválido") + @NotBlank(message = "El email es obligatorio") + private String email; + + @Min(value = 18, message = "Debe ser mayor de 18 años") + private int age; + + @NotBlank(message = "La dirección es obligatoria") + private String address; + + public Customer(String name, String email, int age, String address) { + this.name = name; + this.email = email; + this.age = age; + this.address = address; + } + + public @NotBlank(message = "El nombre es obligatorio") String getName() { + return name; + } + + public void setName(@NotBlank(message = "El nombre es obligatorio") String name) { + this.name = name; + } + + public @Email(message = "Formato de email inválido") @NotBlank(message = "El email es obligatorio") String getEmail() { + return email; + } + + public void setEmail(@Email(message = "Formato de email inválido") @NotBlank(message = "El email es obligatorio") String email) { + this.email = email; + } + + public @Min(value = 18, message = "Debe ser mayor de 18 años") int getAge() { + return age; + } + + public void setAge(@Min(value = 18, message = "Debe ser mayor de 18 años") int age) { + this.age = age; + } + + public @NotBlank(message = "La dirección es obligatoria") String getAddress() { + return address; + } + + public void setAddress(@NotBlank(message = "La dirección es obligatoria") String address) { + this.address = address; + } +} \ No newline at end of file diff --git a/src/main/java/com/ironhack/lab_springboot_api/model/Product.java b/src/main/java/com/ironhack/lab_springboot_api/model/Product.java new file mode 100644 index 0000000..10d3804 --- /dev/null +++ b/src/main/java/com/ironhack/lab_springboot_api/model/Product.java @@ -0,0 +1,59 @@ +package com.ironhack.lab_springboot_api.model; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; + +public class Product { + @NotBlank(message = "El nombre no puede estar vacío") + @Size (min = 3, message = "Mínimo 3 caracteres") + private String name; + + @Positive(message = "El Precio debe ser positivo") + private double price; + + @NotBlank(message = "La categoria es obligatoria") + private String category; + + @Positive(message = "La cantidad debe ser mayor a 0") + private int quantity; + + public Product(String name, double price, String category, int quantity) { + setName(name); + setPrice(price); + setCategory(category); + setQuantity(quantity); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } +} diff --git a/src/main/java/com/ironhack/lab_springboot_api/service/ProductService.java b/src/main/java/com/ironhack/lab_springboot_api/service/ProductService.java new file mode 100644 index 0000000..4d1eaff --- /dev/null +++ b/src/main/java/com/ironhack/lab_springboot_api/service/ProductService.java @@ -0,0 +1,66 @@ +package com.ironhack.lab_springboot_api.service; + +import com.ironhack.lab_springboot_api.model.Product; +import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + +@Service +public class ProductService { + + private List productList = new ArrayList<>(); + + public Product addProduct(Product product) { + productList.add(product); + return product; + } + + public List getProductList() { + return productList; + } + + public Product getProductByName(String name) { + for (Product p : productList) { + if (p.getName().equalsIgnoreCase(name)) { + return p; + } + } + return null; + } + + public List getByCategory(String category) { + List result = new ArrayList<>(); + for (Product p : productList) { + if (p.getCategory().equalsIgnoreCase(category)) { + result.add(p); + } + } + return result; + } + + public List getByPriceRange(double min, double max) { + List result = new ArrayList<>(); + for (Product p : productList) { + if (p.getPrice() >= min && p.getPrice() <= max) { + result.add(p); + } + } + return result; + } + + public Product updateProduct(String name, Product updatedProduct) { + for (Product p : productList) { + if (p.getName().equalsIgnoreCase(name)) { + p.setPrice(updatedProduct.getPrice()); + p.setCategory(updatedProduct.getCategory()); + p.setQuantity(updatedProduct.getQuantity()); + return p; + } + } + return null; + } + + public void deleteProduct(String name) { + productList.removeIf(p -> p.getName().equalsIgnoreCase(name)); + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..47f6bba --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=lab-springboot-api diff --git a/src/test/java/com/ironhack/lab_springboot_api/LabSpringbootApiApplicationTests.java b/src/test/java/com/ironhack/lab_springboot_api/LabSpringbootApiApplicationTests.java new file mode 100644 index 0000000..36d54b5 --- /dev/null +++ b/src/test/java/com/ironhack/lab_springboot_api/LabSpringbootApiApplicationTests.java @@ -0,0 +1,13 @@ +package com.ironhack.lab_springboot_api; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class LabSpringbootApiApplicationTests { + + @Test + void contextLoads() { + } + +}