From 615c8a0fc8d2b35da7df90115456d33444104c41 Mon Sep 17 00:00:00 2001 From: Kevin J Hoerr Date: Thu, 27 Jan 2022 14:12:29 -0500 Subject: Add error handling --- .../submelon/pantry/CustomControllerAdvice.java | 46 +++++++++++++ .../java/dev/submelon/pantry/ErrorResponse.java | 58 +++++++++++++++++ src/main/java/dev/submelon/pantry/Item.java | 75 ---------------------- .../java/dev/submelon/pantry/ItemController.java | 43 ------------- .../java/dev/submelon/pantry/ItemRepository.java | 7 -- src/main/java/dev/submelon/pantry/PantryItem.java | 40 ++++++++++++ .../dev/submelon/pantry/PantryItemController.java | 43 +++++++++++++ .../dev/submelon/pantry/PantryItemRepository.java | 9 +++ .../dev/submelon/pantry/ItemRepositoryTests.java | 14 ++-- 9 files changed, 203 insertions(+), 132 deletions(-) create mode 100644 src/main/java/dev/submelon/pantry/CustomControllerAdvice.java create mode 100644 src/main/java/dev/submelon/pantry/ErrorResponse.java delete mode 100644 src/main/java/dev/submelon/pantry/Item.java delete mode 100644 src/main/java/dev/submelon/pantry/ItemController.java delete mode 100644 src/main/java/dev/submelon/pantry/ItemRepository.java create mode 100644 src/main/java/dev/submelon/pantry/PantryItem.java create mode 100644 src/main/java/dev/submelon/pantry/PantryItemController.java create mode 100644 src/main/java/dev/submelon/pantry/PantryItemRepository.java diff --git a/src/main/java/dev/submelon/pantry/CustomControllerAdvice.java b/src/main/java/dev/submelon/pantry/CustomControllerAdvice.java new file mode 100644 index 0000000..dad961b --- /dev/null +++ b/src/main/java/dev/submelon/pantry/CustomControllerAdvice.java @@ -0,0 +1,46 @@ +package dev.submelon.pantry; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.NoSuchElementException; + +@ControllerAdvice +class CustomControllerAdvice { + @ExceptionHandler(NoSuchElementException.class) + public ResponseEntity handleNullPointerExceptions( + NoSuchElementException e + ) { + HttpStatus status = HttpStatus.NOT_FOUND; + + return new ResponseEntity<>( + new ErrorResponse(status, e.getMessage()), + status + ); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleExceptions( + Exception e + ) { + HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; + + // converting the stack trace to String + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + e.printStackTrace(printWriter); + String stackTrace = stringWriter.toString(); + + return new ResponseEntity<>( + new ErrorResponse( + status, + e.getMessage(), + stackTrace // specifying the stack trace in case of 500s + ), + status + ); + } +} diff --git a/src/main/java/dev/submelon/pantry/ErrorResponse.java b/src/main/java/dev/submelon/pantry/ErrorResponse.java new file mode 100644 index 0000000..c5b1dbe --- /dev/null +++ b/src/main/java/dev/submelon/pantry/ErrorResponse.java @@ -0,0 +1,58 @@ +package dev.submelon.pantry; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; +import org.springframework.http.HttpStatus; +import java.util.Date; + +@Getter +@Setter +public class ErrorResponse { + // customizing timestamp serialization format + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss") + private Date timestamp; + + private int code; + + private String status; + + private String message; + + private String stackTrace; + + private Object data; + + public ErrorResponse() { + timestamp = new Date(); + } + + public ErrorResponse(HttpStatus httpStatus, String message) { + this(); + + this.code = httpStatus.value(); + this.status = httpStatus.name(); + this.message = message; + } + + public ErrorResponse( + HttpStatus httpStatus, + String message, + String stackTrace + ) { + this(httpStatus, message); + + this.stackTrace = stackTrace; + } + + public ErrorResponse( + HttpStatus httpStatus, + String message, + String stackTrace, + Object data + ) { + this(httpStatus, message, stackTrace); + + this.data = data; + } +} diff --git a/src/main/java/dev/submelon/pantry/Item.java b/src/main/java/dev/submelon/pantry/Item.java deleted file mode 100644 index a17f5a1..0000000 --- a/src/main/java/dev/submelon/pantry/Item.java +++ /dev/null @@ -1,75 +0,0 @@ -package dev.submelon.pantry; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -@Entity -public class Item { - @Id - @GeneratedValue(strategy=GenerationType.AUTO) - private Integer id; - - @Column(nullable=false) - private String name; - - private String description; - - @Column(unique=true, nullable=false) - private String shortid; - - @Column(nullable=false) - private double quantity; - - public Item() { - } - - public Item(String name, String description, String shortid, double quantity) { - this.name = name; - this.description = description; - this.shortid = shortid; - this.quantity = quantity; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public String getShortid() { - return shortid; - } - - public void setShortid(String shortid) { - this.shortid = shortid; - } - - public double getQuantity() { - return quantity; - } - - public void setQuantity(double quantity) { - this.quantity = quantity; - } -} diff --git a/src/main/java/dev/submelon/pantry/ItemController.java b/src/main/java/dev/submelon/pantry/ItemController.java deleted file mode 100644 index 7021f0a..0000000 --- a/src/main/java/dev/submelon/pantry/ItemController.java +++ /dev/null @@ -1,43 +0,0 @@ -package dev.submelon.pantry; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -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.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; - -@Controller -@RequestMapping(path="/items") -public class ItemController { - @Autowired - private ItemRepository itemRepository; - - @PostMapping(path="/add") - @ResponseBody - Integer addNewItem(@RequestParam String name, @RequestParam String description, @RequestParam String shortid, @RequestParam double quantity) { - Item item = new Item(); - item.setName(name); - item.setDescription(description); - item.setShortid(shortid); - item.setQuantity(quantity); - - Item updatedItem = itemRepository.save(item); - return updatedItem.getId(); - } - - @GetMapping(path="") - @ResponseBody - Iterable getAllItems() { - return itemRepository.findAll(); - } - - @GetMapping(path="/{shortid}") - @ResponseBody - Item getByShortid(@PathVariable String shortid) { - return itemRepository.findByShortid(shortid); - } - -} diff --git a/src/main/java/dev/submelon/pantry/ItemRepository.java b/src/main/java/dev/submelon/pantry/ItemRepository.java deleted file mode 100644 index 01451e3..0000000 --- a/src/main/java/dev/submelon/pantry/ItemRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.submelon.pantry; - -import org.springframework.data.repository.CrudRepository; - -public interface ItemRepository extends CrudRepository { - Item findByShortid(String shortid); -} diff --git a/src/main/java/dev/submelon/pantry/PantryItem.java b/src/main/java/dev/submelon/pantry/PantryItem.java new file mode 100644 index 0000000..36850bf --- /dev/null +++ b/src/main/java/dev/submelon/pantry/PantryItem.java @@ -0,0 +1,40 @@ +package dev.submelon.pantry; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@NoArgsConstructor +public class PantryItem { + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private Integer id; + + @Column(nullable=false) + private String name; + + private String description; + + @Column(unique=true, nullable=false) + private String shortid; + + @Column(nullable=false) + private double quantity; + + public PantryItem(String name, String description, String shortid, double quantity) { + this.name = name; + this.description = description; + this.shortid = shortid; + this.quantity = quantity; + } + +} diff --git a/src/main/java/dev/submelon/pantry/PantryItemController.java b/src/main/java/dev/submelon/pantry/PantryItemController.java new file mode 100644 index 0000000..37a71e9 --- /dev/null +++ b/src/main/java/dev/submelon/pantry/PantryItemController.java @@ -0,0 +1,43 @@ +package dev.submelon.pantry; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping(path="/items") +public class PantryItemController { + @Autowired + private PantryItemRepository itemRepository; + + @PostMapping(path="/add") + @ResponseBody + Integer addNewItem(@RequestParam String name, @RequestParam String description, @RequestParam String shortid, @RequestParam double quantity) { + PantryItem item = new PantryItem(); + item.setName(name); + item.setDescription(description); + item.setShortid(shortid); + item.setQuantity(quantity); + + PantryItem updatedItem = itemRepository.save(item); + return updatedItem.getId(); + } + + @GetMapping(path="") + @ResponseBody + Iterable getAllItems() { + return itemRepository.findAll(); + } + + @GetMapping(path="/{shortid}") + @ResponseBody + PantryItem getByShortid(@PathVariable String shortid) { + return itemRepository.findByShortid(shortid).get(); + } + +} diff --git a/src/main/java/dev/submelon/pantry/PantryItemRepository.java b/src/main/java/dev/submelon/pantry/PantryItemRepository.java new file mode 100644 index 0000000..7808186 --- /dev/null +++ b/src/main/java/dev/submelon/pantry/PantryItemRepository.java @@ -0,0 +1,9 @@ +package dev.submelon.pantry; + +import java.util.Optional; + +import org.springframework.data.repository.CrudRepository; + +public interface PantryItemRepository extends CrudRepository { + Optional findByShortid(String shortid); +} diff --git a/src/test/java/dev/submelon/pantry/ItemRepositoryTests.java b/src/test/java/dev/submelon/pantry/ItemRepositoryTests.java index 0989198..f2f16ce 100644 --- a/src/test/java/dev/submelon/pantry/ItemRepositoryTests.java +++ b/src/test/java/dev/submelon/pantry/ItemRepositoryTests.java @@ -15,13 +15,13 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class ItemRepositoryTests { @Autowired - private ItemRepository itemRepository; + private PantryItemRepository itemRepository; @BeforeAll public void before() throws Exception { - Item pb = new Item("Peanut Butter", "Crunchy", "crunchy-pb", 14.0); - Item jelly = new Item("Strawberry Preserves", "The best", "sb-preserves", 12.8); - Item bread = new Item("Oatnut Bread", "Relatively healthy, right?", "oatnut-bread", 10); + PantryItem pb = new PantryItem("Peanut Butter", "Crunchy", "crunchy-pb", 14.0); + PantryItem jelly = new PantryItem("Strawberry Preserves", "The best", "sb-preserves", 12.8); + PantryItem bread = new PantryItem("Oatnut Bread", "Relatively healthy, right?", "oatnut-bread", 10); assertNull(pb.getId()); assertNull(jelly.getId()); assertNull(bread.getId()); @@ -35,13 +35,13 @@ public class ItemRepositoryTests { @Test public void testFetchData() { - Item pb = itemRepository.findByShortid("crunchy-pb"); + PantryItem pb = itemRepository.findByShortid("crunchy-pb").get(); assertNotNull(pb); assertEquals(14.0, pb.getQuantity()); - Iterable items = itemRepository.findAll(); + Iterable items = itemRepository.findAll(); int count = 0; - for (Item item : items) { + for (PantryItem item : items) { assertNotNull(item); count++; } -- cgit