From 0d5848b9e83c1298b8e7aec3554e9a50383e33ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 11:35:28 +0100 Subject: [PATCH 01/11] Create and initialize classes, implement getAll --- build.gradle | 4 +- src/main/java/com/booleanuk/api/Main.java | 11 ++++ .../com/booleanuk/api/products/Product.java | 27 ++++++++++ .../api/products/ProductController.java | 51 +++++++++++++++++++ .../api/products/ProductRepository.java | 31 +++++++++++ 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/booleanuk/api/Main.java create mode 100644 src/main/java/com/booleanuk/api/products/Product.java create mode 100644 src/main/java/com/booleanuk/api/products/ProductController.java create mode 100644 src/main/java/com/booleanuk/api/products/ProductRepository.java diff --git a/build.gradle b/build.gradle index d1f7789..c66e483 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.3.1' - id 'io.spring.dependency-management' version '1.1.5' + id 'org.springframework.boot' version '3.4.1' + id 'io.spring.dependency-management' version '1.1.7' } group = 'com.example' diff --git a/src/main/java/com/booleanuk/api/Main.java b/src/main/java/com/booleanuk/api/Main.java new file mode 100644 index 0000000..4e437d6 --- /dev/null +++ b/src/main/java/com/booleanuk/api/Main.java @@ -0,0 +1,11 @@ +package com.booleanuk.api; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Main { + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/booleanuk/api/products/Product.java b/src/main/java/com/booleanuk/api/products/Product.java new file mode 100644 index 0000000..47958a6 --- /dev/null +++ b/src/main/java/com/booleanuk/api/products/Product.java @@ -0,0 +1,27 @@ +package com.booleanuk.api.products; + +public class Product { + private int id; + private String name; + private String category; + private int price; + + public Product(int id, String name, String category, int price) { + this.id = id; + this.name = name; + this.category = category; + this.price = price; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public int getPrice() { + return price; + } +} diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java new file mode 100644 index 0000000..36b6d29 --- /dev/null +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -0,0 +1,51 @@ +package com.booleanuk.api.products; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("products") +public class ProductController { + ProductRepository repository; + + public ProductController(ProductRepository repository) { + this.repository = repository; + } + + public ProductController() { + this.repository = new ProductRepository(); + } + + @PostMapping + @ResponseStatus + public Product create(@RequestBody Product product) { + return null; + } + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public List getAll() { + return this.repository.findAll(); + } + + @GetMapping("/{id}") + @ResponseStatus() + public Product getOne(@PathVariable(name="id") int id, @RequestBody Product product) { + return null; + } + + @PutMapping("/{id}") + @ResponseStatus() + public Product update(@PathVariable(name="id") int id, @RequestBody Product product) { + return null; + } + + @DeleteMapping("/{id}") + @ResponseStatus() + public Product delete(@PathVariable(name="id") int id) { + return null; + } + +} diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java new file mode 100644 index 0000000..807964b --- /dev/null +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -0,0 +1,31 @@ +package com.booleanuk.api.products; + +import java.util.ArrayList; +import java.util.List; + +public class ProductRepository { + private int idCounter = 1; + private List data; + + public ProductRepository() { + data = new ArrayList<>(); + create("Bagel", "Food", 12); + create("Apple", "Food", 7); + } + + public void create(String name, String category, int price) { + Product product = new Product(this.idCounter++, name, category, price); + this.data.add(product); + } + + public List findAll() { + return this.data; + } + + public Product find(int id) { + return this.data.stream() + .filter(product -> product.getId() == id) + .findFirst() + .orElseThrow(); + } +} From a94b163afd09c9073caecc042a4840141a0817d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 11:47:05 +0100 Subject: [PATCH 02/11] Implement creating products --- src/main/java/com/booleanuk/api/products/Product.java | 6 ++++++ .../java/com/booleanuk/api/products/ProductController.java | 2 +- .../java/com/booleanuk/api/products/ProductRepository.java | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/Product.java b/src/main/java/com/booleanuk/api/products/Product.java index 47958a6..8398ef9 100644 --- a/src/main/java/com/booleanuk/api/products/Product.java +++ b/src/main/java/com/booleanuk/api/products/Product.java @@ -13,6 +13,8 @@ public Product(int id, String name, String category, int price) { this.price = price; } + + public int getId() { return id; } @@ -21,6 +23,10 @@ public String getName() { return name; } + public String getCategory() { + return category; + } + public int getPrice() { return price; } diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index 36b6d29..840f9ca 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -21,7 +21,7 @@ public ProductController() { @PostMapping @ResponseStatus public Product create(@RequestBody Product product) { - return null; + return repository.create(product); } @GetMapping diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 807964b..9613e19 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -13,9 +13,14 @@ public ProductRepository() { create("Apple", "Food", 7); } - public void create(String name, String category, int price) { + public Product create(String name, String category, int price) { Product product = new Product(this.idCounter++, name, category, price); this.data.add(product); + return product; + } + + public Product create(Product product) { + return this.create(product.getName(), product.getCategory(), product.getPrice()); } public List findAll() { From 2d5b496e1f90fa66bbe0d0d469302584ecc101fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 12:03:11 +0100 Subject: [PATCH 03/11] Implement finding a product --- .../java/com/booleanuk/api/products/ProductController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index 840f9ca..e2e93bf 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -6,7 +6,7 @@ import java.util.List; @RestController -@RequestMapping("products") +@RequestMapping("/products") public class ProductController { ProductRepository repository; @@ -32,8 +32,8 @@ public List getAll() { @GetMapping("/{id}") @ResponseStatus() - public Product getOne(@PathVariable(name="id") int id, @RequestBody Product product) { - return null; + public Product getOne(@PathVariable(name="id") int id) { + return repository.find(id); } @PutMapping("/{id}") From 354c49020e9bf12cb8afda5ede5ee5b404a29c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 12:08:02 +0100 Subject: [PATCH 04/11] Implement updating a product --- .../java/com/booleanuk/api/products/Product.java | 14 ++++++++++++-- .../booleanuk/api/products/ProductController.java | 2 +- .../booleanuk/api/products/ProductRepository.java | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/Product.java b/src/main/java/com/booleanuk/api/products/Product.java index 8398ef9..df095bb 100644 --- a/src/main/java/com/booleanuk/api/products/Product.java +++ b/src/main/java/com/booleanuk/api/products/Product.java @@ -13,8 +13,6 @@ public Product(int id, String name, String category, int price) { this.price = price; } - - public int getId() { return id; } @@ -30,4 +28,16 @@ public String getCategory() { public int getPrice() { return price; } + + public void setName(String name) { + this.name = name; + } + + public void setCategory(String category) { + this.category = category; + } + + public void setPrice(int price) { + this.price = price; + } } diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index e2e93bf..11350c6 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -39,7 +39,7 @@ public Product getOne(@PathVariable(name="id") int id) { @PutMapping("/{id}") @ResponseStatus() public Product update(@PathVariable(name="id") int id, @RequestBody Product product) { - return null; + return repository.update(id, product); } @DeleteMapping("/{id}") diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 9613e19..0af143c 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -33,4 +33,12 @@ public Product find(int id) { .findFirst() .orElseThrow(); } + + public Product update(int id, Product product) { + Product productToUpdate = find(id); + productToUpdate.setName(product.getName()); + productToUpdate.setCategory(product.getCategory()); + productToUpdate.setPrice(product.getPrice()); + return productToUpdate; + } } From 755a79ee8e8705dce5270e1a16a41732afb5688b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 12:10:19 +0100 Subject: [PATCH 05/11] Implement deleting a product --- .../java/com/booleanuk/api/products/ProductController.java | 3 +-- .../java/com/booleanuk/api/products/ProductRepository.java | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index 11350c6..2dc5da9 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -45,7 +45,6 @@ public Product update(@PathVariable(name="id") int id, @RequestBody Product prod @DeleteMapping("/{id}") @ResponseStatus() public Product delete(@PathVariable(name="id") int id) { - return null; + return repository.delete(id); } - } diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 0af143c..479a97b 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -41,4 +41,10 @@ public Product update(int id, Product product) { productToUpdate.setPrice(product.getPrice()); return productToUpdate; } + + public Product delete(int id) { + Product productToDelete = find(id); + data.remove(productToDelete); + return productToDelete; + } } From 03697fc5b1b59ec770dbe3b1a8fdab5b6ac65018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 13:15:28 +0100 Subject: [PATCH 06/11] Return HTTP error when trying to get product with invalid ID --- .../com/booleanuk/api/products/ProductController.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index 2dc5da9..da63e98 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -2,8 +2,10 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; import java.util.List; +import java.util.NoSuchElementException; @RestController @RequestMapping("/products") @@ -33,7 +35,11 @@ public List getAll() { @GetMapping("/{id}") @ResponseStatus() public Product getOne(@PathVariable(name="id") int id) { - return repository.find(id); + try { + return repository.find(id); + } catch (NoSuchElementException e) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found"); + } } @PutMapping("/{id}") From c7b306688bfa05f730587816b01e93bfc7b1bb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 13:29:32 +0100 Subject: [PATCH 07/11] Return HTTP error when trying to add product with already existing name --- .../com/booleanuk/api/products/ProductController.java | 8 ++++++-- .../com/booleanuk/api/products/ProductRepository.java | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index da63e98..075aeb2 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -23,7 +23,11 @@ public ProductController() { @PostMapping @ResponseStatus public Product create(@RequestBody Product product) { - return repository.create(product); + Product p = repository.create(product); + if (p == null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Product with provided name already exists."); + } + return p; } @GetMapping @@ -38,7 +42,7 @@ public Product getOne(@PathVariable(name="id") int id) { try { return repository.find(id); } catch (NoSuchElementException e) { - throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found"); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found."); } } diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 479a97b..3b6184c 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -20,7 +20,12 @@ public Product create(String name, String category, int price) { } public Product create(Product product) { - return this.create(product.getName(), product.getCategory(), product.getPrice()); + if (this.data.stream() + .filter(p -> p.getName().equals(product.getName())) + .toList().isEmpty()) { + return this.create(product.getName(), product.getCategory(), product.getPrice()); + } + return null; } public List findAll() { From 800847e70c22fca19bdb158de0f44509b496d554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 14:04:41 +0100 Subject: [PATCH 08/11] Implement filter by category --- .../com/booleanuk/api/products/ProductController.java | 11 +++++++++-- .../com/booleanuk/api/products/ProductRepository.java | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index 075aeb2..d4874a6 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -32,8 +32,15 @@ public Product create(@RequestBody Product product) { @GetMapping @ResponseStatus(HttpStatus.OK) - public List getAll() { - return this.repository.findAll(); + public List getAll(@RequestParam(name="category", required=false) String category) { + List products; + if (category != null) + products = this.repository.findAll(category); + else + products = this.repository.findAll(); + if (products.isEmpty()) + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No products of the provided category were found."); + return products; } @GetMapping("/{id}") diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 3b6184c..96793a9 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -28,6 +28,12 @@ public Product create(Product product) { return null; } + public List findAll(String category) { + return this.data.stream() + .filter(p -> p.getCategory().equals(category)) + .toList(); + } + public List findAll() { return this.data; } From b4b8e9cfb0a1715aa8d8314efd82f8c05831fc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 14:27:35 +0100 Subject: [PATCH 09/11] Throw exceptions when updating with invalid parameters --- .../api/products/ProductController.java | 11 +++++++++- .../api/products/ProductRepository.java | 20 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index d4874a6..70d6c3e 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -56,7 +56,16 @@ public Product getOne(@PathVariable(name="id") int id) { @PutMapping("/{id}") @ResponseStatus() public Product update(@PathVariable(name="id") int id, @RequestBody Product product) { - return repository.update(id, product); + Product updatedProduct; + try { + updatedProduct = repository.update(id, product); + } catch (NoSuchElementException e) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found."); + } + if (updatedProduct == null) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Product with provided name already exists."); + } + return updatedProduct; } @DeleteMapping("/{id}") diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 96793a9..58b6ef5 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.NoSuchElementException; public class ProductRepository { private int idCounter = 1; @@ -20,9 +21,7 @@ public Product create(String name, String category, int price) { } public Product create(Product product) { - if (this.data.stream() - .filter(p -> p.getName().equals(product.getName())) - .toList().isEmpty()) { + if (!nameExists(product.getName())) { return this.create(product.getName(), product.getCategory(), product.getPrice()); } return null; @@ -46,6 +45,9 @@ public Product find(int id) { } public Product update(int id, Product product) { + if (nameExists(product.getName(), id)) { + return null; + } Product productToUpdate = find(id); productToUpdate.setName(product.getName()); productToUpdate.setCategory(product.getCategory()); @@ -58,4 +60,16 @@ public Product delete(int id) { data.remove(productToDelete); return productToDelete; } + + private boolean nameExists(String name) { + return !this.data.stream() + .filter(p -> p.getName().equals(name)) + .toList().isEmpty(); + } + + private boolean nameExists(String name, int excludeId) { + return !this.data.stream() + .filter(p -> p.getName().equals(name) && p.getId() != excludeId) + .toList().isEmpty(); + } } From 17d805f060225a57a66a4a18e3cfaa67c912361e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 14:29:26 +0100 Subject: [PATCH 10/11] Throw exception when removing nonexistent product --- .../java/com/booleanuk/api/products/ProductController.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index 70d6c3e..d1e353d 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -71,6 +71,10 @@ public Product update(@PathVariable(name="id") int id, @RequestBody Product prod @DeleteMapping("/{id}") @ResponseStatus() public Product delete(@PathVariable(name="id") int id) { - return repository.delete(id); + try { + return repository.delete(id); + } catch (NoSuchElementException e) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found."); + } } } From b0aeeeaf70d29200ea2e8d09fffc38c55870faef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matilda=20S=C3=B6nnergaard?= Date: Mon, 20 Jan 2025 14:31:24 +0100 Subject: [PATCH 11/11] Add default response statuses --- .../com/booleanuk/api/products/ProductController.java | 8 ++++---- .../com/booleanuk/api/products/ProductRepository.java | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/booleanuk/api/products/ProductController.java b/src/main/java/com/booleanuk/api/products/ProductController.java index d1e353d..96a4a23 100644 --- a/src/main/java/com/booleanuk/api/products/ProductController.java +++ b/src/main/java/com/booleanuk/api/products/ProductController.java @@ -21,7 +21,7 @@ public ProductController() { } @PostMapping - @ResponseStatus + @ResponseStatus(HttpStatus.CREATED) public Product create(@RequestBody Product product) { Product p = repository.create(product); if (p == null) { @@ -44,7 +44,7 @@ public List getAll(@RequestParam(name="category", required=false) Strin } @GetMapping("/{id}") - @ResponseStatus() + @ResponseStatus(HttpStatus.OK) public Product getOne(@PathVariable(name="id") int id) { try { return repository.find(id); @@ -54,7 +54,7 @@ public Product getOne(@PathVariable(name="id") int id) { } @PutMapping("/{id}") - @ResponseStatus() + @ResponseStatus(HttpStatus.CREATED) public Product update(@PathVariable(name="id") int id, @RequestBody Product product) { Product updatedProduct; try { @@ -69,7 +69,7 @@ public Product update(@PathVariable(name="id") int id, @RequestBody Product prod } @DeleteMapping("/{id}") - @ResponseStatus() + @ResponseStatus(HttpStatus.OK) public Product delete(@PathVariable(name="id") int id) { try { return repository.delete(id); diff --git a/src/main/java/com/booleanuk/api/products/ProductRepository.java b/src/main/java/com/booleanuk/api/products/ProductRepository.java index 58b6ef5..7138220 100644 --- a/src/main/java/com/booleanuk/api/products/ProductRepository.java +++ b/src/main/java/com/booleanuk/api/products/ProductRepository.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.NoSuchElementException; public class ProductRepository { private int idCounter = 1;