diff --git a/rest_api/src/main/java/org/ironhack/rest_api/controller/CustomerController.java b/rest_api/src/main/java/org/ironhack/rest_api/controller/CustomerController.java new file mode 100644 index 0000000..730099c --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/controller/CustomerController.java @@ -0,0 +1,49 @@ +package org.ironhack.rest_api.controller; + +import jakarta.validation.Valid; +import org.ironhack.rest_api.model.Customer; +import org.ironhack.rest_api.service.CustomerService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/customers") +public class CustomerController { + private final CustomerService customerService; + + public CustomerController(CustomerService customerService) { + this.customerService = customerService; + } + + @GetMapping + public List getCustomers() { + return customerService.getCustomers(); + } + + @GetMapping("/{email}") + public Customer getCustomer(@PathVariable String email) { + return customerService.getCustomerByEmail(email); + } + + @PostMapping + public ResponseEntity createCustomer(@Valid @RequestBody Customer customer) { + Customer created = customerService.addCustomer(customer.getName(), customer.getEmail(), customer.getAge(), customer.getAddress()); + return ResponseEntity.status(HttpStatus.CREATED).body(created); + } + + @PutMapping("{email}") + public ResponseEntity updateCustomer(@PathVariable String email, @Valid @RequestBody Customer customer) { + Customer customer1=customerService.updateCustomer(customer.getName(), customer.getEmail(), customer.getAge(), customer.getAddress()); + return ResponseEntity.ok(customer1); + } + + @DeleteMapping("{email}") + public ResponseEntity deleteCustomer(@PathVariable String email) { + customerService.deleteCustomerByEmail(email); + return ResponseEntity.noContent().build(); + } + +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/controller/ProductController.java b/rest_api/src/main/java/org/ironhack/rest_api/controller/ProductController.java new file mode 100644 index 0000000..e83a280 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/controller/ProductController.java @@ -0,0 +1,81 @@ +package org.ironhack.rest_api.controller; + +import jakarta.validation.Valid; +import org.ironhack.rest_api.exception.MissingKeyHeaderException; +import org.ironhack.rest_api.model.Customer; +import org.ironhack.rest_api.model.Product; +import org.ironhack.rest_api.service.ProductService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("products") +public class ProductController { + + private final ProductService productService; + private final static String API_KEY = "123456789"; + + public ProductController(ProductService productService) { + this.productService = productService; + } + + public void checkApiKey(String apiKey) { + if (apiKey == null) { + throw new MissingKeyHeaderException("Missing API Key"); + } + if (!apiKey.matches(API_KEY)) { + throw new MissingKeyHeaderException("Invalid API Key"); + } + } + + @GetMapping + public List getProducts(@RequestHeader String apiKey) { + checkApiKey(apiKey); + return productService.findAll(); + } + + @PostMapping + public ResponseEntity addProduct(@RequestHeader String apiKey, @Valid @RequestBody Product product) { + checkApiKey(apiKey); + Product created = productService.save(product.getName(), product.getPrice(), product.getCategory(), product.getQuantity()); + return ResponseEntity.status(HttpStatus.CREATED).body(created); + } + + @GetMapping("/category/{category}") + public List getProductsByCategory(@RequestHeader String apiKey, @PathVariable String category) { + checkApiKey(apiKey); + return productService.findByCategory(category); + } + + @GetMapping("/{name}") + public Product getProductsByName(@RequestHeader String apiKey, @PathVariable String name) { + checkApiKey(apiKey); + return productService.findByName(name); + } + + @GetMapping("/price") + public List getProductsByPriceRange(@RequestHeader String apiKey, + @RequestParam(required = false) double lower, + @RequestParam(required = false) double higher) { + checkApiKey(apiKey); + return productService.findByPriceRange(lower, higher); + } + @PutMapping("/{name}") + public ResponseEntity updateProduct(@RequestHeader String apiKey, @PathVariable String name, @Valid @RequestBody Product product) { + checkApiKey(apiKey); + Product product1 = productService.update(name,product.getPrice(),product.getCategory(),product.getQuantity()); + return ResponseEntity.ok(product1); + } + + @DeleteMapping("/{name}") + public void deleteProduct(@RequestHeader String apiKey, @PathVariable String name) { + checkApiKey(apiKey); + productService.delete(name); + + } + + +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/exception/CustomerNotFound.java b/rest_api/src/main/java/org/ironhack/rest_api/exception/CustomerNotFound.java new file mode 100644 index 0000000..b57ea0e --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/exception/CustomerNotFound.java @@ -0,0 +1,7 @@ +package org.ironhack.rest_api.exception; + +public class CustomerNotFound extends RuntimeException { + public CustomerNotFound(String message) { + super(message); + } +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/exception/GlobalExceptionHandler.java b/rest_api/src/main/java/org/ironhack/rest_api/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..0877352 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/exception/GlobalExceptionHandler.java @@ -0,0 +1,49 @@ +package org.ironhack.rest_api.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map handleValidation(MethodArgumentNotValidException ex) { + + Map errors = new HashMap<>(); + + ex.getBindingResult().getFieldErrors() + .forEach(e -> errors.put(e.getField(), e.getDefaultMessage())); + + return errors; + } + + @ExceptionHandler(MissingKeyHeaderException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public Map handleMissingApiKey(MissingKeyHeaderException ex) { + Map error = new HashMap<>(); + error.put("error", ex.getMessage()); + return error; + } + + @ExceptionHandler(ProductNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public Map handleProductNotFound(ProductNotFoundException ex) { + Map error = new HashMap<>(); + error.put("error", ex.getMessage()); + return error; + } + + @ExceptionHandler(LowerBoundException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map handleInvalidPriceRange(LowerBoundException ex) { + Map error = new HashMap<>(); + error.put("error", ex.getMessage()); + return error; + } +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/exception/LowerBoundException.java b/rest_api/src/main/java/org/ironhack/rest_api/exception/LowerBoundException.java new file mode 100644 index 0000000..87b46b2 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/exception/LowerBoundException.java @@ -0,0 +1,7 @@ +package org.ironhack.rest_api.exception; + +public class LowerBoundException extends RuntimeException { + public LowerBoundException(String message) { + super(message); + } +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/exception/MissingKeyHeaderException.java b/rest_api/src/main/java/org/ironhack/rest_api/exception/MissingKeyHeaderException.java new file mode 100644 index 0000000..5ae6ac8 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/exception/MissingKeyHeaderException.java @@ -0,0 +1,7 @@ +package org.ironhack.rest_api.exception; + +public class MissingKeyHeaderException extends RuntimeException { + public MissingKeyHeaderException(String message) { + super(message); + } +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/exception/ProductNotFoundException.java b/rest_api/src/main/java/org/ironhack/rest_api/exception/ProductNotFoundException.java new file mode 100644 index 0000000..46e6fd9 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/exception/ProductNotFoundException.java @@ -0,0 +1,7 @@ +package org.ironhack.rest_api.exception; + +public class ProductNotFoundException extends RuntimeException { + public ProductNotFoundException(String message) { + super(message); + } +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/model/Customer.java b/rest_api/src/main/java/org/ironhack/rest_api/model/Customer.java new file mode 100644 index 0000000..dae4e1e --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/model/Customer.java @@ -0,0 +1,58 @@ +package org.ironhack.rest_api.model; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class Customer { + + @NotBlank(message = "Name is required") + private String name; + + @Email(message = "That must be in email format") + private String email; + @Min(value = 18, message = "Age must be 18 or greater than 18") + private int age; + + @NotBlank(message = "Address is required") + 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/rest_api/src/main/java/org/ironhack/rest_api/model/Product.java b/rest_api/src/main/java/org/ironhack/rest_api/model/Product.java new file mode 100644 index 0000000..37524f7 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/model/Product.java @@ -0,0 +1,66 @@ +package org.ironhack.rest_api.model; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; + +public class Product { + + + @NotBlank(message = "Name is required") + @Size(min = 3, message = "Name length must be consist of min 3 elements") + private String name; + + @Positive(message = "Price must be greater than 0") + private Double price; + + @NotBlank(message = "Category is required") + private String category; + + @Positive(message = "Quantity must be greater than 0") + 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) { + if (price < 0) + return; + 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) { + if (quantity < 0) + return; + this.quantity = quantity; + } +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/service/CustomerService.java b/rest_api/src/main/java/org/ironhack/rest_api/service/CustomerService.java new file mode 100644 index 0000000..7f7aa2c --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/service/CustomerService.java @@ -0,0 +1,48 @@ +package org.ironhack.rest_api.service; + +import org.ironhack.rest_api.exception.CustomerNotFound; +import org.ironhack.rest_api.model.Customer; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@Service +public class CustomerService { + + private final HashMap customers = new HashMap<>(); + + public List getCustomers() { + return new ArrayList<>(customers.values()); + } + + public Customer addCustomer(String name, String email, int age, String address) { + Customer customer = new Customer(name, email, age, address); + customers.put(email, customer); + return customer; + } + + public Customer getCustomerByEmail(String email) { + if (customers.containsKey(email)) { + return customers.get(email); + } + throw new CustomerNotFound("Customer with email " + email + " not found"); + } + + public Customer updateCustomer(String name, String email, int age, String address) { + Customer customer = getCustomerByEmail(email); + customer.setName(name); + customer.setAge(age); + customer.setAddress(address); + customers.put(email, customer); + return customer; + + } + public void deleteCustomerByEmail(String email) { + getCustomerByEmail(email); + customers.remove(email); + } + + +} diff --git a/rest_api/src/main/java/org/ironhack/rest_api/service/ProductService.java b/rest_api/src/main/java/org/ironhack/rest_api/service/ProductService.java new file mode 100644 index 0000000..9923324 --- /dev/null +++ b/rest_api/src/main/java/org/ironhack/rest_api/service/ProductService.java @@ -0,0 +1,76 @@ +package org.ironhack.rest_api.service; + +import org.ironhack.rest_api.exception.LowerBoundException; +import org.ironhack.rest_api.exception.ProductNotFoundException; +import org.ironhack.rest_api.model.Product; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class ProductService { + + private final Map products = new HashMap<>(); + + public ProductService() { + } + + public List findAll() { + return new ArrayList(products.values()); + + } + + public Product save(String name, Double price, String category, int quantity) { + Product product = new Product(name, price, category, quantity); + products.put(name, product); + return product; + + } + + public List findByCategory(String category) { + List productList = new ArrayList<>(); + for (Product p : products.values()) { + if (p.getCategory().equalsIgnoreCase(category)) { + productList.add(p); + } + } + return productList; + } + + public List findByPriceRange(double lower, double upper) { + if (lower > upper) { + throw new LowerBoundException("Lower bound is greater than upper bound"); + } + return products.values() + .stream() + .filter(product -> product.getPrice() >= lower && product.getPrice() <= upper) + .collect(Collectors.toList()); + + } + + public Product findByName(String name) { + if (products.containsKey(name)) { + return products.get(name); + } + throw new ProductNotFoundException("Product not found"); + } + + public Product update(String name, double price, String category, int quantity) { + Product oldProduct = findByName(name); + oldProduct.setPrice(price); + oldProduct.setCategory(category); + oldProduct.setQuantity(quantity); + products.put(name, oldProduct); + return oldProduct; + } + + public void delete(String name) { + findByName(name); + products.remove(name); + } + +}