Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target/
.idea/

*.iml
*.log
54 changes: 54 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>

<groupId>com.ironhack</groupId>
<artifactId>lab-java-springboot-rest-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lab-java-springboot-rest-api</name>
<description>LAB SpringBoot REST API - Ironhack</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
12 changes: 12 additions & 0 deletions src/main/java/com/ironhack/lab/LabRestApiApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ironhack.lab;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LabRestApiApplication {

public static void main(String[] args) {
SpringApplication.run(LabRestApiApplication.class, args);
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/ironhack/lab/controller/CustomerController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.ironhack.lab.controller;

import com.ironhack.lab.model.Customer;
import com.ironhack.lab.service.CustomerService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/customers")
public class CustomerController {

private final CustomerService customerService;

public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Customer createCustomer(@Valid @RequestBody Customer customer) {
return customerService.addCustomer(customer);
}

@GetMapping
public List<Customer> getAllCustomers() {
return customerService.getAllCustomers();
}

@GetMapping("/{email}")
public Customer getCustomerByEmail(@PathVariable String email) {
return customerService.getCustomerByEmail(email);
}

@PutMapping("/{email}")
public Customer updateCustomer(@PathVariable String email, @Valid @RequestBody Customer customer) {
return customerService.updateCustomer(email, customer);
}

@DeleteMapping("/{email}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCustomer(@PathVariable String email) {
customerService.deleteCustomer(email);
}
}
88 changes: 88 additions & 0 deletions src/main/java/com/ironhack/lab/controller/ProductController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.ironhack.lab.controller;

import com.ironhack.lab.exception.InvalidApiKeyException;
import com.ironhack.lab.model.Product;
import com.ironhack.lab.service.ProductService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/products")
public class ProductController {

private static final String VALID_API_KEY = "123456";
private final ProductService productService;

public ProductController(ProductService productService) {
this.productService = productService;
}

@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<Product> 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);
return productService.getProductByName(name);
}

@PutMapping("/{name}")
public Product updateProduct(
@RequestHeader("API-Key") String apiKey,
@PathVariable String name,
@Valid @RequestBody Product product) {
validateApiKey(apiKey);
return productService.updateProduct(name, product);
}

@DeleteMapping("/{name}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteProduct(@RequestHeader("API-Key") String apiKey, @PathVariable String name) {
validateApiKey(apiKey);
productService.deleteProduct(name);
}

@GetMapping("/category/{category}")
public List<Product> getProductsByCategory(@RequestHeader("API-Key") String apiKey, @PathVariable String category) {
validateApiKey(apiKey);
return productService.getProductsByCategory(category);
}

@GetMapping("/price")
public List<Product> getProductsByPriceRange(
@RequestHeader("API-Key") String apiKey,
@RequestParam double min,
@RequestParam double max) {
validateApiKey(apiKey);
return productService.getProductsByPriceRange(min, max);
}

private void validateApiKey(String apiKey) {
if (!VALID_API_KEY.equals(apiKey)) {
throw new InvalidApiKeyException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ironhack.lab.exception;

public class CustomerNotFoundException extends RuntimeException {

public CustomerNotFoundException(String email) {
super("Customer not found: " + email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ironhack.lab.exception;

public class InvalidApiKeyException extends RuntimeException {

public InvalidApiKeyException() {
super("Invalid API-Key header");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ironhack.lab.exception;

public class InvalidPriceRangeException extends RuntimeException {

public InvalidPriceRangeException() {
super("Invalid price range: min must be less than or equal to max");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ironhack.lab.exception;

public class ProductNotFoundException extends RuntimeException {

public ProductNotFoundException(String name) {
super("Product not found: " + name);
}
}
68 changes: 68 additions & 0 deletions src/main/java/com/ironhack/lab/handler/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.ironhack.lab.handler;

import com.ironhack.lab.exception.CustomerNotFoundException;
import com.ironhack.lab.exception.InvalidApiKeyException;
import com.ironhack.lab.exception.InvalidPriceRangeException;
import com.ironhack.lab.exception.ProductNotFoundException;
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 java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationErrors(MethodArgumentNotValidException exception) {
Map<String, String> validationErrors = new HashMap<>();

exception.getBindingResult().getFieldErrors().forEach(error ->
validationErrors.put(error.getField(), error.getDefaultMessage()));

return buildErrorResponse(HttpStatus.BAD_REQUEST, "Validation failed", validationErrors);
}

@ExceptionHandler(MissingRequestHeaderException.class)
public ResponseEntity<Map<String, Object>> handleMissingHeader(MissingRequestHeaderException exception) {
return buildErrorResponse(
HttpStatus.UNAUTHORIZED,
"Missing required header: " + exception.getHeaderName(),
null);
}

@ExceptionHandler(InvalidApiKeyException.class)
public ResponseEntity<Map<String, Object>> handleInvalidApiKey(InvalidApiKeyException exception) {
return buildErrorResponse(HttpStatus.UNAUTHORIZED, exception.getMessage(), null);
}

@ExceptionHandler({ProductNotFoundException.class, CustomerNotFoundException.class})
public ResponseEntity<Map<String, Object>> handleNotFound(RuntimeException exception) {
return buildErrorResponse(HttpStatus.NOT_FOUND, exception.getMessage(), null);
}

@ExceptionHandler(InvalidPriceRangeException.class)
public ResponseEntity<Map<String, Object>> handleInvalidPriceRange(InvalidPriceRangeException exception) {
return buildErrorResponse(HttpStatus.BAD_REQUEST, exception.getMessage(), null);
}

private ResponseEntity<Map<String, Object>> buildErrorResponse(
HttpStatus status,
String message,
Map<String, String> validationErrors) {
Map<String, Object> response = new HashMap<>();
response.put("status", status.value());
response.put("error", status.getReasonPhrase());
response.put("message", message);

if (validationErrors != null) {
response.put("validationErrors", validationErrors);
}

return ResponseEntity.status(status).body(response);
}
}
63 changes: 63 additions & 0 deletions src/main/java/com/ironhack/lab/model/Customer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.ironhack.lab.model;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

public class Customer {

@NotBlank(message = "Customer name is required")
private String name;

@Email(message = "Email must be valid")
@NotBlank(message = "Email is required")
private String email;

@Min(value = 18, message = "Customer must be at least 18 years old")
private int age;

@NotBlank(message = "Address is required")
private String address;

public Customer() {
}

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;
}
}
Loading