From fa3f89bda84384f4f6b5193e831ec37ff9508c97 Mon Sep 17 00:00:00 2001 From: Wojtek Krzywiec Date: Tue, 12 May 2020 22:09:14 +0200 Subject: [PATCH] GoogleBookAdapter added --- .github/workflows/master.yaml | 2 +- pom.xml | 58 +++++++++++++++++ .../library/application/AddNewBookTest.java | 46 +++++++++++++ src/component-test/resources/application.yml | 7 ++ .../domain/book/model/BookDetailsDTO.java | 24 +++++++ .../book/ports/incoming/GetBookDetails.java | 7 ++ .../infrastructure/GoogleBooksAdapter.java | 65 +++++++++++++++++++ ...lient.java => GoogleBookSearchClient.java} | 11 +--- ....java => GoogleBooksSearchController.java} | 13 ++-- src/main/resources/application.yml | 6 +- .../GoogleBooksAdapterTest.java | 39 +++++++++++ ...t.java => GoogleBookSearchClientTest.java} | 13 +--- 12 files changed, 255 insertions(+), 36 deletions(-) create mode 100644 src/component-test/java/io/wkrzywiec/hexagonal/library/application/AddNewBookTest.java create mode 100644 src/component-test/resources/application.yml create mode 100644 src/main/java/io/wkrzywiec/hexagonal/library/domain/book/model/BookDetailsDTO.java create mode 100644 src/main/java/io/wkrzywiec/hexagonal/library/domain/book/ports/incoming/GetBookDetails.java create mode 100644 src/main/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapter.java rename src/main/java/io/wkrzywiec/hexagonal/library/query/{GoogleBooksClient.java => GoogleBookSearchClient.java} (63%) rename src/main/java/io/wkrzywiec/hexagonal/library/query/{GoogleBooksController.java => GoogleBooksSearchController.java} (51%) create mode 100644 src/test/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapterTest.java rename src/test/java/io/wkrzywiec/hexagonal/library/query/{GoogleBooksClientTest.java => GoogleBookSearchClientTest.java} (69%) diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index a3adf5a..e9a1534 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -33,6 +33,6 @@ jobs: with: java-version: 11.0.4 - name: SonarCloud Scan - run: mvn -B clean verify -Psonar -Dsonar.login=${{ secrets.SONAR_TOKEN }} + run: mvn -B clean verify -Psonar,component-test -Dsonar.login=${{ secrets.SONAR_TOKEN }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a8d2bb3..bcebd6a 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,64 @@ + + component-test + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add-component-test-sources + generate-test-sources + + add-test-source + + + + src/component-test/java + + + + + add-component-test-resources + generate-test-resources + + add-test-resource + + + + + src/component-test/resources + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M3 + + + failsafe-component-test + integration-test + + integration-test + verify + + + false + + + + + + + sonar diff --git a/src/component-test/java/io/wkrzywiec/hexagonal/library/application/AddNewBookTest.java b/src/component-test/java/io/wkrzywiec/hexagonal/library/application/AddNewBookTest.java new file mode 100644 index 0000000..f1cee2c --- /dev/null +++ b/src/component-test/java/io/wkrzywiec/hexagonal/library/application/AddNewBookTest.java @@ -0,0 +1,46 @@ +package io.wkrzywiec.hexagonal.library.application; + +import io.restassured.RestAssured; +import io.restassured.response.ValidatableResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.greaterThan; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class AddNewBookTest { + + @LocalServerPort + private int port; + + private String baseURL; + + @BeforeEach + public void init(){ + this.baseURL = "http://localhost:" + port; + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + @DisplayName("Search for a new book in Google Books") + public void whenSearchForBook_thenGetList(){ + //when + ValidatableResponse response = given() + .when() + .param("query", "lean startup") + .get( baseURL + "/google/books") + .prettyPeek() + .then(); + + //then + response.statusCode(HttpStatus.OK.value()) + .contentType("application/json") + .body("items.size()", greaterThan(0)); + + } +} diff --git a/src/component-test/resources/application.yml b/src/component-test/resources/application.yml new file mode 100644 index 0000000..68c72c6 --- /dev/null +++ b/src/component-test/resources/application.yml @@ -0,0 +1,7 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + driverClassName: org.h2.Driver + username: sa + password: password + jpa.database-platform: org.hibernate.dialect.H2Dialect diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/domain/book/model/BookDetailsDTO.java b/src/main/java/io/wkrzywiec/hexagonal/library/domain/book/model/BookDetailsDTO.java new file mode 100644 index 0000000..fb83189 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/domain/book/model/BookDetailsDTO.java @@ -0,0 +1,24 @@ +package io.wkrzywiec.hexagonal.library.domain.book.model; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Value; + +import java.util.List; + +@Value +@Builder +public class BookDetailsDTO { + + private String bookExternalId; + private String isbn10; + private String isbn13; + private String title; + private List authors; + private String publisher; + private String publishedDate; + private String description; + private int pages; + @EqualsAndHashCode.Exclude + private String imageLink; +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/domain/book/ports/incoming/GetBookDetails.java b/src/main/java/io/wkrzywiec/hexagonal/library/domain/book/ports/incoming/GetBookDetails.java new file mode 100644 index 0000000..64b8c4b --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/domain/book/ports/incoming/GetBookDetails.java @@ -0,0 +1,7 @@ +package io.wkrzywiec.hexagonal.library.domain.book.ports.incoming; + +import io.wkrzywiec.hexagonal.library.domain.book.model.BookDetailsDTO; + +public interface GetBookDetails { + BookDetailsDTO handle(String bookId); +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapter.java b/src/main/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapter.java new file mode 100644 index 0000000..2cc7110 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapter.java @@ -0,0 +1,65 @@ +package io.wkrzywiec.hexagonal.library.infrastructure; + +import io.restassured.path.json.JsonPath; +import io.wkrzywiec.hexagonal.library.domain.book.model.BookDetailsDTO; +import io.wkrzywiec.hexagonal.library.domain.book.ports.incoming.GetBookDetails; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static io.restassured.RestAssured.given; + +public class GoogleBooksAdapter implements GetBookDetails { + + @Override + public BookDetailsDTO handle(String googleBookId) { + + JsonPath response = + given() + .pathParam("bookId", googleBookId) + .when() + .get("https://www.googleapis.com/books/v1/volumes/{bookId}") + .jsonPath(); + + return BookDetailsDTO.builder() + .bookExternalId(googleBookId) + .isbn10(extractIsbn(response, "ISBN_10")) + .isbn13(extractIsbn(response, "ISBN_13")) + .title(response.getString("volumeInfo.title")) + .authors(extractAuthors(response)) + .publisher(response.getString("volumeInfo.publisher")) + .publishedDate(response.getString("volumeInfo.publishedDate")) + .description(response.getString("volumeInfo.description")) + .pages(response.getInt("volumeInfo.pageCount")) + .imageLink(extractImage(response)) + .build(); + } + + private String extractIsbn(JsonPath response, String isbnType){ + return response.getList("volumeInfo.industryIdentifiers") + .stream() + .map(isbnObj -> (Map) isbnObj) + .filter(isbnMap -> isbnMap.containsValue(isbnType)) + .map(isbnMap -> isbnMap.get("identifier")) + .findFirst() + .orElse(""); + } + + private List extractAuthors(JsonPath response) { + return response.getList("volumeInfo.authors") + .stream() + .map(authorObj -> (String) authorObj) + .collect(Collectors.toList()); + } + + private String extractImage(JsonPath response){ + Map imagesMap = response.getMap("volumeInfo.imageLinks"); + if (imagesMap.containsKey("thumbnail")){ + return imagesMap.get("thumbnail"); + } else { + return (String) new ArrayList(imagesMap.values()).get(0); + } + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksClient.java b/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBookSearchClient.java similarity index 63% rename from src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksClient.java rename to src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBookSearchClient.java index a06161c..2839ca8 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksClient.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBookSearchClient.java @@ -8,7 +8,7 @@ import static io.restassured.RestAssured.given; @RequiredArgsConstructor @Component -public class GoogleBooksClient { +public class GoogleBookSearchClient { public String searchForBooks(String query){ Response response = @@ -18,13 +18,4 @@ public class GoogleBooksClient { .get("https://www.googleapis.com/books/v1/volumes?langRestrict=en&maxResults=40&printType=books"); return response.getBody().asString(); } - - public String getBookById(String bookId) { - Response response = - given() - .pathParam("bookId", bookId) - .when() - .get("https://www.googleapis.com/books/v1/volumes/{bookId}"); - return response.getBody().asString(); - } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksController.java b/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksSearchController.java similarity index 51% rename from src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksController.java rename to src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksSearchController.java index cbee7cc..2a58caa 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksController.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksSearchController.java @@ -8,17 +8,12 @@ import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/google/books") @RequiredArgsConstructor -public class GoogleBooksController { +public class GoogleBooksSearchController { - private final GoogleBooksClient client; + private final GoogleBookSearchClient client; - @GetMapping("") - public ResponseEntity searchForBooks(@RequestParam String query){ + @GetMapping(value = "", produces = "application/json") + ResponseEntity searchForBooks(@RequestParam String query){ return new ResponseEntity<>(client.searchForBooks(query), HttpStatus.OK); } - - @GetMapping("/{bookId}") - public ResponseEntity getBookBId(@PathVariable String bookId){ - return new ResponseEntity<>(client.getBookById(bookId), HttpStatus.OK); - } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f37ec1f..58f108a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,8 +6,4 @@ spring: driverClassName: org.h2.Driver username: sa password: password - jpa.database-platform: org.hibernate.dialect.H2Dialect - -ribbon: - eureka: - enabled: false \ No newline at end of file + jpa.database-platform: org.hibernate.dialect.H2Dialect \ No newline at end of file diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapterTest.java b/src/test/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapterTest.java new file mode 100644 index 0000000..f5bd2e5 --- /dev/null +++ b/src/test/java/io/wkrzywiec/hexagonal/library/infrastructure/GoogleBooksAdapterTest.java @@ -0,0 +1,39 @@ +package io.wkrzywiec.hexagonal.library.infrastructure; + +import io.wkrzywiec.hexagonal.library.domain.book.model.BookDetailsDTO; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GoogleBooksAdapterTest { + + GoogleBooksAdapter adapter = new GoogleBooksAdapter(); + + @Test + @DisplayName("Get book details from Google Books") + public void givenCorrectBookId_whenGetBookDetails_thenReturnBookDetailsDTO(){ + //given + String googleBookId = "dWYyCwAAQBAJ"; + + //when + BookDetailsDTO bookDetails = adapter.handle(googleBookId); + + //then + BookDetailsDTO expectedDetails = BookDetailsDTO.builder() + .bookExternalId(googleBookId) + .isbn10("1473545374") + .isbn13("9781473545373") + .title("Homo Deus") + .authors(Collections.singletonList("Yuval Noah Harari")) + .publisher("Random House") + .publishedDate("2016-09-08") + .description("

**THE MILLION COPY BESTSELLER**

Sapiens showed us where we came from. In uncertain times, Homo Deus shows us where we’re going.

Yuval Noah Harari envisions a near future in which we face a new set of challenges. Homo Deus explores the projects, dreams and nightmares that will shape the twenty-first century and beyond – from overcoming death to creating artificial life.

It asks the fundamental questions: how can we protect this fragile world from our own destructive power? And what does our future hold?

'Homo Deus will shock you. It will entertain you. It will make you think in ways you had not thought before’ Daniel Kahneman, bestselling author of Thinking, Fast and Slow

") + .pages(528) + .build(); + + assertEquals(expectedDetails, bookDetails); + } +} diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksClientTest.java b/src/test/java/io/wkrzywiec/hexagonal/library/query/GoogleBookSearchClientTest.java similarity index 69% rename from src/test/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksClientTest.java rename to src/test/java/io/wkrzywiec/hexagonal/library/query/GoogleBookSearchClientTest.java index 5484714..57bad4e 100644 --- a/src/test/java/io/wkrzywiec/hexagonal/library/query/GoogleBooksClientTest.java +++ b/src/test/java/io/wkrzywiec/hexagonal/library/query/GoogleBookSearchClientTest.java @@ -11,10 +11,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest -public class GoogleBooksClientTest { +public class GoogleBookSearchClientTest { @Autowired - private GoogleBooksClient client; + private GoogleBookSearchClient client; @Test @DisplayName("Search for a book") @@ -32,13 +32,4 @@ public class GoogleBooksClientTest { JsonObject response = JsonParser.parseString(responseString).getAsJsonObject(); assertEquals(0, response.get("totalItems").getAsLong()); } - - @Test - @DisplayName("Get book details by id") - public void givenCorrectBookId_whenGetBookById_thenGetBookDetails(){ - String responseString = client.getBookById("wrOQLV6xB-wC"); - JsonObject response = JsonParser.parseString(responseString).getAsJsonObject(); - assertEquals("Harry Potter and the Sorcerer's Stone", response.getAsJsonObject("volumeInfo").getAsJsonPrimitive("title").getAsString()); - } - }