diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerService.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerService.java new file mode 100644 index 0000000000..17676b3f33 --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerService.java @@ -0,0 +1,18 @@ +package com.baeldung.webclient.json; + +import com.baeldung.webclient.json.model.Book; + +import java.util.List; + +public interface ReaderConsumerService { + + List processReaderDataFromObjectArray(); + + List processReaderDataFromReaderArray(); + + List processReaderDataFromReaderList(); + + List processNestedReaderDataFromReaderArray(); + + List processNestedReaderDataFromReaderList(); +} diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerServiceImpl.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerServiceImpl.java new file mode 100644 index 0000000000..8f1a4c019a --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/ReaderConsumerServiceImpl.java @@ -0,0 +1,90 @@ +package com.baeldung.webclient.json; + +import com.baeldung.webclient.json.model.Book; +import com.baeldung.webclient.json.model.Reader; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class ReaderConsumerServiceImpl implements ReaderConsumerService { + + private final WebClient webClient; + private static final ObjectMapper mapper = new ObjectMapper(); + + public ReaderConsumerServiceImpl(WebClient webClient) { + this.webClient = webClient; + } + @Override + public List processReaderDataFromObjectArray() { + Mono response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Object[].class).log(); + Object[] objects = response.block(); + return Arrays.stream(objects) + .map(object -> mapper.convertValue(object, Reader.class)) + .map(Reader::getFavouriteBook) + .collect(Collectors.toList()); + } + + @Override + public List processReaderDataFromReaderArray() { + Mono response = + webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Reader[].class).log(); + + Reader[] readers = response.block(); + return Arrays.stream(readers) + .map(Reader::getFavouriteBook) + .collect(Collectors.toList()); + } + + @Override + public List processReaderDataFromReaderList() { + Mono> response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}); + List readers = response.block(); + + return readers.stream() + .map(Reader::getFavouriteBook) + .collect(Collectors.toList()); + } + + @Override + public List processNestedReaderDataFromReaderArray() { + Mono response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Reader[].class).log(); + Reader[] readers = response.block(); + + return Arrays.stream(readers) + .flatMap(reader -> reader.getBooksRead().stream()) + .map(Book::getAuthor) + .collect(Collectors.toList()); + } + + @Override + public List processNestedReaderDataFromReaderList() { + Mono> response = webClient.get() + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(new ParameterizedTypeReference>() {}); + + List readers = response.block(); + return readers.stream() + .flatMap(reader -> reader.getBooksRead().stream()) + .map(Book::getAuthor) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Book.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Book.java new file mode 100644 index 0000000000..cb3fb258d4 --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Book.java @@ -0,0 +1,22 @@ +package com.baeldung.webclient.json.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Book { + private final String author; + private final String title; + + @JsonCreator + public Book( + @JsonProperty("author") String author, + @JsonProperty("title") String title) { + this.author = author; + this.title = title; + } + public String getAuthor() { + return this.author; + } +} diff --git a/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Reader.java b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Reader.java new file mode 100644 index 0000000000..7d02853ea1 --- /dev/null +++ b/spring-5-reactive-client/src/main/java/com/baeldung/webclient/json/model/Reader.java @@ -0,0 +1,33 @@ +package com.baeldung.webclient.json.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Reader { + private final int id; + private final String name; + private final Book favouriteBook; + private final List booksRead; + + @JsonCreator + public Reader( + @JsonProperty("id") int id, + @JsonProperty("name") String name, + @JsonProperty("favouriteBook") Book favouriteBook, + @JsonProperty("booksRead") List booksRead) { + this.id = id; + this.name = name; + this.favouriteBook = favouriteBook; + this.booksRead =booksRead; + } + + public Book getFavouriteBook() { + return favouriteBook; + } + + public List getBooksRead() { return booksRead; } +} diff --git a/spring-5-reactive-client/src/test/java/com/baeldung/webclient/json/ReaderConsumerServiceImplUnitTest.java b/spring-5-reactive-client/src/test/java/com/baeldung/webclient/json/ReaderConsumerServiceImplUnitTest.java new file mode 100644 index 0000000000..51f0a5c1bb --- /dev/null +++ b/spring-5-reactive-client/src/test/java/com/baeldung/webclient/json/ReaderConsumerServiceImplUnitTest.java @@ -0,0 +1,88 @@ +package com.baeldung.webclient.json; + +import com.baeldung.webclient.json.model.Book; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.hasProperty; + +public class ReaderConsumerServiceImplUnitTest { + + private static String READER_JSON = "[{\"id\":1,\"name\":\"reader1\",\"favouriteBook\":{\"author\":\"Milan Kundera\",\"title\":\"The Unbearable Lightness of Being\"}," + + "\"booksRead\":[{\"author\":\"Charles Dickens\",\"title\":\"Oliver Twist\"},{\"author\":\"Milan Kundera\",\"title\":\"The Unbearable Lightness of Being\"}]}," + + "{\"id\":2,\"name\":\"reader2\",\"favouriteBook\":{\"author\":\"Douglas Adams\",\"title\":\"The Hitchhiker\'s Guide to the Galaxy\"}," + + "\"booksRead\":[{\"author\":\"J.R.R. Tolkien\",\"title\":\"Lord of the Rings\"}, " + + "{\"author\":\"Douglas Adams\",\"title\":\"The Hitchhiker\'s Guide to the Galaxy\"}]}]"; + + private static String BASE_URL = "http://localhost:8080/readers"; + + WebClient webClientMock = WebClient.builder().baseUrl(BASE_URL) + .exchangeFunction(clientRequest -> Mono.just(ClientResponse.create(HttpStatus.OK) + .header("content-type", "application/json") + .body(READER_JSON) + .build())) + .build(); + + private final ReaderConsumerService tested = new ReaderConsumerServiceImpl(webClientMock); + + @Test + void when_processReaderDataFromObjectArray_then_OK() { + String expectedAuthor1 = "Milan Kundera"; + String expectedAuthor2 = "Douglas Adams"; + List actual = tested.processReaderDataFromObjectArray(); + assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)), + hasProperty("author", is(expectedAuthor2)))); + } + + @Test + void when_processReaderDataFromReaderArray_then_OK() { + String expectedAuthor1 = "Milan Kundera"; + String expectedAuthor2 = "Douglas Adams"; + List actual = tested.processReaderDataFromReaderArray(); + assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)), + hasProperty("author", is(expectedAuthor2)))); + } + + @Test + void when_processReaderDataFromReaderList_then_OK() { + String expectedAuthor1 = "Milan Kundera"; + String expectedAuthor2 = "Douglas Adams"; + List actual = tested.processReaderDataFromReaderList(); + assertThat(actual, hasItems(hasProperty("author", is(expectedAuthor1)), + hasProperty("author", is(expectedAuthor2)))); + + } + + @Test + void when_processNestedReaderDataFromReaderArray_then_OK() { + List expected = Arrays.asList( + "Milan Kundera", + "Charles Dickens", + "J.R.R. Tolkien", + "Douglas Adams"); + + List actual = tested.processNestedReaderDataFromReaderArray(); + assertThat(actual, hasItems(expected.get(0), expected.get(1), expected.get(2), expected.get(3))); + } + + @Test + void when_processNestedReaderDataFromReaderList_then_OK() { + List expected = Arrays.asList( + "Milan Kundera", + "Charles Dickens", + "J.R.R. Tolkien", + "Douglas Adams"); + + List actual = tested.processNestedReaderDataFromReaderList(); + assertThat(actual, hasItems(expected.get(0), expected.get(1), expected.get(2), expected.get(3))); + } +} \ No newline at end of file