From 679306eebff7aa7e6cf3bfb359ad99711d3cd88d Mon Sep 17 00:00:00 2001 From: Anar Vali Date: Tue, 10 Mar 2026 03:59:55 -0700 Subject: [PATCH] lab9 --- pom.xml | 55 +++++++++++ .../com/ironhack/GlobalExceptionHandler.java | 50 ++++++++++ .../com/ironhack/SpringbootApplication.java | 12 +++ .../controllers/CustomerController.java | 68 ++++++++++++++ .../controllers/ProductController.java | 93 +++++++++++++++++++ .../java/com/ironhack/models/Customer.java | 47 ++++++++++ .../java/com/ironhack/models/Product.java | 39 ++++++++ .../com/ironhack/services/ProductService.java | 58 ++++++++++++ 8 files changed, 422 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/com/ironhack/GlobalExceptionHandler.java create mode 100644 src/main/java/com/ironhack/SpringbootApplication.java create mode 100644 src/main/java/com/ironhack/controllers/CustomerController.java create mode 100644 src/main/java/com/ironhack/controllers/ProductController.java create mode 100644 src/main/java/com/ironhack/models/Customer.java create mode 100644 src/main/java/com/ironhack/models/Product.java create mode 100644 src/main/java/com/ironhack/services/ProductService.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a2b2f5c --- /dev/null +++ b/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.3 + + + + com.ironhack + lab-java-springboot-rest-api + 0.0.1-SNAPSHOT + lab-java-springboot-rest-api + lab-java-springboot-rest-api + + + 17 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/src/main/java/com/ironhack/GlobalExceptionHandler.java b/src/main/java/com/ironhack/GlobalExceptionHandler.java new file mode 100644 index 0000000..68e09c4 --- /dev/null +++ b/src/main/java/com/ironhack/GlobalExceptionHandler.java @@ -0,0 +1,50 @@ +package com.ironhack; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import jakarta.servlet.ServletException; +import org.springframework.util.MultiValueMap; +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) { + Map error = new HashMap<>(); + error.put("error", "Missing API-Key header. You must provide the 'API-Key' in your request headers."); + return new ResponseEntity<>(error, HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity> handleRuntimeExceptions(RuntimeException ex) { + Map error = new HashMap<>(); + String message = ex.getMessage(); + error.put("error", message); + + if (message.contains("not found")) { + return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); + } else if (message.contains("Invalid API Key")) { + return new ResponseEntity<>(error, HttpStatus.UNAUTHORIZED); + } else if (message.contains("Invalid price range")) { + return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); + } + + return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); + } +} \ No newline at end of file diff --git a/src/main/java/com/ironhack/SpringbootApplication.java b/src/main/java/com/ironhack/SpringbootApplication.java new file mode 100644 index 0000000..b11ce4e --- /dev/null +++ b/src/main/java/com/ironhack/SpringbootApplication.java @@ -0,0 +1,12 @@ +package com.ironhack; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringbootApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringbootApplication.class, args); + } + +} \ No newline at end of file diff --git a/src/main/java/com/ironhack/controllers/CustomerController.java b/src/main/java/com/ironhack/controllers/CustomerController.java new file mode 100644 index 0000000..7f25e19 --- /dev/null +++ b/src/main/java/com/ironhack/controllers/CustomerController.java @@ -0,0 +1,68 @@ +package com.ironhack.controllers; + +import com.ironhack.models.Customer; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@RestController +@RequestMapping("/customers") +public class CustomerController { + + + private final List customers = new ArrayList<>(); + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Customer createCustomer(@Valid @RequestBody Customer customer) { + customers.add(customer); + return customer; + } + + @GetMapping + public List getAllCustomers() { + return customers; + } + + @GetMapping("/{email}") + public Customer getCustomerByEmail(@PathVariable String email) { + Optional customer = customers.stream() + .filter(c -> c.getEmail().equalsIgnoreCase(email)) + .findFirst(); + + if (customer.isEmpty()) { + throw new RuntimeException("Customer not found"); + } + return customer.get(); + } + + @PutMapping("/{email}") + public Customer updateCustomer(@PathVariable String email, @Valid @RequestBody Customer updatedCustomer) { + Optional existing = customers.stream() + .filter(c -> c.getEmail().equalsIgnoreCase(email)) + .findFirst(); + + if (existing.isEmpty()) { + throw new RuntimeException("Customer not found"); + } + + Customer c = existing.get(); + c.setName(updatedCustomer.getName()); + c.setAge(updatedCustomer.getAge()); + c.setAddress(updatedCustomer.getAddress()); + return c; + } + + @DeleteMapping("/{email}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCustomer(@PathVariable String email) { + boolean deleted = customers.removeIf(c -> c.getEmail().equalsIgnoreCase(email)); + if (!deleted) { + throw new RuntimeException("Customer not found"); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ironhack/controllers/ProductController.java b/src/main/java/com/ironhack/controllers/ProductController.java new file mode 100644 index 0000000..c6b8c2b --- /dev/null +++ b/src/main/java/com/ironhack/controllers/ProductController.java @@ -0,0 +1,93 @@ +package com.ironhack.controllers; + +import com.ironhack.models.Product; +import com.ironhack.services.ProductService; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@RestController +@RequestMapping("/products") +public class ProductController { + + private final ProductService productService; + + public ProductController(ProductService productService) { + this.productService = productService; + } + + private void validateApiKey(String apiKey) { + if (!"123456".equals(apiKey)) { + throw new RuntimeException("Invalid API Key"); + } + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Product createProduct(@RequestHeader("API-Key") String apiKey, @Valid @RequestBody Product product) { + validateApiKey(apiKey); + return productService.addProduct(product); + } + + @GetMapping + public List getAllProducts(@RequestHeader("API-Key") String apiKey) { + validateApiKey(apiKey); + return productService.getAllProducts(); + } + + @GetMapping("/{name}") + public Product getProductByName(@RequestHeader("API-Key") String apiKey, @PathVariable String name) { + validateApiKey(apiKey); + Optional product = productService.getProductByName(name); + + if (product.isEmpty()) { + throw new RuntimeException("Product not found"); + } + return product.get(); + } + + @PutMapping("/{name}") + public Product updateProduct(@RequestHeader("API-Key") String apiKey, @PathVariable String name, @Valid @RequestBody Product product) { + validateApiKey(apiKey); + Product updated = productService.updateProduct(name, product); + + if (updated == null) { + throw new RuntimeException("Product not found"); + } + return updated; + } + + @DeleteMapping("/{name}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteProduct(@RequestHeader("API-Key") String apiKey, @PathVariable String name) { + validateApiKey(apiKey); + boolean deleted = productService.deleteProduct(name); + if (!deleted) { + throw new RuntimeException("Product not found"); + } + } + + @GetMapping("/category/{category}") + public List getProductsByCategory(@RequestHeader("API-Key") String apiKey, @PathVariable String category) { + validateApiKey(apiKey); + return productService.getProductsByCategory(category); + } + + @GetMapping("/price") + public List getProductsByPriceRange( + @RequestHeader("API-Key") String apiKey, + @RequestParam double min, + @RequestParam double max) { + + validateApiKey(apiKey); + + if (min < 0 || max < 0 || min > max) { + throw new RuntimeException("Invalid price range"); + } + + return productService.getProductsByPriceRange(min, max); + } +} \ No newline at end of file diff --git a/src/main/java/com/ironhack/models/Customer.java b/src/main/java/com/ironhack/models/Customer.java new file mode 100644 index 0000000..cc2d686 --- /dev/null +++ b/src/main/java/com/ironhack/models/Customer.java @@ -0,0 +1,47 @@ +package com.ironhack.models; + + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class Customer { + + @NotBlank(message = "Name cannot be blank") + private String name; + + @NotBlank(message = "Email is required") + @Email(message = "Must be a valid email fromat") + private String email; + + @Min(value = 18, message = "Age must be at least 18") + private int age; + + @NotBlank(message = "Adres cannot be blank") + 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 String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + + public int getAge() { return age; } + public void setAge(int age) { this.age = age; } + + public String getAddress() { return address; } + public void setAddress(String address) { this.address = address; } +} diff --git a/src/main/java/com/ironhack/models/Product.java b/src/main/java/com/ironhack/models/Product.java new file mode 100644 index 0000000..ca68b07 --- /dev/null +++ b/src/main/java/com/ironhack/models/Product.java @@ -0,0 +1,39 @@ +package com.ironhack.models; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; + +public class Product { + @NotBlank(message = "Name cannot be blank") + @Size(min = 3, message = "Name must be at least 3 caracters long") + private String name; + + @Positive(message = "Price must be a positive number") + private double price; + + @NotBlank(message = "Quantity must be a positive number") + private String category; + + @Positive(message = "Guality must be a positive number") + private int quantity; + + public Product(String name, double price, String category, int quantity){ + this.name = name; + this.price = price; + this.category = category; + this.quantity =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/services/ProductService.java b/src/main/java/com/ironhack/services/ProductService.java new file mode 100644 index 0000000..6547d5d --- /dev/null +++ b/src/main/java/com/ironhack/services/ProductService.java @@ -0,0 +1,58 @@ +package com.ironhack.services; + +import com.ironhack.models.Product; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +@Service +public class ProductService { + private final List products = new ArrayList<>(); + + public Product addProduct(Product product) { + products.add(product); + return product; + } + + public List getAllProducts(){ + return products; + } + + public OptionalgetProductByName (String name){ + return products.stream() + .filter(p -> p.getName().equalsIgnoreCase(name)) + .findFirst(); + } + public Product updateProduct(String name, Product updatedProduct) { + Optional existing = getProductByName(name); + + if (existing.isPresent()) { + Product p = existing.get(); + p.setPrice(updatedProduct.getPrice()); + p.setCategory(updatedProduct.getCategory()); + p.setQuantity(updatedProduct.getQuantity()); + return p; + } + return null; + } + + public boolean deleteProduct(String name) { + return products.removeIf(p -> p.getName().equalsIgnoreCase(name)); + } + + public List getProductsByCategory(String category) { + return products.stream() + .filter(p -> p.getCategory().equalsIgnoreCase(category)) + .collect(Collectors.toList()); + } + + public List getProductsByPriceRange(double min, double max) { + return products.stream() + .filter(p -> p.getPrice() >= min && p.getPrice() <= max) + .collect(Collectors.toList()); + } +}