From 228e23abf9d225ced663eabfa6e1cae235b4839c Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Thu, 28 Mar 2024 14:13:04 +0100 Subject: [PATCH 01/13] Add subscription data to book details --- .../book/BookDetailsResponseCombiner.java | 9 +++++++- .../library/gateway/book/BookService.java | 10 +++++---- .../gateway/client/InventoryClient.java | 21 ++++++++++++++++--- src/main/resources/application.yml | 4 ++++ .../BookDetailsResponseCombinerShould.java | 6 ++++-- .../gateway/service/BookServiceShould.java | 9 ++++++-- 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java b/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java index 2c69726..c981bec 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java +++ b/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java @@ -14,11 +14,14 @@ public class BookDetailsResponseCombiner { private static final String JSON_FIELD_RECORDS = "records"; + private static final String JSON_FIELD_SUBSCRIPTION = "subscribed"; - public JsonNode generateBookDetailsDto(Object book, List rentalRecords, int availableBooksCount) { + public JsonNode generateBookDetailsDto(Object book, List rentalRecords, int availableBooksCount, boolean bookSubscription) { List records = combineRentalRecordsWithAvailable(rentalRecords, availableBooksCount); var json = jsonOf(book); extendJsonWithRecords((ObjectNode) json, jsonOf(records)); + extendJsonWithSubscription((ObjectNode) json, jsonOf(bookSubscription)); + return json; } @@ -38,4 +41,8 @@ private List generateAvailableRecords(int availableBoo private void extendJsonWithRecords(ObjectNode json, JsonNode records) { json.putIfAbsent(JSON_FIELD_RECORDS, records); } + + private void extendJsonWithSubscription(ObjectNode json, JsonNode bookSubscription) { + json.putIfAbsent(JSON_FIELD_SUBSCRIPTION, bookSubscription); + } } diff --git a/src/main/java/com/productdock/library/gateway/book/BookService.java b/src/main/java/com/productdock/library/gateway/book/BookService.java index 2cd9402..e581307 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookService.java +++ b/src/main/java/com/productdock/library/gateway/book/BookService.java @@ -18,9 +18,10 @@ public JsonNode getBookDetailsById(String bookId, String jwtToken) { var bookDtoMono = catalogClient.getBookData(bookId, jwtToken); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); + var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken); - var bookDetailsDtoMono = Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono).flatMap(tuple -> { - var book = bookDetailsResponseCombiner.generateBookDetailsDto(tuple.getT1(), tuple.getT2(), tuple.getT3()); + var bookDetailsDtoMono = Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { + var book = bookDetailsResponseCombiner.generateBookDetailsDto(tuple.getT1(), tuple.getT2(), tuple.getT3(), tuple.getT4()); return Mono.just(book); }); return bookDetailsDtoMono.toFuture().get(); @@ -33,9 +34,10 @@ public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, Stri String bookId = getIdFromBook(bookDetails); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); + var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken); - var bookDetailsDtoMono = Mono.zip(rentalRecordsDtoMono, availableBooksCountMono).flatMap(tuple -> { - var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetails, tuple.getT1(), tuple.getT2()); + var bookDetailsDtoMono = Mono.zip(rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { + var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetails, tuple.getT1(), tuple.getT2(), tuple.getT3()); return Mono.just(book); }); return bookDetailsDtoMono.toFuture().get(); diff --git a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java index cfdccc0..8f5f1f5 100644 --- a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java +++ b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java @@ -12,13 +12,16 @@ public class InventoryClient { @Value("${inventory.service.url}/api/inventory/book/") private String inventoryServiceUrl; + @Value("${inventory.service.url}/api/inventory/subscriptions/") + private String subscriptionServiceUrl; + private WebClient webClient; - public InventoryClient(){ + public InventoryClient() { this.webClient = WebClient.create(); } - public Mono getAvailableBookCopiesCount(String bookId, String jwtToken){ + public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) { var inventoryBookUrl = inventoryServiceUrl + bookId; return webClient @@ -27,7 +30,19 @@ public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) .header(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken) .retrieve() .bodyToMono(Integer.class) - .onErrorReturn(RuntimeException.class,0); + .onErrorReturn(RuntimeException.class, 0); + } + + public Mono getBookSubscription(String bookId, String jwtToken) { + var subscriptionUrl = subscriptionServiceUrl + bookId; + + return webClient + .get() + .uri(subscriptionUrl) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken) + .retrieve() + .bodyToMono(Boolean.class) + .onErrorReturn(RuntimeException.class, false); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7029e66..79e93e7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -42,6 +42,10 @@ spring: uri: ${USER_PROFILES_SERVICE_URL} predicates: - Path=/api/user-profiles/** + - id: inventory-route + uri: ${INVENTORY_SERVICE_URL} + predicates: + - Path=/api/inventory/subscriptions/** data: mongodb: diff --git a/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java b/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java index e77a90a..55af154 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java @@ -25,8 +25,9 @@ void generateBookDetailsDtoWithAvailableRecord_whenAvailableBookCopiesExist() { anyRecord.put("recordProperty", "recordValue"); List rentalRecordsDto = List.of(anyRecord); var availableBookCount = 1; + var bookSubscription = false; - var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(anyDto, rentalRecordsDto, availableBookCount); + var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(anyDto, rentalRecordsDto, availableBookCount, bookSubscription); assertThat(bookDetails.get("property").asText()).isEqualTo("value"); assertThat(bookDetails.get("records")).isNotNull(); @@ -43,8 +44,9 @@ void generateBookDetailsDto_whenAvailableBookCopiesDoNotExist() { anyRecord.put("recordProperty", "recordValue"); List rentalRecordsDto = List.of(anyRecord); var availableBookCount = 0; + var bookSubscription = false; - var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(anyDto, rentalRecordsDto, availableBookCount); + var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(anyDto, rentalRecordsDto, availableBookCount, bookSubscription); assertThat(bookDetails.get("records")).isNotNull(); assertThat(bookDetails.get("records").size()).isEqualTo(1); diff --git a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java index 24c07b4..f761065 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java @@ -30,11 +30,14 @@ class BookServiceShould { private static final String BOOK_AUTHOR = "::author::"; private static final String JWT_TOKEN = ""; private static final int AVAILABLE_BOOK_COUNT = 1; + private static final boolean BOOK_SUBSCRIPTION = false; private static final Object CATALOG_RESPONSE = Mockito.mock(Object.class); private static final List RENTAL_RESPONSE = List.of(mock(Object.class)); private static final Mono CATALOG_MONO = Mono.just(CATALOG_RESPONSE); private static final Mono> RENTAL_MONO = Mono.just(RENTAL_RESPONSE); private static final Mono AVAILABLE_BOOK_COUNT_MONO = Mono.just(AVAILABLE_BOOK_COUNT); + + private static final Mono BOOK_SUBSCRIPTION_MONO = Mono.just(BOOK_SUBSCRIPTION); private static final JsonNode BOOK_DETAILS_JSON = Mockito.mock(JsonNode.class); @InjectMocks @@ -57,7 +60,8 @@ void generateBookDetailsDtoByBookId() { given(catalogClient.getBookData(BOOK_ID, JWT_TOKEN)).willReturn(CATALOG_MONO); given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(bookDetailsResponseCombiner.generateBookDetailsDto(CATALOG_RESPONSE, RENTAL_RESPONSE, AVAILABLE_BOOK_COUNT)).willReturn(BOOK_DETAILS_JSON); + given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(bookDetailsResponseCombiner.generateBookDetailsDto(CATALOG_RESPONSE, RENTAL_RESPONSE, AVAILABLE_BOOK_COUNT, BOOK_SUBSCRIPTION)).willReturn(BOOK_DETAILS_JSON); var bookDetails = bookService.getBookDetailsById(BOOK_ID, JWT_TOKEN); @@ -73,7 +77,8 @@ void generateBookDetailsDtoByBookTitleAndAuthor() { given(catalogClient.getBookDataByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN)).willReturn(catalogMono); given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(bookDetailsResponseCombiner.generateBookDetailsDto(catalogResponse, RENTAL_RESPONSE, AVAILABLE_BOOK_COUNT)) + given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(bookDetailsResponseCombiner.generateBookDetailsDto(catalogResponse, RENTAL_RESPONSE, AVAILABLE_BOOK_COUNT, BOOK_SUBSCRIPTION)) .willReturn(BOOK_DETAILS_JSON); var bookDetails = bookService.getBookDetailsByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN); From cf8864ac231de3a10ce622ce0b136a89f9c8fe2f Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Thu, 28 Mar 2024 14:23:33 +0100 Subject: [PATCH 02/13] Fix code smells --- .../library/gateway/client/InventoryClient.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java index 8f5f1f5..2e99421 100644 --- a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java +++ b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java @@ -1,6 +1,5 @@ package com.productdock.library.gateway.client; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; @@ -9,11 +8,8 @@ @Component public class InventoryClient { - @Value("${inventory.service.url}/api/inventory/book/") - private String inventoryServiceUrl; - - @Value("${inventory.service.url}/api/inventory/subscriptions/") - private String subscriptionServiceUrl; + private static final String inventoryServiceUrl = "${inventory.service.url}/api/inventory/book/"; + private static final String subscriptionServiceUrl = "${inventory.service.url}/api/inventory/subscriptions/"; private WebClient webClient; From 570a5f7d9b4f6657b493bca49c5152cd4b65c0f8 Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Thu, 28 Mar 2024 14:32:29 +0100 Subject: [PATCH 03/13] Fix inventory service url builder --- .../library/gateway/client/InventoryClient.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java index 2e99421..3ba76d7 100644 --- a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java +++ b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java @@ -1,5 +1,6 @@ package com.productdock.library.gateway.client; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; @@ -8,8 +9,8 @@ @Component public class InventoryClient { - private static final String inventoryServiceUrl = "${inventory.service.url}/api/inventory/book/"; - private static final String subscriptionServiceUrl = "${inventory.service.url}/api/inventory/subscriptions/"; + @Value("${inventory.service.url}") + private String inventoryServiceUrl; private WebClient webClient; @@ -18,7 +19,7 @@ public InventoryClient() { } public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) { - var inventoryBookUrl = inventoryServiceUrl + bookId; + var inventoryBookUrl = inventoryServiceUrl + "/api/inventory/book/" + bookId; return webClient .get() @@ -30,7 +31,7 @@ public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) } public Mono getBookSubscription(String bookId, String jwtToken) { - var subscriptionUrl = subscriptionServiceUrl + bookId; + var subscriptionUrl = inventoryServiceUrl + "/api/inventory/subscriptions/" + bookId; return webClient .get() From 711d28eea6f1eead81822bd2b04ef9bf3fc6c83f Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Tue, 2 Apr 2024 10:46:16 +0200 Subject: [PATCH 04/13] Add book details dto --- .../library/gateway/book/BookDetailsDto.java | 19 +++++++++++++++++++ .../book/BookDetailsResponseCombiner.java | 8 ++++---- .../library/gateway/book/BookService.java | 14 ++++++++++++-- src/main/resources/application.yml | 2 +- .../BookDetailsResponseCombinerShould.java | 17 +++++++++++++++-- .../gateway/service/BookServiceShould.java | 6 +++--- 6 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java diff --git a/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java b/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java new file mode 100644 index 0000000..ccaddf4 --- /dev/null +++ b/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java @@ -0,0 +1,19 @@ +package com.productdock.library.gateway.book; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class BookDetailsDto { + private Object bookDataDto; + private List rentalRecordsDto; + private Integer availableBookCount; + private Boolean bookSubscription; +} diff --git a/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java b/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java index c981bec..9db7219 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java +++ b/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java @@ -16,11 +16,11 @@ public class BookDetailsResponseCombiner { private static final String JSON_FIELD_RECORDS = "records"; private static final String JSON_FIELD_SUBSCRIPTION = "subscribed"; - public JsonNode generateBookDetailsDto(Object book, List rentalRecords, int availableBooksCount, boolean bookSubscription) { - List records = combineRentalRecordsWithAvailable(rentalRecords, availableBooksCount); - var json = jsonOf(book); + public JsonNode generateBookDetailsDto(BookDetailsDto bookDetailsDto) { + List records = combineRentalRecordsWithAvailable(bookDetailsDto.getRentalRecordsDto(), bookDetailsDto.getAvailableBookCount()); + var json = jsonOf(bookDetailsDto.getBookDataDto()); extendJsonWithRecords((ObjectNode) json, jsonOf(records)); - extendJsonWithSubscription((ObjectNode) json, jsonOf(bookSubscription)); + extendJsonWithSubscription((ObjectNode) json, jsonOf(bookDetailsDto.getBookSubscription())); return json; } diff --git a/src/main/java/com/productdock/library/gateway/book/BookService.java b/src/main/java/com/productdock/library/gateway/book/BookService.java index e581307..185ae7b 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookService.java +++ b/src/main/java/com/productdock/library/gateway/book/BookService.java @@ -21,7 +21,12 @@ public JsonNode getBookDetailsById(String bookId, String jwtToken) { var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken); var bookDetailsDtoMono = Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { - var book = bookDetailsResponseCombiner.generateBookDetailsDto(tuple.getT1(), tuple.getT2(), tuple.getT3(), tuple.getT4()); + var bookDetailsDto = new BookDetailsDto(); + bookDetailsDto.setBookDataDto(tuple.getT1()); + bookDetailsDto.setRentalRecordsDto(tuple.getT2()); + bookDetailsDto.setAvailableBookCount(tuple.getT3()); + bookDetailsDto.setBookSubscription(tuple.getT4()); + var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); return Mono.just(book); }); return bookDetailsDtoMono.toFuture().get(); @@ -37,7 +42,12 @@ public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, Stri var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken); var bookDetailsDtoMono = Mono.zip(rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { - var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetails, tuple.getT1(), tuple.getT2(), tuple.getT3()); + var bookDetailsDto = new BookDetailsDto(); + bookDetailsDto.setBookDataDto(bookDetails); + bookDetailsDto.setRentalRecordsDto(tuple.getT1()); + bookDetailsDto.setAvailableBookCount(tuple.getT2()); + bookDetailsDto.setBookSubscription(tuple.getT3()); + var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); return Mono.just(book); }); return bookDetailsDtoMono.toFuture().get(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 79e93e7..0b42c4a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -45,7 +45,7 @@ spring: - id: inventory-route uri: ${INVENTORY_SERVICE_URL} predicates: - - Path=/api/inventory/subscriptions/** + - Path=/api/inventory/** data: mongodb: diff --git a/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java b/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java index 55af154..b996614 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java @@ -1,5 +1,6 @@ package com.productdock.library.gateway.service; +import com.productdock.library.gateway.book.BookDetailsDto; import com.productdock.library.gateway.book.BookDetailsResponseCombiner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,7 +28,13 @@ void generateBookDetailsDtoWithAvailableRecord_whenAvailableBookCopiesExist() { var availableBookCount = 1; var bookSubscription = false; - var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(anyDto, rentalRecordsDto, availableBookCount, bookSubscription); + var bookDetailsDto = new BookDetailsDto(); + bookDetailsDto.setBookDataDto(anyDto); + bookDetailsDto.setRentalRecordsDto(rentalRecordsDto); + bookDetailsDto.setAvailableBookCount(availableBookCount); + bookDetailsDto.setBookSubscription(bookSubscription); + + var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); assertThat(bookDetails.get("property").asText()).isEqualTo("value"); assertThat(bookDetails.get("records")).isNotNull(); @@ -46,7 +53,13 @@ void generateBookDetailsDto_whenAvailableBookCopiesDoNotExist() { var availableBookCount = 0; var bookSubscription = false; - var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(anyDto, rentalRecordsDto, availableBookCount, bookSubscription); + var bookDetailsDto = new BookDetailsDto(); + bookDetailsDto.setBookDataDto(anyDto); + bookDetailsDto.setRentalRecordsDto(rentalRecordsDto); + bookDetailsDto.setAvailableBookCount(availableBookCount); + bookDetailsDto.setBookSubscription(bookSubscription); + + var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); assertThat(bookDetails.get("records")).isNotNull(); assertThat(bookDetails.get("records").size()).isEqualTo(1); diff --git a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java index f761065..ba0cbe9 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java @@ -19,6 +19,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -36,7 +37,6 @@ class BookServiceShould { private static final Mono CATALOG_MONO = Mono.just(CATALOG_RESPONSE); private static final Mono> RENTAL_MONO = Mono.just(RENTAL_RESPONSE); private static final Mono AVAILABLE_BOOK_COUNT_MONO = Mono.just(AVAILABLE_BOOK_COUNT); - private static final Mono BOOK_SUBSCRIPTION_MONO = Mono.just(BOOK_SUBSCRIPTION); private static final JsonNode BOOK_DETAILS_JSON = Mockito.mock(JsonNode.class); @@ -61,7 +61,7 @@ void generateBookDetailsDtoByBookId() { given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN)).willReturn(BOOK_SUBSCRIPTION_MONO); - given(bookDetailsResponseCombiner.generateBookDetailsDto(CATALOG_RESPONSE, RENTAL_RESPONSE, AVAILABLE_BOOK_COUNT, BOOK_SUBSCRIPTION)).willReturn(BOOK_DETAILS_JSON); + given(bookDetailsResponseCombiner.generateBookDetailsDto(any())).willReturn(BOOK_DETAILS_JSON); var bookDetails = bookService.getBookDetailsById(BOOK_ID, JWT_TOKEN); @@ -78,7 +78,7 @@ void generateBookDetailsDtoByBookTitleAndAuthor() { given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN)).willReturn(BOOK_SUBSCRIPTION_MONO); - given(bookDetailsResponseCombiner.generateBookDetailsDto(catalogResponse, RENTAL_RESPONSE, AVAILABLE_BOOK_COUNT, BOOK_SUBSCRIPTION)) + given(bookDetailsResponseCombiner.generateBookDetailsDto(any())) .willReturn(BOOK_DETAILS_JSON); var bookDetails = bookService.getBookDetailsByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN); From a74f95aff85a63197a27ede81f9127a7d2ca4676 Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Thu, 4 Apr 2024 09:49:47 +0200 Subject: [PATCH 05/13] Fix get user subscription for book --- .../library/gateway/book/BookService.java | 13 +++++++++++-- .../gateway/book/BookSubscriptionDto.java | 18 ++++++++++++++++++ .../gateway/client/InventoryClient.java | 17 ++++++++++++----- .../gateway/service/BookServiceShould.java | 7 ++++--- 4 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java diff --git a/src/main/java/com/productdock/library/gateway/book/BookService.java b/src/main/java/com/productdock/library/gateway/book/BookService.java index 185ae7b..4220953 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookService.java +++ b/src/main/java/com/productdock/library/gateway/book/BookService.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jwt.JWTParser; import com.productdock.library.gateway.client.CatalogClient; import com.productdock.library.gateway.client.InventoryClient; import com.productdock.library.gateway.client.RentalClient; @@ -13,12 +14,17 @@ public record BookService(CatalogClient catalogClient, RentalClient rentalClient, InventoryClient inventoryClient, BookDetailsResponseCombiner bookDetailsResponseCombiner) { + private static final String CLAIM_EMAIL = "email"; + @SneakyThrows public JsonNode getBookDetailsById(String bookId, String jwtToken) { + var jwt = JWTParser.parse(jwtToken); + var userId = jwt.getJWTClaimsSet().getClaim(CLAIM_EMAIL).toString(); + var bookDtoMono = catalogClient.getBookData(bookId, jwtToken); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); - var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken); + var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken, userId); var bookDetailsDtoMono = Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { var bookDetailsDto = new BookDetailsDto(); @@ -34,12 +40,15 @@ public JsonNode getBookDetailsById(String bookId, String jwtToken) { @SneakyThrows public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, String jwtToken) { + var jwt = JWTParser.parse(jwtToken); + var userId = jwt.getJWTClaimsSet().getClaim(CLAIM_EMAIL).toString(); + var bookDtoMono = catalogClient.getBookDataByTitleAndAuthor(title, author, jwtToken); var bookDetails = bookDtoMono.toFuture().get(); String bookId = getIdFromBook(bookDetails); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); - var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken); + var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken, userId); var bookDetailsDtoMono = Mono.zip(rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { var bookDetailsDto = new BookDetailsDto(); diff --git a/src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java b/src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java new file mode 100644 index 0000000..6cd10aa --- /dev/null +++ b/src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java @@ -0,0 +1,18 @@ +package com.productdock.library.gateway.book; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Date; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class BookSubscriptionDto { + private String bookId; + private String userId; + private Date createdDate; +} diff --git a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java index 3ba76d7..9930ace 100644 --- a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java +++ b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java @@ -4,6 +4,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.util.DefaultUriBuilderFactory; import reactor.core.publisher.Mono; @Component @@ -30,16 +31,22 @@ public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) .onErrorReturn(RuntimeException.class, 0); } - public Mono getBookSubscription(String bookId, String jwtToken) { - var subscriptionUrl = inventoryServiceUrl + "/api/inventory/subscriptions/" + bookId; + public Mono getBookSubscription(String bookId, String jwtToken, String userId) { + var subscriptionUrl = inventoryServiceUrl + "/api/inventory/books/" + bookId + "/subscriptions"; + var uri = new DefaultUriBuilderFactory(subscriptionUrl) + .builder() + .queryParam("userId", userId) + .build(); + return webClient .get() - .uri(subscriptionUrl) + .uri(uri) .header(HttpHeaders.AUTHORIZATION, "Bearer " + jwtToken) .retrieve() - .bodyToMono(Boolean.class) - .onErrorReturn(RuntimeException.class, false); + .toBodilessEntity() + .map(responseEntity -> responseEntity.getStatusCode().is2xxSuccessful()) + .onErrorReturn(false); } } diff --git a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java index ba0cbe9..1bfa5ed 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java @@ -29,7 +29,8 @@ class BookServiceShould { private static final String BOOK_ID = "1"; private static final String BOOK_TITLE = "::title::"; private static final String BOOK_AUTHOR = "::author::"; - private static final String JWT_TOKEN = ""; + private static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVtYWlsIn0.9_82fPfiHdHoOkyx8WQY8FsgPivtguil3QL5a3bDE7g"; + private static final String USER_ID = "email"; private static final int AVAILABLE_BOOK_COUNT = 1; private static final boolean BOOK_SUBSCRIPTION = false; private static final Object CATALOG_RESPONSE = Mockito.mock(Object.class); @@ -60,7 +61,7 @@ void generateBookDetailsDtoByBookId() { given(catalogClient.getBookData(BOOK_ID, JWT_TOKEN)).willReturn(CATALOG_MONO); given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); given(bookDetailsResponseCombiner.generateBookDetailsDto(any())).willReturn(BOOK_DETAILS_JSON); var bookDetails = bookService.getBookDetailsById(BOOK_ID, JWT_TOKEN); @@ -77,7 +78,7 @@ void generateBookDetailsDtoByBookTitleAndAuthor() { given(catalogClient.getBookDataByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN)).willReturn(catalogMono); given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); given(bookDetailsResponseCombiner.generateBookDetailsDto(any())) .willReturn(BOOK_DETAILS_JSON); From a67169ae92670ecffa92245e4a204909039ea509 Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Thu, 4 Apr 2024 10:14:52 +0200 Subject: [PATCH 06/13] Fix BookApiTest token creation --- .../library/gateway/service/BookApiTest.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/productdock/library/gateway/service/BookApiTest.java b/src/test/java/com/productdock/library/gateway/service/BookApiTest.java index 630e7a1..e342e69 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookApiTest.java +++ b/src/test/java/com/productdock/library/gateway/service/BookApiTest.java @@ -14,6 +14,8 @@ import org.springframework.test.web.reactive.server.WebTestClient; import java.io.IOException; +import java.util.Base64; +import java.util.Date; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasSize; @@ -54,7 +56,8 @@ static void tearDown() throws IOException { @Test @WithMockUser void givenBookId_thenGetBookDetails() { - mockUserProfilesBackEnd.enqueue(new MockResponse().setBody("id-token")); + var token = generateToken(); + mockUserProfilesBackEnd.enqueue(new MockResponse().setBody(token)); mockCatalogBackEnd.enqueue(new MockResponse() .setBody("{\"id\": \"1\", \"title\": \"Title\", \"author\": \"John Doe\", \"cover\": \"Cover\", " + "\"reviews\":[{\"userFullName\":\"John Doe\",\"rating\":5,\"recommendation\":[\"JUNIOR\"],\"comment\":\"Must read!\"}]}") @@ -79,7 +82,8 @@ void givenBookId_thenGetBookDetails() { @Test @WithMockUser void givenTitleAndAuthor_thenGetBookDetails() { - mockUserProfilesBackEnd.enqueue(new MockResponse().setBody("id-token")); + var token = generateToken(); + mockUserProfilesBackEnd.enqueue(new MockResponse().setBody(token)); mockCatalogBackEnd.enqueue(new MockResponse() .setBody("{\"id\": \"1\", \"title\": \"Title\", \"author\": \"John Doe\", \"cover\": \"Cover\", " + "\"reviews\":[{\"userFullName\":\"John Doe\",\"rating\":5,\"recommendation\":[\"JUNIOR\"],\"comment\":\"Must read!\"}]}") @@ -100,4 +104,13 @@ void givenTitleAndAuthor_thenGetBookDetails() { .jsonPath("$.reviews[0].comment").isEqualTo("Must read!") .jsonPath("$.records").value(empty()); } + + private String generateToken() { + var email = "user@email.com"; + var header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; + var payload = "{\"sub\":\"subject\",\"email\":\"" + email + "\",\"iat\":" + new Date().getTime() + "}"; + var headerBase64 = Base64.getEncoder().encodeToString(header.getBytes()); + var payloadBase64 = Base64.getEncoder().encodeToString(payload.getBytes()); + return headerBase64 + "." + payloadBase64 + ".signature"; + } } From b66fb7815b8edfb4b996038ca775d0903f1d693b Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Thu, 4 Apr 2024 10:21:13 +0200 Subject: [PATCH 07/13] Fix BookApiTest check subscription --- .../productdock/library/gateway/service/BookApiTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/productdock/library/gateway/service/BookApiTest.java b/src/test/java/com/productdock/library/gateway/service/BookApiTest.java index e342e69..ba21206 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookApiTest.java +++ b/src/test/java/com/productdock/library/gateway/service/BookApiTest.java @@ -76,7 +76,8 @@ void givenBookId_thenGetBookDetails() { .jsonPath("$.reviews[0].recommendation[0]").isEqualTo("JUNIOR") .jsonPath("$.reviews[0].recommendation").value(hasSize(1)) .jsonPath("$.reviews[0].comment").isEqualTo("Must read!") - .jsonPath("$.records").value(empty()); + .jsonPath("$.records").value(empty()) + .jsonPath("$.subscribed").isEqualTo(false); } @Test @@ -102,7 +103,8 @@ void givenTitleAndAuthor_thenGetBookDetails() { .jsonPath("$.reviews[0].recommendation[0]").isEqualTo("JUNIOR") .jsonPath("$.reviews[0].recommendation").value(hasSize(1)) .jsonPath("$.reviews[0].comment").isEqualTo("Must read!") - .jsonPath("$.records").value(empty()); + .jsonPath("$.records").value(empty()) + .jsonPath("$.subscribed").isEqualTo(false); } private String generateToken() { From fde2401d9d13575eda5ae164bd948a3e64f836fd Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Mon, 8 Apr 2024 11:39:50 +0200 Subject: [PATCH 08/13] Fix available books url --- .../com/productdock/library/gateway/client/InventoryClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java index 9930ace..2809b14 100644 --- a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java +++ b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java @@ -20,7 +20,7 @@ public InventoryClient() { } public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) { - var inventoryBookUrl = inventoryServiceUrl + "/api/inventory/book/" + bookId; + var inventoryBookUrl = inventoryServiceUrl + "/api/inventory/books/" + bookId; return webClient .get() From a414d29d6910b4992e5c44b83108fb2bd0e5f372 Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Mon, 8 Apr 2024 11:44:53 +0200 Subject: [PATCH 09/13] Fix book derails dto and service refactor --- .../library/gateway/book/BookDetailsDto.java | 16 +------- .../book/BookDetailsResponseCombiner.java | 6 +-- .../library/gateway/book/BookService.java | 41 ++++++++++--------- .../BookDetailsResponseCombinerShould.java | 16 ++------ 4 files changed, 29 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java b/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java index ccaddf4..672026a 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java +++ b/src/main/java/com/productdock/library/gateway/book/BookDetailsDto.java @@ -1,19 +1,7 @@ package com.productdock.library.gateway.book; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - import java.util.List; -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class BookDetailsDto { - private Object bookDataDto; - private List rentalRecordsDto; - private Integer availableBookCount; - private Boolean bookSubscription; +public record BookDetailsDto(Object bookData, List rentalRecords, Integer availableBookCount, + Boolean bookSubscription) { } diff --git a/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java b/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java index 9db7219..3ce8ec3 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java +++ b/src/main/java/com/productdock/library/gateway/book/BookDetailsResponseCombiner.java @@ -17,10 +17,10 @@ public class BookDetailsResponseCombiner { private static final String JSON_FIELD_SUBSCRIPTION = "subscribed"; public JsonNode generateBookDetailsDto(BookDetailsDto bookDetailsDto) { - List records = combineRentalRecordsWithAvailable(bookDetailsDto.getRentalRecordsDto(), bookDetailsDto.getAvailableBookCount()); - var json = jsonOf(bookDetailsDto.getBookDataDto()); + List records = combineRentalRecordsWithAvailable(bookDetailsDto.rentalRecords(), bookDetailsDto.availableBookCount()); + var json = jsonOf(bookDetailsDto.bookData()); extendJsonWithRecords((ObjectNode) json, jsonOf(records)); - extendJsonWithSubscription((ObjectNode) json, jsonOf(bookDetailsDto.getBookSubscription())); + extendJsonWithSubscription((ObjectNode) json, jsonOf(bookDetailsDto.bookSubscription())); return json; } diff --git a/src/main/java/com/productdock/library/gateway/book/BookService.java b/src/main/java/com/productdock/library/gateway/book/BookService.java index 4220953..111ac48 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookService.java +++ b/src/main/java/com/productdock/library/gateway/book/BookService.java @@ -9,6 +9,10 @@ import lombok.SneakyThrows; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; +import reactor.util.function.Tuple4; + +import java.text.ParseException; +import java.util.List; @Service public record BookService(CatalogClient catalogClient, RentalClient rentalClient, @@ -18,30 +22,21 @@ public record BookService(CatalogClient catalogClient, RentalClient rentalClient @SneakyThrows public JsonNode getBookDetailsById(String bookId, String jwtToken) { - var jwt = JWTParser.parse(jwtToken); - var userId = jwt.getJWTClaimsSet().getClaim(CLAIM_EMAIL).toString(); + var userId = getClaimEmail(jwtToken); var bookDtoMono = catalogClient.getBookData(bookId, jwtToken); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken, userId); - var bookDetailsDtoMono = Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { - var bookDetailsDto = new BookDetailsDto(); - bookDetailsDto.setBookDataDto(tuple.getT1()); - bookDetailsDto.setRentalRecordsDto(tuple.getT2()); - bookDetailsDto.setAvailableBookCount(tuple.getT3()); - bookDetailsDto.setBookSubscription(tuple.getT4()); - var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); - return Mono.just(book); - }); + var bookDetailsDtoMono = generateBookDetailsDtoMono(Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono)); + return bookDetailsDtoMono.toFuture().get(); } @SneakyThrows public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, String jwtToken) { - var jwt = JWTParser.parse(jwtToken); - var userId = jwt.getJWTClaimsSet().getClaim(CLAIM_EMAIL).toString(); + var userId = getClaimEmail(jwtToken); var bookDtoMono = catalogClient.getBookDataByTitleAndAuthor(title, author, jwtToken); var bookDetails = bookDtoMono.toFuture().get(); @@ -50,16 +45,17 @@ public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, Stri var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken, userId); - var bookDetailsDtoMono = Mono.zip(rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono).flatMap(tuple -> { - var bookDetailsDto = new BookDetailsDto(); - bookDetailsDto.setBookDataDto(bookDetails); - bookDetailsDto.setRentalRecordsDto(tuple.getT1()); - bookDetailsDto.setAvailableBookCount(tuple.getT2()); - bookDetailsDto.setBookSubscription(tuple.getT3()); + var bookDetailsDtoMono = generateBookDetailsDtoMono(Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono)); + + return bookDetailsDtoMono.toFuture().get(); + } + + private Mono generateBookDetailsDtoMono(Mono, Integer, Boolean>> mono) { + return mono.flatMap(tuple -> { + var bookDetailsDto = new BookDetailsDto(tuple.getT1(), tuple.getT2(), tuple.getT3(), tuple.getT4()); var book = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); return Mono.just(book); }); - return bookDetailsDtoMono.toFuture().get(); } private String getIdFromBook(Object bookDetails) { @@ -68,6 +64,11 @@ private String getIdFromBook(Object bookDetails) { return bookIdNode.asText(); } + private String getClaimEmail(String jwtToken) throws ParseException { + var jwt = JWTParser.parse(jwtToken); + return jwt.getJWTClaimsSet().getClaim(CLAIM_EMAIL).toString(); + } + private JsonNode jsonOf(Object book) { return new ObjectMapper().valueToTree(book); } diff --git a/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java b/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java index b996614..9b00aa5 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookDetailsResponseCombinerShould.java @@ -27,12 +27,7 @@ void generateBookDetailsDtoWithAvailableRecord_whenAvailableBookCopiesExist() { List rentalRecordsDto = List.of(anyRecord); var availableBookCount = 1; var bookSubscription = false; - - var bookDetailsDto = new BookDetailsDto(); - bookDetailsDto.setBookDataDto(anyDto); - bookDetailsDto.setRentalRecordsDto(rentalRecordsDto); - bookDetailsDto.setAvailableBookCount(availableBookCount); - bookDetailsDto.setBookSubscription(bookSubscription); + var bookDetailsDto = new BookDetailsDto(anyDto, rentalRecordsDto, availableBookCount, bookSubscription); var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); @@ -52,13 +47,8 @@ void generateBookDetailsDto_whenAvailableBookCopiesDoNotExist() { List rentalRecordsDto = List.of(anyRecord); var availableBookCount = 0; var bookSubscription = false; - - var bookDetailsDto = new BookDetailsDto(); - bookDetailsDto.setBookDataDto(anyDto); - bookDetailsDto.setRentalRecordsDto(rentalRecordsDto); - bookDetailsDto.setAvailableBookCount(availableBookCount); - bookDetailsDto.setBookSubscription(bookSubscription); - + var bookDetailsDto = new BookDetailsDto(anyDto, rentalRecordsDto, availableBookCount, bookSubscription); + var bookDetails = bookDetailsResponseCombiner.generateBookDetailsDto(bookDetailsDto); assertThat(bookDetails.get("records")).isNotNull(); From 60a33216f09fc23d9d55a1bb6dd935b021f902af Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Mon, 8 Apr 2024 15:33:10 +0200 Subject: [PATCH 10/13] Fix book details test failure --- .../com/productdock/library/gateway/book/BookService.java | 6 +++--- .../productdock/library/gateway/client/InventoryClient.java | 2 +- .../library/gateway/service/BookServiceShould.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/productdock/library/gateway/book/BookService.java b/src/main/java/com/productdock/library/gateway/book/BookService.java index 111ac48..75c5c7e 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookService.java +++ b/src/main/java/com/productdock/library/gateway/book/BookService.java @@ -27,7 +27,7 @@ public JsonNode getBookDetailsById(String bookId, String jwtToken) { var bookDtoMono = catalogClient.getBookData(bookId, jwtToken); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); - var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken, userId); + var bookSubscriptionMono = inventoryClient.isUserSubscribedToBook(bookId, jwtToken, userId); var bookDetailsDtoMono = generateBookDetailsDtoMono(Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono)); @@ -43,9 +43,9 @@ public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, Stri String bookId = getIdFromBook(bookDetails); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); var availableBooksCountMono = inventoryClient.getAvailableBookCopiesCount(bookId, jwtToken); - var bookSubscriptionMono = inventoryClient.getBookSubscription(bookId, jwtToken, userId); + var bookSubscriptionMono = inventoryClient.isUserSubscribedToBook(bookId, jwtToken, userId); - var bookDetailsDtoMono = generateBookDetailsDtoMono(Mono.zip(bookDtoMono, rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono)); + var bookDetailsDtoMono = generateBookDetailsDtoMono(Mono.zip(Mono.just(bookDetails), rentalRecordsDtoMono, availableBooksCountMono, bookSubscriptionMono)); return bookDetailsDtoMono.toFuture().get(); } diff --git a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java index 2809b14..566295f 100644 --- a/src/main/java/com/productdock/library/gateway/client/InventoryClient.java +++ b/src/main/java/com/productdock/library/gateway/client/InventoryClient.java @@ -31,7 +31,7 @@ public Mono getAvailableBookCopiesCount(String bookId, String jwtToken) .onErrorReturn(RuntimeException.class, 0); } - public Mono getBookSubscription(String bookId, String jwtToken, String userId) { + public Mono isUserSubscribedToBook(String bookId, String jwtToken, String userId) { var subscriptionUrl = inventoryServiceUrl + "/api/inventory/books/" + bookId + "/subscriptions"; var uri = new DefaultUriBuilderFactory(subscriptionUrl) .builder() diff --git a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java index 1bfa5ed..040cf98 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java @@ -61,7 +61,7 @@ void generateBookDetailsDtoByBookId() { given(catalogClient.getBookData(BOOK_ID, JWT_TOKEN)).willReturn(CATALOG_MONO); given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(inventoryClient.isUserSubscribedToBook(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); given(bookDetailsResponseCombiner.generateBookDetailsDto(any())).willReturn(BOOK_DETAILS_JSON); var bookDetails = bookService.getBookDetailsById(BOOK_ID, JWT_TOKEN); @@ -78,7 +78,7 @@ void generateBookDetailsDtoByBookTitleAndAuthor() { given(catalogClient.getBookDataByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN)).willReturn(catalogMono); given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(inventoryClient.getBookSubscription(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(inventoryClient.isUserSubscribedToBook(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); given(bookDetailsResponseCombiner.generateBookDetailsDto(any())) .willReturn(BOOK_DETAILS_JSON); From ada44f57b3da814f5c9151a0a396a4c5ca3ae5b9 Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Tue, 9 Apr 2024 10:47:53 +0200 Subject: [PATCH 11/13] Fix method name --- .../com/productdock/library/gateway/book/BookService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/productdock/library/gateway/book/BookService.java b/src/main/java/com/productdock/library/gateway/book/BookService.java index 75c5c7e..40498dd 100644 --- a/src/main/java/com/productdock/library/gateway/book/BookService.java +++ b/src/main/java/com/productdock/library/gateway/book/BookService.java @@ -22,7 +22,7 @@ public record BookService(CatalogClient catalogClient, RentalClient rentalClient @SneakyThrows public JsonNode getBookDetailsById(String bookId, String jwtToken) { - var userId = getClaimEmail(jwtToken); + var userId = extractUserId(jwtToken); var bookDtoMono = catalogClient.getBookData(bookId, jwtToken); var rentalRecordsDtoMono = rentalClient.getBookRentalRecords(bookId, jwtToken); @@ -36,7 +36,7 @@ public JsonNode getBookDetailsById(String bookId, String jwtToken) { @SneakyThrows public JsonNode getBookDetailsByTitleAndAuthor(String title, String author, String jwtToken) { - var userId = getClaimEmail(jwtToken); + var userId = extractUserId(jwtToken); var bookDtoMono = catalogClient.getBookDataByTitleAndAuthor(title, author, jwtToken); var bookDetails = bookDtoMono.toFuture().get(); @@ -64,7 +64,7 @@ private String getIdFromBook(Object bookDetails) { return bookIdNode.asText(); } - private String getClaimEmail(String jwtToken) throws ParseException { + private String extractUserId(String jwtToken) throws ParseException { var jwt = JWTParser.parse(jwtToken); return jwt.getJWTClaimsSet().getClaim(CLAIM_EMAIL).toString(); } From 605e1f13fa986abe929ba8be71847d9bce7f48b1 Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Wed, 10 Apr 2024 11:22:44 +0200 Subject: [PATCH 12/13] Fix jwt token naming --- .../gateway/service/BookServiceShould.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java index 040cf98..11b361e 100644 --- a/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java +++ b/src/test/java/com/productdock/library/gateway/service/BookServiceShould.java @@ -29,7 +29,7 @@ class BookServiceShould { private static final String BOOK_ID = "1"; private static final String BOOK_TITLE = "::title::"; private static final String BOOK_AUTHOR = "::author::"; - private static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVtYWlsIn0.9_82fPfiHdHoOkyx8WQY8FsgPivtguil3QL5a3bDE7g"; + private static final String DUMMY_JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVtYWlsIn0.9_82fPfiHdHoOkyx8WQY8FsgPivtguil3QL5a3bDE7g"; private static final String USER_ID = "email"; private static final int AVAILABLE_BOOK_COUNT = 1; private static final boolean BOOK_SUBSCRIPTION = false; @@ -58,13 +58,13 @@ class BookServiceShould { @Test void generateBookDetailsDtoByBookId() { - given(catalogClient.getBookData(BOOK_ID, JWT_TOKEN)).willReturn(CATALOG_MONO); - given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); - given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(inventoryClient.isUserSubscribedToBook(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(catalogClient.getBookData(BOOK_ID, DUMMY_JWT_TOKEN)).willReturn(CATALOG_MONO); + given(rentalClient.getBookRentalRecords(BOOK_ID, DUMMY_JWT_TOKEN)).willReturn(RENTAL_MONO); + given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, DUMMY_JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); + given(inventoryClient.isUserSubscribedToBook(BOOK_ID, DUMMY_JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); given(bookDetailsResponseCombiner.generateBookDetailsDto(any())).willReturn(BOOK_DETAILS_JSON); - var bookDetails = bookService.getBookDetailsById(BOOK_ID, JWT_TOKEN); + var bookDetails = bookService.getBookDetailsById(BOOK_ID, DUMMY_JWT_TOKEN); assertThat(bookDetails).isEqualTo(BOOK_DETAILS_JSON); } @@ -75,14 +75,14 @@ void generateBookDetailsDtoByBookTitleAndAuthor() { Object catalogResponse = new ObjectMapper() .readValue("{\"id\": \"1\", \"title\": \"::title::\", \"author\":\"::author::\"}", Object.class); var catalogMono = Mono.just(catalogResponse); - given(catalogClient.getBookDataByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN)).willReturn(catalogMono); - given(rentalClient.getBookRentalRecords(BOOK_ID, JWT_TOKEN)).willReturn(RENTAL_MONO); - given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); - given(inventoryClient.isUserSubscribedToBook(BOOK_ID, JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); + given(catalogClient.getBookDataByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, DUMMY_JWT_TOKEN)).willReturn(catalogMono); + given(rentalClient.getBookRentalRecords(BOOK_ID, DUMMY_JWT_TOKEN)).willReturn(RENTAL_MONO); + given(inventoryClient.getAvailableBookCopiesCount(BOOK_ID, DUMMY_JWT_TOKEN)).willReturn(AVAILABLE_BOOK_COUNT_MONO); + given(inventoryClient.isUserSubscribedToBook(BOOK_ID, DUMMY_JWT_TOKEN, USER_ID)).willReturn(BOOK_SUBSCRIPTION_MONO); given(bookDetailsResponseCombiner.generateBookDetailsDto(any())) .willReturn(BOOK_DETAILS_JSON); - var bookDetails = bookService.getBookDetailsByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, JWT_TOKEN); + var bookDetails = bookService.getBookDetailsByTitleAndAuthor(BOOK_TITLE, BOOK_AUTHOR, DUMMY_JWT_TOKEN); assertThat(bookDetails).isEqualTo(BOOK_DETAILS_JSON); } From 7b425f26ee300b952c1c18d6bc2f5cbf5cdb9d0f Mon Sep 17 00:00:00 2001 From: Nenad Jeckovic Date: Tue, 16 Apr 2024 13:58:05 +0200 Subject: [PATCH 13/13] Delete unused subscription DTO --- .../gateway/book/BookSubscriptionDto.java | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java diff --git a/src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java b/src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java deleted file mode 100644 index 6cd10aa..0000000 --- a/src/main/java/com/productdock/library/gateway/book/BookSubscriptionDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.productdock.library.gateway.book; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.util.Date; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class BookSubscriptionDto { - private String bookId; - private String userId; - private Date createdDate; -}