diff --git a/src/main/java/group/rohlik/application/CartFetcher.java b/src/main/java/group/rohlik/application/CartFetcher.java new file mode 100644 index 0000000..7e94f3b --- /dev/null +++ b/src/main/java/group/rohlik/application/CartFetcher.java @@ -0,0 +1,53 @@ +package group.rohlik.application; + +import group.rohlik.entity.Cart; +import group.rohlik.entity.CartRepository; +import group.rohlik.entity.Discount; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Comparator; +import java.util.List; + +@AllArgsConstructor +@Service +@Transactional(readOnly = true) +public class CartFetcher { + + private final CartRepository cartRepository; + + public CartDetail fetch(long cartId) { + Cart cart = cartRepository.findById(cartId).orElseThrow(); + + return new CartDetail( + cart.getId(), + cart.totalPrice(), + cart + .getLines() + .stream() + .map(line -> new Line(line.getProduct().getSku(), line.getProduct().getName(), line.getQuantity())) + .sorted(Comparator.comparing(line -> line.sku)) + .toList(), + cart + .getDiscounts() + .stream() + .map(Discount::getName) + .sorted() + .toList() + ); + } + + private record CartDetail( + long id, + double total, + List lines, + List discounts + ) { } + + private record Line( + String sku, + String name, + int quantity + ) { } +} diff --git a/src/main/java/group/rohlik/controller/CartController.java b/src/main/java/group/rohlik/controller/CartController.java index dc91271..704eeb4 100644 --- a/src/main/java/group/rohlik/controller/CartController.java +++ b/src/main/java/group/rohlik/controller/CartController.java @@ -1,11 +1,13 @@ package group.rohlik.controller; import com.fasterxml.jackson.databind.JsonNode; +import group.rohlik.application.CartFetcher; import group.rohlik.application.CartDiscountAdder; import group.rohlik.application.CartLineAdder; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +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.RequestBody; @@ -17,6 +19,7 @@ public class CartController { private final CartLineAdder cartLineAdder; private final CartDiscountAdder cartDiscountAdder; + private final CartFetcher cartFetcher; @PostMapping(path = "/carts/{id}/lines", consumes = "application/json", produces = "application/json") public ResponseEntity addLine(@PathVariable long id, @RequestBody JsonNode payload) { @@ -36,4 +39,11 @@ public ResponseEntity addDiscount(@PathVariable long id, @RequestBody JsonNode p return new ResponseEntity<>(HttpStatus.OK); } + + @GetMapping(path = "/carts/{id}", consumes = "application/json", produces = "application/json") + public ResponseEntity recoverCartDetail(@PathVariable long id) { + var cartDetail = cartFetcher.fetch(id); + + return new ResponseEntity<>(cartDetail, HttpStatus.OK); + } } \ No newline at end of file diff --git a/src/test/java/group/rohlik/acceptance/steps/HttpSteps.java b/src/test/java/group/rohlik/acceptance/steps/HttpSteps.java new file mode 100644 index 0000000..bde3b4f --- /dev/null +++ b/src/test/java/group/rohlik/acceptance/steps/HttpSteps.java @@ -0,0 +1,45 @@ +package group.rohlik.acceptance.steps; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.cucumber.java.Before; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Assertions; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +@RequiredArgsConstructor +public class HttpSteps { + + private final TestRestTemplate template; + private ResponseEntity response; + + @Before + public void setUp() { + response = null; + } + + @When("I send a {string} request to {string}") + public void sendARequestWithBodyToWith(String method, String path) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(null, headers); + response = template.exchange(path, HttpMethod.resolve(method.toUpperCase()), entity, String.class); + } + + @Then("the response status should be {int} with body:") + public void theResponseStatusAndBodyShouldBe(int status, String body) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + + Assertions.assertEquals(response.getStatusCode(), HttpStatus.resolve(status)); + Assertions.assertEquals(mapper.readTree(body), mapper.readTree(response.getBody())); + } +} diff --git a/src/test/resources/features/get_cart_detail.feature b/src/test/resources/features/get_cart_detail.feature new file mode 100644 index 0000000..5103c95 --- /dev/null +++ b/src/test/resources/features/get_cart_detail.feature @@ -0,0 +1,37 @@ +Feature: Get cart detail + + Background: + Given the following products exist: + | sku | name | price | + | 001 | potatoes | 2.5 | + | 002 | water | 0.95 | + And there is a cart discount "free shipping" for 10 % with code "FREE-SHIPPING" + And I have a cart + And I add 2 units of product "001" to my cart + And I add 1 units of product "002" to my cart + And I apply "FREE-SHIPPING" discount to my cart + + Scenario: Get cart details + When I send a "GET" request to "/carts/1" + Then the response status should be 200 with body: + """ + { + "id": 1, + "total": 5.36, + "lines": [ + { + "sku": "001", + "name": "potatoes", + "quantity": 2 + }, + { + "sku": "002", + "name": "water", + "quantity": 1 + } + ], + "discounts": [ + "free shipping" + ] + } + """