From 65e801331da39026d5037e0b6143a2fe76695cb4 Mon Sep 17 00:00:00 2001 From: Thiago Araujo Date: Mon, 24 Oct 2022 19:14:05 -0300 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20adding=20integration=20to=20retriev?= =?UTF-8?q?e=20Pok=C3=A9mons=20listing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++ .../challenge/ChallengeApplication.java | 7 ++++ .../controllers/PokemonController.java | 34 +++++++++++++++++-- .../challenge/controllers/PokemonDTO.java | 27 +++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/origin/challenge/controllers/PokemonDTO.java diff --git a/build.gradle b/build.gradle index 15f8bc5..4575ac7 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,12 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.projectlombok:lombok:1.18.24' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.24' } tasks.named('test') { diff --git a/src/main/java/com/origin/challenge/ChallengeApplication.java b/src/main/java/com/origin/challenge/ChallengeApplication.java index 2db13ea..03160d3 100644 --- a/src/main/java/com/origin/challenge/ChallengeApplication.java +++ b/src/main/java/com/origin/challenge/ChallengeApplication.java @@ -2,6 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +import java.net.http.HttpClient; @SpringBootApplication public class ChallengeApplication { @@ -10,4 +13,8 @@ public static void main(String[] args) { SpringApplication.run(ChallengeApplication.class, args); } + @Bean + public HttpClient httpClient() { + return HttpClient.newHttpClient(); + } } diff --git a/src/main/java/com/origin/challenge/controllers/PokemonController.java b/src/main/java/com/origin/challenge/controllers/PokemonController.java index cf56303..a77f20a 100644 --- a/src/main/java/com/origin/challenge/controllers/PokemonController.java +++ b/src/main/java/com/origin/challenge/controllers/PokemonController.java @@ -1,18 +1,46 @@ package com.origin.challenge.controllers; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.Map; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; @RestController @RequestMapping("/pokemon") public class PokemonController { + @Autowired + private HttpClient httpClient; + @GetMapping - public ResponseEntity> root() { - return ResponseEntity.ok(Map.of("message", "Hello World")); + public ResponseEntity listPokemon(@RequestParam(name = "page", defaultValue = "1") int page) + throws Exception { + final int limit = 20; + final int offset = page - 1; + final String url = String.format("https://pokeapi.co/api/v2/pokemon?offset=%d&limit=%d", offset, limit); + + final HttpRequest r = HttpRequest.newBuilder() + .uri(new URI(url)) + .header( + "Authorization", + "eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZ" + + "SIsImV4cCI6MTY2MjA0MjMzNCwiaWF0IjoxNjYyMDQyMzM0fQ.xi3uKpbHXXxE5iTOkDrkHJfpXQhGQGjLHXwC1SE-kFI" + ) + .GET() + .build(); + final HttpResponse r2 = this.httpClient.send(r, HttpResponse.BodyHandlers.ofString()); + + final ObjectMapper objectMapper = new ObjectMapper(); + final PokemonDTO r3 = objectMapper.readValue(r2.body(), PokemonDTO.class); + + return ResponseEntity.ok(r3); } } diff --git a/src/main/java/com/origin/challenge/controllers/PokemonDTO.java b/src/main/java/com/origin/challenge/controllers/PokemonDTO.java new file mode 100644 index 0000000..6507d29 --- /dev/null +++ b/src/main/java/com/origin/challenge/controllers/PokemonDTO.java @@ -0,0 +1,27 @@ +package com.origin.challenge.controllers; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PokemonDTO { + @JsonProperty("results") + private List results; + + @Data + @NoArgsConstructor + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Type { + @JsonProperty("name") + private String name; + + @JsonProperty("url") + private String url; + } +} From 404346b14d7db17ceef83dec85e6bb7d84d8d70d Mon Sep 17 00:00:00 2001 From: Thiago Araujo Date: Mon, 24 Oct 2022 19:14:18 -0300 Subject: [PATCH 2/2] =?UTF-8?q?test:=20add=20integration=20test=20for=20Po?= =?UTF-8?q?k=C3=A9mon=20listing=20route?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PokemonControllerIntegrationTest.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/test/java/com/origin/challenge/controllers/PokemonControllerIntegrationTest.java diff --git a/src/test/java/com/origin/challenge/controllers/PokemonControllerIntegrationTest.java b/src/test/java/com/origin/challenge/controllers/PokemonControllerIntegrationTest.java new file mode 100644 index 0000000..70bc157 --- /dev/null +++ b/src/test/java/com/origin/challenge/controllers/PokemonControllerIntegrationTest.java @@ -0,0 +1,74 @@ +package com.origin.challenge.controllers; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@SuppressWarnings({"unchecked", "rawtypes"}) +public class PokemonControllerIntegrationTest { + @Mock + private HttpClient httpClient; + + @InjectMocks + private PokemonController pokemonController; + + + private static HttpResponse httpResponse; + + + @BeforeEach + public void setup() { + httpResponse = mock(HttpResponse.class); + when(httpResponse.body()).thenReturn("{\"count\":1154,\"next\":\"https://pokeapi.co/api/v2/pokemon?offset=20" + + "&limit=20\",\"previous\":null,\"results\":[{\"name\":\"bulbasaur\",\"url\":\"https://pokeapi.co/api" + + "/v2/pokemon/1/\"}]}" + ); + } + + + @Test + void test1() throws Exception { + when(httpClient.send(any(), any())).thenReturn(httpResponse); + final HttpRequest expected = HttpRequest.newBuilder() + .uri(new URI("https://pokeapi.co/api/v2/pokemon?offset=0&limit=20")) + .header( + "Authorization", + "eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZ" + + "SIsImV4cCI6MTY2MjA0MjMzNCwiaWF0IjoxNjYyMDQyMzM0fQ.xi3uKpbHXXxE5iTOkDrkHJfpXQhGQGjLHXwC1SE-kFI" + ) + .GET() + .build(); + + pokemonController.listPokemon(1); + verify(httpClient, times(1)).send(expected, HttpResponse.BodyHandlers.ofString()); + } + + @Test + void test2() throws Exception { + when(httpClient.send(any(), any())).thenReturn(httpResponse); + final HttpRequest expected = HttpRequest.newBuilder() + .uri(new URI("https://pokeapi.co/api/v2/pokemon?offset=20&limit=20")) + .header( + "Authorization", + "eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZ" + + "SIsImV4cCI6MTY2MjA0MjMzNCwiaWF0IjoxNjYyMDQyMzM0fQ.xi3uKpbHXXxE5iTOkDrkHJfpXQhGQGjLHXwC1SE-kFI" + ) + .GET() + .build(); + + pokemonController.listPokemon(2); + verify(httpClient, times(1)).send(expected, HttpResponse.BodyHandlers.ofString()); + } +}