From 34d7543ff489ce99d9f8d7cf070528c33383bf95 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sun, 13 Jun 2021 06:28:03 +0100 Subject: [PATCH 01/35] Add whitespace --- src/main/java/me/goudham/APIUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/me/goudham/APIUtils.java b/src/main/java/me/goudham/APIUtils.java index f0b7566..38d04ed 100644 --- a/src/main/java/me/goudham/APIUtils.java +++ b/src/main/java/me/goudham/APIUtils.java @@ -28,6 +28,7 @@ class APIUtils { * @param model The actual class of the given model. E.g {@link Waifu#getClass()} * @param The type of model to be returned. E.g {@link Waifu} or {@link Series} * @return {@link JavaType} of {@link PaginationData} + * */ static JavaType paginationData(Class model) { return TypeFactory.defaultInstance().constructParametricType(PaginationData.class, model); From ec08a6b232c20405f1c7169d2c6419429bf6341e Mon Sep 17 00:00:00 2001 From: Hammy Date: Sun, 13 Jun 2021 07:18:29 +0100 Subject: [PATCH 02/35] Add first test for APIWrapper --- src/main/java/me/goudham/APIWrapper.java | 12 ++- src/test/java/me/goudham/APIWrapperTest.java | 108 +++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/test/java/me/goudham/APIWrapperTest.java diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index a7fca8a..7af966a 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -36,7 +36,7 @@ import static me.goudham.APIUtils.paginationData; public class APIWrapper { private final String version = "1.0"; private static final String host = "https://mywaifulist.moe/api/v1/"; - private final String apiKey; + private String apiKey; private final APIMapper apiMapper; private final HttpClient httpClient; @@ -67,7 +67,7 @@ public class APIWrapper { * cannot be decoded or the thread was interrupted while waiting to receive the data * */ - private Result sendRequest(String param) throws APIResponseException { + Result sendRequest(String param) throws APIResponseException { CompletableFuture futureResult = CompletableFuture.supplyAsync(() -> { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(host + param)) @@ -329,4 +329,12 @@ public class APIWrapper { Result userProfileResult = sendRequest("user/" + userId + "/lists/" + listId); return apiMapper.deserialize(userProfileResult, UserList.class); } + + void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + String getApiKey() { + return apiKey; + } } diff --git a/src/test/java/me/goudham/APIWrapperTest.java b/src/test/java/me/goudham/APIWrapperTest.java new file mode 100644 index 0000000..7f10a8c --- /dev/null +++ b/src/test/java/me/goudham/APIWrapperTest.java @@ -0,0 +1,108 @@ +package me.goudham; + +import me.goudham.exception.APIResponseException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import javax.net.ssl.SSLSession; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.Optional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.*; + +class APIWrapperTest { + + @Mock + private + HttpClient httpClient; + + private APIWrapper sut; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + sut = new APIWrapper("ValidAPIKey", httpClient); + } + + @Test + void successfullyReturn400WhenApiTokenInvalid() throws APIResponseException, IOException, InterruptedException { + sut.setApiKey("InvalidAPIKey"); + HttpRequest expectedHttpRequest = buildHttpRequest(sut); + HttpResponse expectedHttpResponse = build400HttpResponse(); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + doReturn(HttpClient.Version.HTTP_2).when(httpClient).version(); + + Result actualResult = sut.sendRequest("waifu/1"); + + assertThat(actualResult.getStatusCode(), is(400)); + assertThat(actualResult.getBody(), is("{\"message\":\"Access denied - please check your token\",\"code\":400}")); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verify(httpClient, times(1)).version(); + verifyNoMoreInteractions(httpClient); + } + + private HttpRequest buildHttpRequest(APIWrapper sut) { + return HttpRequest.newBuilder() + .uri(URI.create("https://mywaifulist.moe/api/v1/waifu/1")) + .version(HttpClient.Version.HTTP_2) + .timeout(Duration.ofSeconds(20)) + .headers("Content-Type", "application/json", "apikey", sut.getApiKey()) + .GET() + .build(); + } + + private HttpResponse build400HttpResponse() { + return new HttpResponse<>() { + @Override + public int statusCode() { + return 400; + } + + @Override + public HttpRequest request() { + return null; + } + + @Override + public Optional> previousResponse() { + return Optional.empty(); + } + + @Override + public HttpHeaders headers() { + return null; + } + + @Override + public String body() { + return "{\"message\":\"Access denied - please check your token\",\"code\":400}"; + } + + @Override + public Optional sslSession() { + return Optional.empty(); + } + + @Override + public URI uri() { + return null; + } + + @Override + public HttpClient.Version version() { + return null; + } + }; + } +} \ No newline at end of file From 393350f218b2a88090d5271be06657e81e9f2a0b Mon Sep 17 00:00:00 2001 From: Hammy Date: Sun, 13 Jun 2021 07:18:46 +0100 Subject: [PATCH 03/35] Update pom.xml Add hamcrest dependency for testing --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index b61a18a..d107163 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,12 @@ 3.10.0 test + + org.hamcrest + hamcrest-all + 1.3 + test + org.jetbrains annotations From acf85b386c46f5396783a5bf1703239f8a8d7987 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sun, 13 Jun 2021 07:23:21 +0100 Subject: [PATCH 04/35] Update README.md Display badges based on main branch instead of release --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 615cabc..0427cad 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [license]: https://img.shields.io/github/license/sgoudham/MyWaifuWrapper [maven-central]: https://img.shields.io/maven-central/v/me.goudham/MyWaifuWrapper -[build-status]: https://goudham.me/jenkins/job/MyWaifuWrapper/job/release/badge/icon -[codecov]: https://codecov.io/gh/sgoudham/MyWaifuWrapper/branch/release/graph/badge.svg?token=RxUDnCWnF0 +[build-status]: https://goudham.me/jenkins/job/MyWaifuWrapper/job/main/badge/icon +[codecov]: https://codecov.io/gh/sgoudham/MyWaifuWrapper/branch/main/graph/badge.svg?token=RxUDnCWnF0 [issues]: https://img.shields.io/github/issues/sgoudham/MyWaifuWrapper?label=issues [pull-requests]: https://img.shields.io/github/issues-pr/sgoudham/MyWaifuWrapper [fossa]: https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsgoudham%2FMyWaifuWrapper.svg?type=shield From cd5d40193d387c4a0d8ef47db5fcd882c6ba5c58 Mon Sep 17 00:00:00 2001 From: Hammy Date: Mon, 14 Jun 2021 01:57:59 +0100 Subject: [PATCH 05/35] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0427cad..a3cf6e9 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,11 @@ import java.time.Duration; public class Main { private static void main(String[] args) { - // Bare Minimum (Would recommend using createDefault()) + // Bare Minimum Config + // (Would recommend using createDefault()) MyWaifuClient myWaifuClient = new MyWaifuClient.Builder("apiKey").build(); - // Creating MyWaifuClient through Builder + // Creation Through Builder MyWaifuClient myWaifuClient = new MyWaifuClient.Builder("apiKey") .withVersion(HttpClient.Version.HTTP_2) .withConnectTimeout(Duration.ofMinutes(10)) @@ -77,7 +78,7 @@ public class Main { TODO -# Download +# Installation Latest Stable Version: ![maven-central]

Be sure to replace the **VERSION** key below with the one of the versions shown above!

From a640781deb48ee9ed29a49e244571f8b873d3eca Mon Sep 17 00:00:00 2001 From: Hammy Date: Mon, 14 Jun 2021 02:06:15 +0100 Subject: [PATCH 06/35] Add method to support retrieving objects as strings --- src/main/java/me/goudham/APIMapper.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/me/goudham/APIMapper.java b/src/main/java/me/goudham/APIMapper.java index e7d460d..a78ade4 100644 --- a/src/main/java/me/goudham/APIMapper.java +++ b/src/main/java/me/goudham/APIMapper.java @@ -22,6 +22,14 @@ class APIMapper { objectMapper = new ObjectMapper(); } + String getValueAsString(Object obj) throws APIMapperException { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException jpe) { + throw new APIMapperException(jpe.getMessage(), jpe); + } + } + /** * Using the given {@code model}, {@link ObjectMapper} deserializes the given Json * into a Java POJO From 786c6990cb748b8c4d690696168f3d0d2cd0ee0f Mon Sep 17 00:00:00 2001 From: Hammy Date: Mon, 14 Jun 2021 02:06:31 +0100 Subject: [PATCH 07/35] Update tests --- src/test/java/me/goudham/APIWrapperTest.java | 42 ++++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/test/java/me/goudham/APIWrapperTest.java b/src/test/java/me/goudham/APIWrapperTest.java index 7f10a8c..22fd00b 100644 --- a/src/test/java/me/goudham/APIWrapperTest.java +++ b/src/test/java/me/goudham/APIWrapperTest.java @@ -36,37 +36,53 @@ class APIWrapperTest { @Test void successfullyReturn400WhenApiTokenInvalid() throws APIResponseException, IOException, InterruptedException { + HttpRequest expectedHttpRequest = buildHttpRequest("InvalidAPIKey"); + int expectedStatusCode = 400; + String expectedBody = "{\"message\":\"Access denied - please check your token\",\"code\":400}"; + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + sut.setApiKey("InvalidAPIKey"); - HttpRequest expectedHttpRequest = buildHttpRequest(sut); - HttpResponse expectedHttpResponse = build400HttpResponse(); + Result actualResult = sut.sendGetRequest("waifu/1"); + + assertThat(actualResult.getStatusCode(), is(expectedStatusCode)); + assertThat(actualResult.getBody(), is(expectedBody)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void successfullyReturn200WhenApiTokenValid() throws APIResponseException, IOException, InterruptedException { + HttpRequest expectedHttpRequest = buildHttpRequest("ValidAPIKey"); + int expectedStatusCode = 200; + String expectedBody = "{\"message\":\"Token Valid\",\"code\":200}"; + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - doReturn(HttpClient.Version.HTTP_2).when(httpClient).version(); - Result actualResult = sut.sendRequest("waifu/1"); + Result actualResult = sut.sendGetRequest("waifu/1"); - assertThat(actualResult.getStatusCode(), is(400)); - assertThat(actualResult.getBody(), is("{\"message\":\"Access denied - please check your token\",\"code\":400}")); + assertThat(actualResult.getStatusCode(), is(expectedStatusCode)); + assertThat(actualResult.getBody(), is(expectedBody)); verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - verify(httpClient, times(1)).version(); verifyNoMoreInteractions(httpClient); } - private HttpRequest buildHttpRequest(APIWrapper sut) { + private HttpRequest buildHttpRequest(String apiKey) { return HttpRequest.newBuilder() .uri(URI.create("https://mywaifulist.moe/api/v1/waifu/1")) - .version(HttpClient.Version.HTTP_2) .timeout(Duration.ofSeconds(20)) - .headers("Content-Type", "application/json", "apikey", sut.getApiKey()) + .headers("Content-Type", "application/json", "apikey", apiKey) .GET() .build(); } - private HttpResponse build400HttpResponse() { + private HttpResponse buildHttpResponse(int statusCode, String body) { return new HttpResponse<>() { @Override public int statusCode() { - return 400; + return statusCode; } @Override @@ -86,7 +102,7 @@ class APIWrapperTest { @Override public String body() { - return "{\"message\":\"Access denied - please check your token\",\"code\":400}"; + return body; } @Override From eb45813c7f25d4f8b4dade887f195a0a61edba4f Mon Sep 17 00:00:00 2001 From: Hammy Date: Mon, 14 Jun 2021 02:06:52 +0100 Subject: [PATCH 08/35] Add endpoint support for searching Waifus --- src/main/java/me/goudham/APIWrapper.java | 73 +++++++++++++-------- src/main/java/me/goudham/MyWaifuClient.java | 4 ++ 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index 7af966a..c55b9ba 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -20,6 +20,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -57,28 +58,39 @@ public class APIWrapper { apiMapper = new APIMapper(); } + private HttpRequest.Builder getBaseRequest(String param) { + return HttpRequest.newBuilder() + .uri(URI.create(host + param)) + .timeout(Duration.ofSeconds(20)) + .headers("Content-Type", "application/json", "apikey", apiKey); + } + + Result sendGetRequest(String param) throws APIResponseException { + HttpRequest request = getBaseRequest(param).GET().build(); + return sendRequest(request); + } + + private Result sendPostRequest(String param, Map headers) throws APIResponseException, APIMapperException { + HttpRequest request = getBaseRequest(param) + .POST(HttpRequest.BodyPublishers.ofString(apiMapper.getValueAsString(headers))) + .build(); + return sendRequest(request); + } + /** * Handles sending a request to the API asynchronously using {@link HttpRequest} * and the underlying {@link HttpClient} * - * @param param The end of the endpoint appended onto the host + * @param httpRequest The {@link HttpRequest} to be sent by the {@link HttpClient} * @return {@link Result} * @throws APIResponseException If the {@link CompletableFuture Response} * cannot be decoded or the thread was interrupted while waiting to receive the data * */ - Result sendRequest(String param) throws APIResponseException { + private Result sendRequest(HttpRequest httpRequest) throws APIResponseException { CompletableFuture futureResult = CompletableFuture.supplyAsync(() -> { - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(host + param)) - .version(httpClient.version()) - .timeout(Duration.ofSeconds(20)) - .headers("Content-Type", "application/json", "apikey", apiKey) - .GET() - .build(); - try { - return httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + return httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); } catch (IOException | InterruptedException exp) { exp.printStackTrace(); } @@ -102,7 +114,7 @@ public class APIWrapper { * */ Response getWaifu(String waifuId) throws APIResponseException, APIMapperException { - Result waifuResult = sendRequest("waifu/" + waifuId); + Result waifuResult = sendGetRequest("waifu/" + waifuId); return apiMapper.deserialize(waifuResult, Waifu.class); } @@ -117,7 +129,7 @@ public class APIWrapper { * */ Response> getWaifuImages(String waifuId, String pageNum) throws APIResponseException, APIMapperException { - Result waifuImagesResult = sendRequest("waifu/" + waifuId + "/images?page=" + pageNum); + Result waifuImagesResult = sendGetRequest("waifu/" + waifuId + "/images?page=" + pageNum); return apiMapper.deserializeToPaginationData(waifuImagesResult, paginationData(WaifuImage.class)); } @@ -131,7 +143,7 @@ public class APIWrapper { * */ Response> getWaifusByPage(String pageNum) throws APIResponseException, APIMapperException { - Result waifusByPageResult = sendRequest("waifu?page=" + pageNum); + Result waifusByPageResult = sendGetRequest("waifu?page=" + pageNum); return apiMapper.deserializeToPaginationData(waifusByPageResult, paginationData(FilteredWaifu.class)); } @@ -144,7 +156,7 @@ public class APIWrapper { * */ Response getDailyWaifu() throws APIResponseException, APIMapperException { - Result dailyWaifuResult = sendRequest("meta/daily"); + Result dailyWaifuResult = sendGetRequest("meta/daily"); return apiMapper.deserialize(dailyWaifuResult, FilteredWaifu.class); } @@ -157,7 +169,7 @@ public class APIWrapper { * */ Response getRandomWaifu() throws APIResponseException, APIMapperException { - Result randomWaifuResult = sendRequest("meta/random"); + Result randomWaifuResult = sendGetRequest("meta/random"); return apiMapper.deserialize(randomWaifuResult, FilteredWaifu.class); } @@ -170,7 +182,7 @@ public class APIWrapper { * */ Response> getSeasonalAnime() throws APIResponseException, APIMapperException { - Result seasonalAnimeResult = sendRequest("airing"); + Result seasonalAnimeResult = sendGetRequest("airing"); return apiMapper.deserializeToList(seasonalAnimeResult, listOf(FilteredSeries.class)); } @@ -183,7 +195,7 @@ public class APIWrapper { * */ Response> getBestWaifus() throws APIResponseException, APIMapperException { - Result waifuResults = sendRequest("airing/best"); + Result waifuResults = sendGetRequest("airing/best"); return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); } @@ -197,7 +209,7 @@ public class APIWrapper { * */ Response> getPopularWaifus() throws APIResponseException, APIMapperException { - Result waifuResults = sendRequest("airing/popular"); + Result waifuResults = sendGetRequest("airing/popular"); return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); } @@ -210,7 +222,7 @@ public class APIWrapper { * */ Response> getTrashWaifus() throws APIResponseException, APIMapperException { - Result waifuResults = sendRequest("airing/trash"); + Result waifuResults = sendGetRequest("airing/trash"); return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); } @@ -224,7 +236,7 @@ public class APIWrapper { * */ Response getSeries(String seriesId) throws APIResponseException, APIMapperException { - Result seriesResult = sendRequest("series/" + seriesId); + Result seriesResult = sendGetRequest("series/" + seriesId); return apiMapper.deserialize(seriesResult, Series.class); } @@ -238,7 +250,7 @@ public class APIWrapper { * */ Response> getSeriesByPage(String pageNum) throws APIResponseException, APIMapperException { - Result seriesPageResult = sendRequest("series?page=" + pageNum); + Result seriesPageResult = sendGetRequest("series?page=" + pageNum); return apiMapper.deserializeToPaginationData(seriesPageResult, paginationData(FilteredSeries.class)); } @@ -253,7 +265,7 @@ public class APIWrapper { * */ Response> getAllSeries(Season season, Integer year) throws APIResponseException, APIMapperException { - Result allSeriesResult = sendRequest("airing/" + season.getSeason() + "/" + year); + Result allSeriesResult = sendGetRequest("airing/" + season.getSeason() + "/" + year); return apiMapper.deserializeToList(allSeriesResult, listOf(FilteredSeries.class)); } @@ -267,7 +279,7 @@ public class APIWrapper { * */ Response> getSeriesWaifus(String seriesId) throws APIResponseException, APIMapperException { - Result allWaifusFromSeriesResults = sendRequest("series/" + seriesId + "/waifus"); + Result allWaifusFromSeriesResults = sendGetRequest("series/" + seriesId + "/waifus"); return apiMapper.deserializeToList(allWaifusFromSeriesResults, listOf(FilteredWaifu.class)); } @@ -281,7 +293,7 @@ public class APIWrapper { * */ Response getUserProfile(String userId) throws APIResponseException, APIMapperException { - Result userProfileResult = sendRequest("user/" + userId); + Result userProfileResult = sendGetRequest("user/" + userId); return apiMapper.deserialize(userProfileResult, User.class); } @@ -297,7 +309,7 @@ public class APIWrapper { * */ Response> getUserWaifus(String userId, String listType, String pageNum) throws APIResponseException, APIMapperException { - Result userWaifusResult = sendRequest("user/" + userId + "/" + listType + "?page=" + pageNum); + Result userWaifusResult = sendGetRequest("user/" + userId + "/" + listType + "?page=" + pageNum); return apiMapper.deserializeToPaginationData(userWaifusResult, paginationData(FilteredWaifu.class)); } @@ -311,7 +323,7 @@ public class APIWrapper { * */ Response> getUserLists(String userId) throws APIResponseException, APIMapperException { - Result userProfileResult = sendRequest("user/" + userId + "/lists"); + Result userProfileResult = sendGetRequest("user/" + userId + "/lists"); return apiMapper.deserializeToList(userProfileResult, listOf(UserList.class)); } @@ -326,10 +338,15 @@ public class APIWrapper { * */ Response getUserList(String userId, String listId) throws APIResponseException, APIMapperException { - Result userProfileResult = sendRequest("user/" + userId + "/lists/" + listId); + Result userProfileResult = sendGetRequest("user/" + userId + "/lists/" + listId); return apiMapper.deserialize(userProfileResult, UserList.class); } + Response> searchWaifus(String waifuName) throws APIMapperException, APIResponseException { + Result searchWaifusResult = sendPostRequest("search/waifus", Map.of("term", waifuName)); + return apiMapper.deserializeToList(searchWaifusResult, listOf(FilteredWaifu.class)); + } + void setApiKey(String apiKey) { this.apiKey = apiKey; } diff --git a/src/main/java/me/goudham/MyWaifuClient.java b/src/main/java/me/goudham/MyWaifuClient.java index 95b811d..a77a77d 100644 --- a/src/main/java/me/goudham/MyWaifuClient.java +++ b/src/main/java/me/goudham/MyWaifuClient.java @@ -328,6 +328,10 @@ public class MyWaifuClient { return APIWrapper.getUserList(String.valueOf(userId), String.valueOf(listId)); } + public Response> searchWaifus(@NotNull String name) throws APIMapperException, APIResponseException { + return APIWrapper.searchWaifus(name); + } + /** * Builder for {@link MyWaifuClient} * From 92002c18e457be197c09c20de4a9e0ba7c83d877 Mon Sep 17 00:00:00 2001 From: Hammy Date: Tue, 15 Jun 2021 01:45:58 +0100 Subject: [PATCH 09/35] Add/Update documentation --- src/main/java/me/goudham/APIMapper.java | 14 +++++++++++- src/main/java/me/goudham/APIWrapper.java | 27 +++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/goudham/APIMapper.java b/src/main/java/me/goudham/APIMapper.java index a78ade4..12d0bdb 100644 --- a/src/main/java/me/goudham/APIMapper.java +++ b/src/main/java/me/goudham/APIMapper.java @@ -22,7 +22,15 @@ class APIMapper { objectMapper = new ObjectMapper(); } - String getValueAsString(Object obj) throws APIMapperException { + /** + * Convert any object passed and return as a {@link String} + * + * @param obj {@link Object} to write as {@link String} + * @return {@link String} + * @throws APIMapperException If {@link ObjectMapper} is not able to serialize object into {@link String} + * + */ + String getObjectAsString(Object obj) throws APIMapperException { try { return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException jpe) { @@ -67,6 +75,7 @@ class APIMapper { * @param The type of model to be returned. E.g {@link Waifu} or {@link Series} * @return {@link Response} * @throws APIMapperException If {@link ObjectMapper} is not able to deserialize JSON to Java POJO properly + * */ Response> deserializeToList(Result result, JavaType model) throws APIMapperException { Integer statusCode = result.getStatusCode(); @@ -94,6 +103,7 @@ class APIMapper { * @param The type of model to be returned. E.g {@link Waifu} or {@link Series} * @return {@link Response} * @throws APIMapperException If {@link ObjectMapper} is not able to deserialize JSON to Java POJO properly + * */ Response> deserializeToPaginationData(Result result, JavaType model) throws APIMapperException { Integer statusCode = result.getStatusCode(); @@ -119,6 +129,7 @@ class APIMapper { * @return {@link String} — The proper json data to deserialize * @throws JsonProcessingException If {@link ObjectMapper} is not able to * read the given {@code jsonBody} + * */ private String getData(String jsonBody) throws JsonProcessingException { JsonNode parent = objectMapper.readTree(jsonBody); @@ -132,6 +143,7 @@ class APIMapper { * * @param throwable Type of throwable to throw * @throws APIMapperException Purpose of the method + * */ private void throwAPIMapperException(Throwable throwable) throws APIMapperException { String customExceptionMessage = "If you are seeing this message, " + diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index c55b9ba..6ef4eae 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -58,6 +58,13 @@ public class APIWrapper { apiMapper = new APIMapper(); } + /** + * Create base {@link HttpRequest.Builder} with custom url, default headers and timeout + * + * @param param The end of the endpoint appended onto the host + * @return {@link HttpRequest.Builder} + * + */ private HttpRequest.Builder getBaseRequest(String param) { return HttpRequest.newBuilder() .uri(URI.create(host + param)) @@ -65,14 +72,32 @@ public class APIWrapper { .headers("Content-Type", "application/json", "apikey", apiKey); } + /** + * Separate method for sending GET requests + * + * @param param The end of the endpoint appended onto the host + * @return {@link Result} + * @throws APIResponseException If {@link #sendRequest(HttpRequest)} cannot retrieve the proper data from the API + * + */ Result sendGetRequest(String param) throws APIResponseException { HttpRequest request = getBaseRequest(param).GET().build(); return sendRequest(request); } + /** + * Separate method for sending POST requests + * + * @param param The end of the endpoint appended onto the host + * @param headers Headers as Key/Value pairs for POST requests + * @return {@link Result} + * @throws APIResponseException If {@link #sendRequest(HttpRequest)} cannot retrieve the proper data from the API + * @throws APIMapperException If {@link APIMapper#getObjectAsString(Object)} cannot properly serialize object + * + */ private Result sendPostRequest(String param, Map headers) throws APIResponseException, APIMapperException { HttpRequest request = getBaseRequest(param) - .POST(HttpRequest.BodyPublishers.ofString(apiMapper.getValueAsString(headers))) + .POST(HttpRequest.BodyPublishers.ofString(apiMapper.getObjectAsString(headers))) .build(); return sendRequest(request); } From e8bced78cb55f994164c7260d77a2bcfe8b12f31 Mon Sep 17 00:00:00 2001 From: Hammy Date: Tue, 15 Jun 2021 02:40:58 +0100 Subject: [PATCH 10/35] Add endpoint support for searchSeries & Update documentation --- src/main/java/me/goudham/APIWrapper.java | 23 +++++++++++++++++++++ src/main/java/me/goudham/MyWaifuClient.java | 22 ++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index 6ef4eae..c8225d5 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -367,11 +367,34 @@ public class APIWrapper { return apiMapper.deserialize(userProfileResult, UserList.class); } + /** + * Retrieve a List of {@link FilteredWaifu}'s given a query, by sending POST request to API + * + * @param waifuName The name of the Waifu + * @return {@link Response} of {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + * + */ Response> searchWaifus(String waifuName) throws APIMapperException, APIResponseException { Result searchWaifusResult = sendPostRequest("search/waifus", Map.of("term", waifuName)); return apiMapper.deserializeToList(searchWaifusResult, listOf(FilteredWaifu.class)); } + /** + * Retrieve a List of {@link FilteredSeries}'s given a query, by sending POST request to API + * + * @param seriesName The name of the Series + * @return {@link Response} of {@link FilteredSeries} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + * + */ + Response> searchSeries(String seriesName) throws APIMapperException, APIResponseException { + Result searchSeriesResult = sendPostRequest("search/series", Map.of("term", seriesName)); + return apiMapper.deserializeToList(searchSeriesResult, listOf(FilteredSeries.class)); + } + void setApiKey(String apiKey) { this.apiKey = apiKey; } diff --git a/src/main/java/me/goudham/MyWaifuClient.java b/src/main/java/me/goudham/MyWaifuClient.java index a77a77d..130d345 100644 --- a/src/main/java/me/goudham/MyWaifuClient.java +++ b/src/main/java/me/goudham/MyWaifuClient.java @@ -328,10 +328,32 @@ public class MyWaifuClient { return APIWrapper.getUserList(String.valueOf(userId), String.valueOf(listId)); } + /** + * Searches only Waifu's using a given query. The higher the relevance, the better the match + * + * @param name The name of the Waifu + * @return {@link Response} of {@link FilteredWaifu} + * @throws APIMapperException If {@link APIWrapper} could not return information properly + * @throws APIResponseException If {@link APIMapper} could not correctly {@code deserialize} model + * + */ public Response> searchWaifus(@NotNull String name) throws APIMapperException, APIResponseException { return APIWrapper.searchWaifus(name); } + /** + * Searches only Series' using a given query. The higher the relevance, the better the match + * + * @param name The name of the Series + * @return {@link Response} of {@link FilteredSeries} + * @throws APIMapperException If {@link APIWrapper} could not return information properly + * @throws APIResponseException If {@link APIMapper} could not correctly {@code deserialize} model + * + */ + public Response> searchSeries(@NotNull String name) throws APIMapperException, APIResponseException { + return APIWrapper.searchSeries(name); + } + /** * Builder for {@link MyWaifuClient} * From 2525bbe7bbe0da4ed3f842f29307216e73c32c12 Mon Sep 17 00:00:00 2001 From: Hamothy <58985301+sgoudham@users.noreply.github.com> Date: Tue, 15 Jun 2021 02:47:20 +0100 Subject: [PATCH 11/35] Create dependabot.yml --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..76e22be --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "maven" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" From 2d7ff995b03cde74ae4a5900d6302c0ed3872706 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 01:47:42 +0000 Subject: [PATCH 12/35] Bump maven-source-plugin from 3.2.0 to 3.2.1 Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.0...maven-source-plugin-3.2.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d107163..19c977c 100644 --- a/pom.xml +++ b/pom.xml @@ -108,7 +108,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.0 + 3.2.1 attach-sources From 7528982ac90156af77208531fe2b768b62e3f871 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 01:47:44 +0000 Subject: [PATCH 13/35] Bump maven-javadoc-plugin from 3.2.0 to 3.3.0 Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.2.0...maven-javadoc-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d107163..19c413e 100644 --- a/pom.xml +++ b/pom.xml @@ -121,7 +121,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.2.0 + 3.3.0 attach-javadocs From 0eb2eb469218b975bffb2df2bbb5579b1512d1e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jun 2021 01:47:49 +0000 Subject: [PATCH 14/35] Bump mockito-core from 3.10.0 to 3.11.1 Bumps [mockito-core](https://github.com/mockito/mockito) from 3.10.0 to 3.11.1. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.10.0...v3.11.1) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d107163..027d5e5 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ org.mockito mockito-core - 3.10.0 + 3.11.1 test From 90f23bd14f3c38ff28eba0cecd46d2a3c6c692f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jun 2021 04:22:45 +0000 Subject: [PATCH 15/35] Bump mockito-core from 3.11.1 to 3.11.2 Bumps [mockito-core](https://github.com/mockito/mockito) from 3.11.1 to 3.11.2. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.11.1...v3.11.2) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e21d2a0..02ace9d 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ org.mockito mockito-core - 3.11.1 + 3.11.2 test From c61bcf845164d7fa8355c694444803f1300a1ccd Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 2 Jul 2021 01:45:57 +0100 Subject: [PATCH 16/35] Update README.md --- README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a3cf6e9..355ca84 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,11 @@ the data returned **may not be** fully complete and at its best quality # Configuration -## Creating The MyWaifuClient - -There are 2 ways to create the [MyWaifuClient](https://github.com/sgoudham/MyWaifuWrapper/blob/main/src/main/java/me/goudham/MyWaifuClient.java) +There are 2 ways to create and configure the [MyWaifuClient](https://github.com/sgoudham/MyWaifuWrapper/blob/main/src/main/java/me/goudham/MyWaifuClient.java) + `createDefault(apiKey)` + `build()` -### createDefault() +## createDefault() `createDefault(apiKey)` will provide a default implementation and return a MyWaifuClient ready to be used. Only the `apiKey` is required to instantiate MyWaifuClient. @@ -47,10 +45,10 @@ public class Main { } ``` -### build() +## build() `build()` is used to build the object from the ground up, allowing for the fine-tuning of properties within the -MyWaifuClient. Not all the additional properties need to specified within the builder but the bare minimum would be +MyWaifuClient. Not all the additional properties need to specified within the builder. However, the bare minimum would be the `apiKey` within the Builder constructor and then `.build()` ```java @@ -76,7 +74,49 @@ public class Main { # Usage -TODO +Once **MyWaifuClient** has been configured properly, many methods are available for you to get the data that you wish. +Each method links up to an endpoint that is listed within the [MyWaifuList API Docs](https://mywaifulist.docs.stoplight.io/api-reference) + +With every method executed from the **MyWaifuClient**, a [Response]() object will be returned. The object will house +the type of entity, response status code, and the response body. This allows for extreme flexibility as the raw response +and marshalled entity are available to the user. + +The documentation for each method provides clear and detailed information on what arguments to pass in, please +do also refer to that. + +Shown below are some examples of how you can retrieve data: + +```java +public class Main { + public static void main(String[] args) throws APIMapperException, APIResponseException { + // ... myWaifuClient has been instantiated + + // getWaifu(Integer id) + Response waifuResponse = myWaifuClient.getWaifu(7); + Integer waifuResponseCode = waifuResponse.getStatusCode(); + String waifuResponseBody = waifuResponse.getBody(); + Waifu waifu = waifuResponse.getModel(); + + // getRandomWaifu() + Response randomWaifuResponse = myWaifuClient.getRandomWaifu(); + Integer randomWaifuResponseCode = randomWaifuResponse.getStatusCode(); + String randomWaifuResponseBody = randomWaifuResponse.getBody(); + FilteredWaifu randomWaifu = randomWaifuResponse.model(); + + // getSeasonalAnime() + Response> seasonalAnimeResponse = myWaifuClient.getSeasonalAnime(); + Integer seasonalAnimeResponseStatusCode = seasonalAnimeResponse.getStatusCode(); + String seasonalAnimeResponseBody = seasonalAnimeResponse.getBody(); + List seasonalAnime = seasonalAnimeResponse.getModel(); + + // getUserWaifus(Integer id, WaifuListType waifuListType, Integer pageNum) + Response> userWaifusLikedResponse = myWaifuClient.getUserWaifus(1, WaifuListType.LIKED, 1); + Integer userWaifusLikedResponseStatusCode = userWaifusLikedResponse.getStatusCode(); + String userWaifusLikedResponseBody = userWaifusLikedResponse.getBody(); + PaginationData userWaifusLiked = userWaifusLikedResponse.getModel(); + } +} +``` # Installation From 36a940c4a61ac5e3d2c51e8a582c089506d112cf Mon Sep 17 00:00:00 2001 From: Hamothy <58985301+sgoudham@users.noreply.github.com> Date: Fri, 2 Jul 2021 01:58:18 +0100 Subject: [PATCH 17/35] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 355ca84..c6d6ef0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ # Summary -This is an Asynchronous API Wrapper for [MyWaifuList](https://mywaifulist.moe/dash) +This is an Unofficial Asynchronous API Wrapper for [MyWaifuList](https://mywaifulist.moe/dash) # Disclaimer From 87b2b8025c8a06d6edcca80eead8d05acaa5ae54 Mon Sep 17 00:00:00 2001 From: Hamothy <58985301+sgoudham@users.noreply.github.com> Date: Fri, 2 Jul 2021 01:58:43 +0100 Subject: [PATCH 18/35] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 02ace9d..f388b98 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ jar MyWaifuWrapper - An Asynchronous Java API Wrapper for MyWaifuList. + An Unofficial Asynchronous Java API Wrapper for MyWaifuList. https://github.com/sgoudham/MyWaifuWrapper From de1f2fc1abe48e4bf1f3dc9259672af2e8667600 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 01:15:17 +0100 Subject: [PATCH 19/35] Add new endpoint - /search/beta --- src/main/java/me/goudham/APIWrapper.java | 18 ++++++++++++++++-- src/main/java/me/goudham/MyWaifuClient.java | 13 +++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index c8225d5..981e9a5 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -367,11 +367,25 @@ public class APIWrapper { return apiMapper.deserialize(userProfileResult, UserList.class); } + /** + * Retrieve a {@link List} of {@link FilteredWaifu} with a search term by sending a POST request to the API + * + * @param searchString {@link String} that should be searched for + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + * + */ + Response> betaSearch(String searchString) throws APIMapperException, APIResponseException { + Result betaSearchResult = sendPostRequest("search/beta", Map.of("term", searchString)); + return apiMapper.deserializeToList(betaSearchResult, listOf(FilteredWaifu.class)); + } + /** * Retrieve a List of {@link FilteredWaifu}'s given a query, by sending POST request to API * * @param waifuName The name of the Waifu - * @return {@link Response} of {@link FilteredWaifu} + * @return {@link Response} of {@link List} with {@link FilteredWaifu} * @throws APIResponseException If {@link APIWrapper} could not return information properly * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model * @@ -385,7 +399,7 @@ public class APIWrapper { * Retrieve a List of {@link FilteredSeries}'s given a query, by sending POST request to API * * @param seriesName The name of the Series - * @return {@link Response} of {@link FilteredSeries} + * @return {@link Response} of {@link List} with {@link FilteredSeries} * @throws APIResponseException If {@link APIWrapper} could not return information properly * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model * diff --git a/src/main/java/me/goudham/MyWaifuClient.java b/src/main/java/me/goudham/MyWaifuClient.java index 130d345..35b1d16 100644 --- a/src/main/java/me/goudham/MyWaifuClient.java +++ b/src/main/java/me/goudham/MyWaifuClient.java @@ -328,6 +328,19 @@ public class MyWaifuClient { return APIWrapper.getUserList(String.valueOf(userId), String.valueOf(listId)); } + /** + * Allows searching for a {@link Waifu} + * This search is more aggressive when it comes to name matching, resulting in better accuracy in most cases + * + * @param searchString {@link String} that should be searched for + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + public Response> betaSearch(@NotNull String searchString) throws APIResponseException, APIMapperException { + return APIWrapper.betaSearch(searchString); + } + /** * Searches only Waifu's using a given query. The higher the relevance, the better the match * From a5365bea8b802d70acc6b33d9c59a32ba722a97e Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 03:10:10 +0100 Subject: [PATCH 20/35] Remove java "Generated" annotation --- src/main/java/me/goudham/domain/waifu/Waifu.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/me/goudham/domain/waifu/Waifu.java b/src/main/java/me/goudham/domain/waifu/Waifu.java index 12711a4..30ca185 100644 --- a/src/main/java/me/goudham/domain/waifu/Waifu.java +++ b/src/main/java/me/goudham/domain/waifu/Waifu.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.*; import me.goudham.domain.series.Series; import me.goudham.domain.user.Creator; -import javax.annotation.processing.Generated; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -659,7 +658,6 @@ public class Waifu { *

Common in Japanese culture as a potential representation of personality types

* */ - @Generated("jsonschema2pojo") public enum BloodType { A("A"), @@ -668,7 +666,7 @@ public class Waifu { AB("AB"); private final String value; - private final static Map CONSTANTS = new HashMap(); + private final static Map CONSTANTS = new HashMap<>(); static { for (Waifu.BloodType bloodType: values()) { From e97075341a43143199616fe7488bca21f2516c92 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 06:35:48 +0100 Subject: [PATCH 21/35] Reformat code --- src/test/java/me/goudham/APIWrapperTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/me/goudham/APIWrapperTest.java b/src/test/java/me/goudham/APIWrapperTest.java index 22fd00b..fc35e55 100644 --- a/src/test/java/me/goudham/APIWrapperTest.java +++ b/src/test/java/me/goudham/APIWrapperTest.java @@ -23,8 +23,7 @@ import static org.mockito.Mockito.*; class APIWrapperTest { @Mock - private - HttpClient httpClient; + private HttpClient httpClient; private APIWrapper sut; From 2785f887158bcc4cadaf3e06c12969b5c8a0d1f8 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 06:36:23 +0100 Subject: [PATCH 22/35] Make constructor private & add setter for APIWrapper --- src/main/java/me/goudham/MyWaifuClient.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/goudham/MyWaifuClient.java b/src/main/java/me/goudham/MyWaifuClient.java index 35b1d16..09df518 100644 --- a/src/main/java/me/goudham/MyWaifuClient.java +++ b/src/main/java/me/goudham/MyWaifuClient.java @@ -32,7 +32,7 @@ import java.util.concurrent.Executor; * */ public class MyWaifuClient { - private final APIWrapper APIWrapper; + private APIWrapper APIWrapper; /** * Creates an instance of {@link MyWaifuClient} @@ -42,7 +42,7 @@ public class MyWaifuClient { * @param httpClient The underlying {@link HttpClient} to use for HttpRequests * */ - MyWaifuClient(@NotNull String apiKey, @NotNull HttpClient httpClient) { + private MyWaifuClient(@NotNull String apiKey, @NotNull HttpClient httpClient) { APIWrapper = new APIWrapper(apiKey, httpClient); } @@ -367,6 +367,10 @@ public class MyWaifuClient { return APIWrapper.searchSeries(name); } + void setAPIWrapper(me.goudham.APIWrapper APIWrapper) { + this.APIWrapper = APIWrapper; + } + /** * Builder for {@link MyWaifuClient} * From 473a523b87c7bc9e8a2ffa037fc0507d5ae7ddf0 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 06:36:43 +0100 Subject: [PATCH 23/35] Add sample JSON response from API --- src/test/resources/getBestWaifus.json | 604 ++++++++++++++++++++++++++ src/test/resources/getWaifu.json | 71 +++ 2 files changed, 675 insertions(+) create mode 100644 src/test/resources/getBestWaifus.json create mode 100644 src/test/resources/getWaifu.json diff --git a/src/test/resources/getBestWaifus.json b/src/test/resources/getBestWaifus.json new file mode 100644 index 0000000..a820e47 --- /dev/null +++ b/src/test/resources/getBestWaifus.json @@ -0,0 +1,604 @@ +{ + "data": [ + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "When They Cry", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "when-they-cry", + "type": null, + "url": "https://www.mywaifulist.moe/series/when-they-cry" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "When They Cry (2020)", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "when-they-cry-2020", + "type": null, + "url": "https://www.mywaifulist.moe/series/when-they-cry-2020" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "When They Cry - Sotsu", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "when-they-cry-sotsu", + "type": null, + "url": "https://www.mywaifulist.moe/series/when-they-cry-sotsu" + } + ], + "description": "Rika is one of the main characters in Higurashi no Naku Koro ni Kai. She is...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/730/4aa77bd6a64aff6504cd2f8e718040b72b9f010a4b8124968cc3f2872904b3bb_thumb.jpeg", + "id": 730, + "likes": 373, + "name": "Rika Furude", + "original_name": "古手 梨花", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "rika-furude-when-they-cry", + "trash": 42, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/rika-furude-when-they-cry" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime OVA", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-ova", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2: Part II", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Slime Diaries: That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime" + } + ], + "description": "Shion is one of the third group of subordinates to be named by Rimuru, and...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/7727/bfaed60aca345dcf1937fe4a395112c9748f9712cbad7e4d1ab95faca9480f31_thumb.jpeg", + "id": 7727, + "likes": 727, + "name": "Shion", + "original_name": "シオン", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "shion-that-time-i-got-reincarnated-as-a-slime", + "trash": 70, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/shion-that-time-i-got-reincarnated-as-a-slime" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime OVA", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-ova", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2: Part II", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Slime Diaries: That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime" + } + ], + "description": "Milim Nava is one of the oldest and strongest Demon Lords, and the third Tr...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/21495/788959e5be43dcea863ea095e7380b18e330f98d98ea21ae799b68a02c4972a7_thumb.jpeg", + "id": 7728, + "likes": 1016, + "name": "Milim Nava", + "original_name": "ミリム・ナーヴァ", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "milim-nava-that-time-i-got-reincarnated-as-a-slime", + "trash": 85, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/milim-nava-that-time-i-got-reincarnated-as-a-slime" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime OVA", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-ova", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2: Part II", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Slime Diaries: That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime" + } + ], + "description": "Shuna is one of the third group of subordinate Monsters to be named by Rimu...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/7729/e1f93cba40cb61513793d21a6f2772f849485d7cb6402b07a80b716e099faed8_thumb.jpeg", + "id": 7729, + "likes": 998, + "name": "Shuna", + "original_name": "朱菜", + "relevance": 1, + "romaji": "Shuna", + "romaji_name": "Shuna", + "series": null, + "slug": "shuna-that-time-i-got-reincarnated-as-a-slime", + "trash": 78, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/shuna-that-time-i-got-reincarnated-as-a-slime" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime OVA", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-ova", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2: Part II", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Slime Diaries: That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime" + } + ], + "description": "The main protagonist of the series and Founder and King of the city known a...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/7730/a5c4a0dcc0f01851f6df0c551ca4e00df3af50b7d65342e02e994f2ecf62b7a8_thumb.jpeg", + "id": 7730, + "likes": 550, + "name": "Rimuru Tempest", + "original_name": "リムル=テンペスト", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "rimuru-tempest-that-time-i-got-reincarnated-as-a-slime", + "trash": 55, + "type": "Husbando", + "url": "https://www.mywaifulist.moe/waifu/rimuru-tempest-that-time-i-got-reincarnated-as-a-slime" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2: Part II", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Slime Diaries: That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime" + } + ], + "description": "Shizue Izawa, also known as simply Shizu, was the companion of the previous...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/7731/c89f6d88d65ec0358babb3b35696d2648178bf745b0bb1431167f3a97d2ccd7d_thumb.jpeg", + "id": 7731, + "likes": 914, + "name": "Shizue Izawa", + "original_name": "井沢静江", + "relevance": 1, + "romaji": "Izawa Shizue", + "romaji_name": "Izawa Shizue", + "series": null, + "slug": "shizue-izawa-that-time-i-got-reincarnated-as-a-slime", + "trash": 81, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/shizue-izawa-that-time-i-got-reincarnated-as-a-slime" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "Magia Record: Puella Magi Madoka Magica Side Story", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "magia-record-puella-magi-madoka-magica-side-story", + "type": null, + "url": "https://www.mywaifulist.moe/series/magia-record-puella-magi-madoka-magica-side-story" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "Magia Record: Puella Magi Madoka Magica Side Story 2nd Season", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "magia-record-puella-magi-madoka-magica-side-story-2nd-season", + "type": null, + "url": "https://www.mywaifulist.moe/series/magia-record-puella-magi-madoka-magica-side-story-2nd-season" + } + ], + "description": "A genius artist obsessed with the theme of magical girl life and death, who...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/16490/fbc5dab5ec0381973c837261ec8f6bfa93cebe8a7471ece03627c754442f9965_thumb.png", + "id": 16490, + "likes": 44, + "name": "Alina Gray", + "original_name": "アリナ グレイ", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "alina-gray", + "trash": 1, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/alina-gray" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Great Jahy Will Not Be Defeated!", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-great-jahy-will-not-be-defeated", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-great-jahy-will-not-be-defeated" + } + ], + "description": "The #2 of the Demon Realm... well, was", + "display_picture": "https://thicc.mywaifulist.moe/waifus/18772/6d44ea024aff2a387319065b4c37a2f6eeab72deecfc5a308b2bb42a796d8bff_thumb.jpeg", + "id": 18772, + "likes": 126, + "name": "Jahy", + "original_name": "ジャヒ", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "jahy", + "trash": 4, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/jahy" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "That Time I Got Reincarnated as a Slime Season 2: Part II", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", + "type": null, + "url": "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii" + }, + { + "description": null, + "display_picture": null, + "id": null, + "name": "The Slime Diaries: That Time I Got Reincarnated as a Slime", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", + "type": null, + "url": "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime" + } + ], + "description": "Treyni (トレイニー Toreinii) is a dryad that resides in the Jura Forest....", + "display_picture": "https://thicc.mywaifulist.moe/waifus/19048/f624b96166f76c3e1d5fc720ca93713e0d491fbae1634373e540d5cdd73dbcd0_thumb.png", + "id": 19048, + "likes": 187, + "name": "Treyni", + "original_name": "トレイニー Protector of Treants Manager of the Great Jura Forest", + "relevance": 1, + "romaji": null, + "romaji_name": null, + "series": null, + "slug": "treyni-that-time-i-got-reincarnated-as-a-slime", + "trash": 16, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/treyni-that-time-i-got-reincarnated-as-a-slime" + }, + { + "appearances": [ + { + "description": null, + "display_picture": null, + "id": null, + "name": "Obey Me!", + "original_name": null, + "relevance": 1, + "romaji_name": null, + "slug": "obey-me", + "type": null, + "url": "https://www.mywaifulist.moe/series/obey-me" + } + ], + "description": "Satan is the youngest member of the family but is ranked 4th due to his pow...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/31538/5a5889850922830d32dcc800d88acc4e858c19ad7e8f30b53df9f1b3963ea068_thumb.png", + "id": 31538, + "likes": 36, + "name": "Satan", + "original_name": "サタン", + "relevance": 1, + "romaji": "", + "romaji_name": "", + "series": null, + "slug": "satan-2", + "trash": 0, + "type": "Husbando", + "url": "https://www.mywaifulist.moe/waifu/satan-2" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/getWaifu.json b/src/test/resources/getWaifu.json new file mode 100644 index 0000000..11c4f3e --- /dev/null +++ b/src/test/resources/getWaifu.json @@ -0,0 +1,71 @@ +{ + "data": { + "age": null, + "appearances": [ + { + "airing_end": null, + "airing_start": null, + "description": "With his parents abroad, Kimihito Kurusu lived a quiet, unremarkable life alone until monster girls came crowding in! This alternate reality presents cutting-edge Japan, the first country to promote the integration of non-human species into society. After the incompetence of interspecies exchange coordinator Agent Smith leaves Kimihito as the homestay caretaker of a Lamia named Miia, the newly-minted \"Darling\" quickly attracts girls of various breeds, resulting in an ever-growing harem flush with eroticism and attraction.\r\n\r\nUnfortunately for him and the ladies, sexual interactions between species is forbidden by the Interspecies Exchange Act! The only loophole is through an experimental marriage provision. Kimihito's life becomes fraught with an abundance of creature-specific caveats and sensitive interspecies law as the passionate, affectionate, and lusty women hound his every move, seeking his romantic and sexual affections. With new species often appearing and events materializing out of thin air, where Kimihito and his harem go is anyone's guess!\r\n\r\n[Written by MAL Rewrite]", + "display_picture": "https://thicc.mywaifulist.moe/series/201/29eb0d9393f2f281e2882b08d3b2af81577a3033a3544189b325b22f9659eba4.jpeg", + "episode_count": 12, + "name": "Monster Musume: Everyday Life with Monster Girls", + "original_name": null, + "release": null, + "romaji_name": null, + "slug": "monster-musume-everyday-life-with-monster-girls", + "studio": null + } + ], + "birthday_day": 27, + "birthday_month": "June", + "birthday_year": null, + "blood_type": null, + "bust": "92.00", + "creator": { + "id": 1, + "name": "ReaverCelty" + }, + "description": "Rachnera Arachnera is an Arachne and the sixth girl to move in with Kimihito and fifth girl to officially do so. Due to their fear of her, her original host family had sold her off to Kasegi, who used her and her webbing to make money. This, along with his cruel behavior left her with a deep hatred for humans. This behaviour changed after she abducted Kurusu and found out that he was a decent person who didn't discriminate against non-humans. Since then, Rachnera has been flirting with Kurusu while facing her fellow monster girls in the house, especially Miia and Centorea Shianus who see her as their love rival for Kurusu.\n\nRachnera has the body of a young woman with short lavender hair that covers the right side of her face. She has six pupil-less, monochromatic red eyes, and sharp and pointed teeth. Her most noticeable physical trait is the mass located at her humanoid buttocks which is that of a giant spider adorned with a large skull design on the back of its abdomen. She has black carapace covering her arms from the shoulders down, and long fingers with gauntlet-like plating on her hands that end in sharpened points. Her usual attire consists of a revealing halter top with shoulder-less sleeves, and black and gold loin cloth with a slightly-frilly white trim. Her outfits change more often than most of the other girls and are usually more revealing or mature.\n\nMost of Rachnera's personality seems to be determined by her bad experiences with humans. Because of this, she initially hated humans, and believed them all to be hypocrites. While she believed this by her outlook of Kimihito as well, she was shocked to find out his kindness was genuine. This changed her outlook significantly, though it did not completely change her. Nevertheless, Rachnera still retains at least some of her cynicism as evidenced by her conviction that Kimhito, as a man, would inevitably cheat on his home-stays. One of the biggest impacts that Rachnera's past has had on her is a deep-rooted hatred of dishonesty in any shape or form. No matter how ugly the truth is, Rachnera would still prefer it over pleasant lies. This is demonstrated when she managed to goad Cerea into admitting she hates her. Rachnera was actually happy with this, despite Cerea standing up for her to Miia earlier. Rachnera told Cerea that if she dislikes her then she should do so openly. One way to make her genuinely angry is to mock her history and motivations. Rachnera is extremely mischievous as evidenced when she gleefully traps her former host in her webbing after scolding her for not keeping her webs in one place . Likewise, she greatly enjoys provoking others, displaying an innate talent for saying the right thing at the right time to cause cracks in almost anyone's composure. In this respect, Miia and Centorea make very easy targets for her due to their mutual dislike for her and highly sensitive personalities. Rachnera also displays a pronounced sadistic streak given how she subjects Lilith to sexual torture and made members of her host family \"subs\" in her BDSM routines. . Notwithstanding her frightening appearance and unsettling \"hobbies\", Rachnera possesses a very sophisticated and seductive personality. Of all of Kimihito's tenants, she consistently displays the most confidence and self-awareness in her sex appeal when flirting with Kimihito (particularly when compared with Miia's suffocating displays of affection and Cerea's bashful prudishness). Likewise, Rachnera is the most licentious of the homestays as evidenced by her conscious attempts to sexually arouse Kimihito, along with her unabashed enjoyment of acts of (accidental) perversity on Kimihito's part . From what is shown, Rachnera greatly enjoys bondage, getting a great kick from tying her victims up in her own webbing, both sexually and non-sexually. However, dealing with both Papi and Suu in the others' absence proved to be too exhausting even for her. She is also revealed to be somewhat obsessive about perfecting her bondage technique, which annoys most of the other monster girls (except Suu, who cannot be bound, and Mero, who rather shamefully enjoys it) to varying degrees , though she seems to have exempted Kimihito from being webbed up in this fashion. However, she seems to include every monster girl who lives in Kimihito's home or is a possible rival as fair game, a fact that has Lala more than a little scared of her. She is implied to be somewhat lazy, and can frequently be seen slacking off. However, even despite her sinister traits, it is clear Rachnera's the most mature of all the home-stays, and can quickly discard her playful attitude and become very serious if the situation calls for it. Most notably, Papi and Suu consider Rachnera to be their favorite teacher in the household, even more than Kimihito himself, due to Rachnera being the most knowledgeable and skilled at teaching. Ironically, despite being built like a dangerous predator, Rachnera has great self-restraint, and Kimihito even noted that she's the only member of the household who has never genuinely harmed or endangered him, even by accident, which caused to her to become embarrassed. It is also shown that she cares deeply for Kimihito as when Lala said that Kimihito was going to die, she angrily bound her in thread and threatened her not to speak so offensively in front of her. Despite all this, according to Lilith, Rachnera's strong personality is merely a façade she puts up. Rachnera pretends to be uncaring, however she is afraid of being rejected again for her features, and that she is very self-conscious about her appearance. Rachnera's angry reaction implies that there is some truth to those words. In fact, when Rachnera gets intoxicated, she becomes much more emotional and honest about her true feelings.", + "display_picture": "https://thicc.mywaifulist.moe/waifus/1/5c1be30a7b7834faef76e4b4dd8a512030a0cd3437f6a5bcc75de1ac33908092_thumb.jpeg", + "height": "198.00", + "hip": "87.00", + "husbando": false, + "id": 1, + "like_rank": 159, + "likes": 1282, + "name": "Rachnera Arachnera", + "nsfw": false, + "origin": null, + "original_name": "ラクネラ・アラクネラ", + "popularity_rank": 173, + "romaji_name": "Rakunera Arakunera", + "series": { + "airing_end": null, + "airing_start": null, + "description": "With his parents abroad, Kimihito Kurusu lived a quiet, unremarkable life alone until monster girls came crowding in! This alternate reality presents cutting-edge Japan, the first country to promote the integration of non-human species into society. After the incompetence of interspecies exchange coordinator Agent Smith leaves Kimihito as the homestay caretaker of a Lamia named Miia, the newly-minted \"Darling\" quickly attracts girls of various breeds, resulting in an ever-growing harem flush with eroticism and attraction.\r\n\r\nUnfortunately for him and the ladies, sexual interactions between species is forbidden by the Interspecies Exchange Act! The only loophole is through an experimental marriage provision. Kimihito's life becomes fraught with an abundance of creature-specific caveats and sensitive interspecies law as the passionate, affectionate, and lusty women hound his every move, seeking his romantic and sexual affections. With new species often appearing and events materializing out of thin air, where Kimihito and his harem go is anyone's guess!\r\n\r\n[Written by MAL Rewrite]", + "display_picture": "https://thicc.mywaifulist.moe/series/201/29eb0d9393f2f281e2882b08d3b2af81577a3033a3544189b325b22f9659eba4.jpeg", + "episode_count": 12, + "name": "Monster Musume: Everyday Life with Monster Girls", + "original_name": null, + "release": null, + "romaji_name": null, + "slug": "monster-musume-everyday-life-with-monster-girls", + "studio": null + }, + "slug": "rachnera-arachnera-monster-musume-everyday-life-with-monster-girls", + "tags": [ + { + "created_at": "2021-07-02T23:18:04.000000Z", + "id": 2507, + "name": "monster girl", + "slug": "monster-girl", + "updated_at": "2021-07-02T23:18:04.000000Z" + } + ], + "trash": 212, + "trash_rank": 244, + "url": "https://www.mywaifulist.moe/waifu/rachnera-arachnera-monster-musume-everyday-life-with-monster-girls", + "waist": "55.00", + "weight": "82.00" + } +} \ No newline at end of file From b9ccaf159f90ed35365404ec32250487ed27e8f7 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 06:37:09 +0100 Subject: [PATCH 24/35] Create helper class to instantiate expected entities for tests --- src/test/java/me/goudham/util/TestEntity.java | 347 ++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 src/test/java/me/goudham/util/TestEntity.java diff --git a/src/test/java/me/goudham/util/TestEntity.java b/src/test/java/me/goudham/util/TestEntity.java new file mode 100644 index 0000000..968afeb --- /dev/null +++ b/src/test/java/me/goudham/util/TestEntity.java @@ -0,0 +1,347 @@ +package me.goudham.util; + +import me.goudham.domain.series.FilteredSeries; +import me.goudham.domain.series.Series; +import me.goudham.domain.user.Creator; +import me.goudham.domain.waifu.FilteredWaifu; +import me.goudham.domain.waifu.Waifu; + +import java.util.ArrayList; +import java.util.List; + +public class TestEntity { + public static Waifu getExpectedWaifu() { + Series series = new Series(); + series.setName("Monster Musume: Everyday Life with Monster Girls"); + series.setOriginalName(null); + series.setRomajiName(null); + series.setDescription("With his parents abroad, Kimihito Kurusu lived a quiet, unremarkable life alone until monster girls came crowding in! This alternate reality presents cutting-edge Japan, the first country to promote the integration of non-human species into society. After the incompetence of interspecies exchange coordinator Agent Smith leaves Kimihito as the homestay caretaker of a Lamia named Miia, the newly-minted \"Darling\" quickly attracts girls of various breeds, resulting in an ever-growing harem flush with eroticism and attraction.\r\n" + + "\r\n" + + "Unfortunately for him and the ladies, sexual interactions between species is forbidden by the Interspecies Exchange Act! The only loophole is through an experimental marriage provision. Kimihito's life becomes fraught with an abundance of creature-specific caveats and sensitive interspecies law as the passionate, affectionate, and lusty women hound his every move, seeking his romantic and sexual affections. With new species often appearing and events materializing out of thin air, where Kimihito and his harem go is anyone's guess!\r\n" + + "\r\n" + + "[Written by MAL Rewrite]"); + series.setSlug("monster-musume-everyday-life-with-monster-girls"); + series.setReleaseDate(null); + series.setAiringEnd(null); + series.setAiringStart(null); + series.setEpisodeCount(12); + series.setDisplayPicture("https://thicc.mywaifulist.moe/series/201/29eb0d9393f2f281e2882b08d3b2af81577a3033a3544189b325b22f9659eba4.jpeg"); + series.setUrl(null); + series.setStudio(null); + series.setType(null); + series.setId(null); + + List appearances = new ArrayList<>(); + appearances.add(series); + + Creator creator = new Creator(); + creator.setId(1); + creator.setName("ReaverCelty"); + + Waifu waifu = new Waifu(); + waifu.setId(1); + waifu.setSlug("rachnera-arachnera-monster-musume-everyday-life-with-monster-girls"); + waifu.setCreator(creator); + waifu.setName("Rachnera Arachnera"); + waifu.setOriginalName("ラクネラ・アラクネラ"); + waifu.setRomajiName("Rakunera Arakunera"); + waifu.setDisplayPicture("https://thicc.mywaifulist.moe/waifus/1/5c1be30a7b7834faef76e4b4dd8a512030a0cd3437f6a5bcc75de1ac33908092_thumb.jpeg"); + waifu.setDescription("Rachnera Arachnera is an Arachne and the sixth girl to move in with Kimihito and fifth girl to officially do so. Due to their fear of her, her original host family had sold her off to Kasegi, who used her and her webbing to make money. This, along with his cruel behavior left her with a deep hatred for humans. This behaviour changed after she abducted Kurusu and found out that he was a decent person who didn't discriminate against non-humans. Since then, Rachnera has been flirting with Kurusu while facing her fellow monster girls in the house, especially Miia and Centorea Shianus who see her as their love rival for Kurusu.\n" + + "\n" + + "Rachnera has the body of a young woman with short lavender hair that covers the right side of her face. She has six pupil-less, monochromatic red eyes, and sharp and pointed teeth. Her most noticeable physical trait is the mass located at her humanoid buttocks which is that of a giant spider adorned with a large skull design on the back of its abdomen. She has black carapace covering her arms from the shoulders down, and long fingers with gauntlet-like plating on her hands that end in sharpened points. Her usual attire consists of a revealing halter top with shoulder-less sleeves, and black and gold loin cloth with a slightly-frilly white trim. Her outfits change more often than most of the other girls and are usually more revealing or mature.\n" + + "\n" + + "Most of Rachnera's personality seems to be determined by her bad experiences with humans. Because of this, she initially hated humans, and believed them all to be hypocrites. While she believed this by her outlook of Kimihito as well, she was shocked to find out his kindness was genuine. This changed her outlook significantly, though it did not completely change her. Nevertheless, Rachnera still retains at least some of her cynicism as evidenced by her conviction that Kimhito, as a man, would inevitably cheat on his home-stays. One of the biggest impacts that Rachnera's past has had on her is a deep-rooted hatred of dishonesty in any shape or form. No matter how ugly the truth is, Rachnera would still prefer it over pleasant lies. This is demonstrated when she managed to goad Cerea into admitting she hates her. Rachnera was actually happy with this, despite Cerea standing up for her to Miia earlier. Rachnera told Cerea that if she dislikes her then she should do so openly. One way to make her genuinely angry is to mock her history and motivations. Rachnera is extremely mischievous as evidenced when she gleefully traps her former host in her webbing after scolding her for not keeping her webs in one place . Likewise, she greatly enjoys provoking others, displaying an innate talent for saying the right thing at the right time to cause cracks in almost anyone's composure. In this respect, Miia and Centorea make very easy targets for her due to their mutual dislike for her and highly sensitive personalities. Rachnera also displays a pronounced sadistic streak given how she subjects Lilith to sexual torture and made members of her host family \"subs\" in her BDSM routines. . Notwithstanding her frightening appearance and unsettling \"hobbies\", Rachnera possesses a very sophisticated and seductive personality. Of all of Kimihito's tenants, she consistently displays the most confidence and self-awareness in her sex appeal when flirting with Kimihito (particularly when compared with Miia's suffocating displays of affection and Cerea's bashful prudishness). Likewise, Rachnera is the most licentious of the homestays as evidenced by her conscious attempts to sexually arouse Kimihito, along with her unabashed enjoyment of acts of (accidental) perversity on Kimihito's part . From what is shown, Rachnera greatly enjoys bondage, getting a great kick from tying her victims up in her own webbing, both sexually and non-sexually. However, dealing with both Papi and Suu in the others' absence proved to be too exhausting even for her. She is also revealed to be somewhat obsessive about perfecting her bondage technique, which annoys most of the other monster girls (except Suu, who cannot be bound, and Mero, who rather shamefully enjoys it) to varying degrees , though she seems to have exempted Kimihito from being webbed up in this fashion. However, she seems to include every monster girl who lives in Kimihito's home or is a possible rival as fair game, a fact that has Lala more than a little scared of her. She is implied to be somewhat lazy, and can frequently be seen slacking off. However, even despite her sinister traits, it is clear Rachnera's the most mature of all the home-stays, and can quickly discard her playful attitude and become very serious if the situation calls for it. Most notably, Papi and Suu consider Rachnera to be their favorite teacher in the household, even more than Kimihito himself, due to Rachnera being the most knowledgeable and skilled at teaching. Ironically, despite being built like a dangerous predator, Rachnera has great self-restraint, and Kimihito even noted that she's the only member of the household who has never genuinely harmed or endangered him, even by accident, which caused to her to become embarrassed. It is also shown that she cares deeply for Kimihito as when Lala said that Kimihito was going to die, she angrily bound her in thread and threatened her not to speak so offensively in front of her. Despite all this, according to Lilith, Rachnera's strong personality is merely a façade she puts up. Rachnera pretends to be uncaring, however she is afraid of being rejected again for her features, and that she is very self-conscious about her appearance. Rachnera's angry reaction implies that there is some truth to those words. In fact, when Rachnera gets intoxicated, she becomes much more emotional and honest about her true feelings."); + waifu.setWeight(82.0); + waifu.setHeight(198.0); + waifu.setBust(92.0); + waifu.setHip(87.0); + waifu.setWaist(55.0); + waifu.setBloodType(null); + waifu.setOrigin(null); + waifu.setAge(null); + waifu.setBirthdayMonth("June"); + waifu.setBirthdayDay(27); + waifu.setBirthdayYear(null); + waifu.setLikes(1282); + waifu.setTrash(212); + waifu.setUrl("https://www.mywaifulist.moe/waifu/rachnera-arachnera-monster-musume-everyday-life-with-monster-girls"); + waifu.setHusbando(false); + waifu.setNsfw(false); + waifu.setPopularityRank(173); + waifu.setLikeRank(159); + waifu.setTrashRank(244); + waifu.setAppearances(appearances); + waifu.setSeries(series); + + return waifu; + } + + public static List getBestWaifus() { + List bestWaifus = new ArrayList<>(); + + List appearances1 = new ArrayList<>(); + appearances1.add(createFilteredSeries("When They Cry", "when-they-cry", "https://www.mywaifulist.moe/series/when-they-cry")); + appearances1.add(createFilteredSeries("When They Cry (2020)", "when-they-cry-2020", "https://www.mywaifulist.moe/series/when-they-cry-2020")); + appearances1.add(createFilteredSeries("When They Cry - Sotsu", "when-they-cry-sotsu", "https://www.mywaifulist.moe/series/when-they-cry-sotsu")); + + bestWaifus.add(createFilteredWaifu( + appearances1, + "Rika is one of the main characters in Higurashi no Naku Koro ni Kai. She is...", + "https://thicc.mywaifulist.moe/waifus/730/4aa77bd6a64aff6504cd2f8e718040b72b9f010a4b8124968cc3f2872904b3bb_thumb.jpeg", + 730.0, + 373, + "Rika Furude", + "古手 梨花", + null, + null, + "rika-furude-when-they-cry", + 42, + "Waifu", + "https://www.mywaifulist.moe/waifu/rika-furude-when-they-cry" + ) + ); + + List appearances2 = new ArrayList<>(); + appearances2.add(createFilteredSeries("That Time I Got Reincarnated as a Slime", "that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime")); + appearances2.add(createFilteredSeries("That Time I Got Reincarnated as a Slime OVA", "that-time-i-got-reincarnated-as-a-slime-ova", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova")); + appearances2.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2", "that-time-i-got-reincarnated-as-a-slime-season-2", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2")); + appearances2.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2: Part II", "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii")); + appearances2.add(createFilteredSeries("The Slime Diaries: That Time I Got Reincarnated as a Slime", "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime")); + + bestWaifus.add(createFilteredWaifu( + appearances2, + "Shion is one of the third group of subordinates to be named by Rimuru, and...", + "https://thicc.mywaifulist.moe/waifus/7727/bfaed60aca345dcf1937fe4a395112c9748f9712cbad7e4d1ab95faca9480f31_thumb.jpeg", + 7727.0, + 727, + "Shion", + "シオン", + null, + null, + "shion-that-time-i-got-reincarnated-as-a-slime", + 70, + "Waifu", + "https://www.mywaifulist.moe/waifu/shion-that-time-i-got-reincarnated-as-a-slime" + ) + ); + + List appearances3 = new ArrayList<>(); + appearances3.add(createFilteredSeries("That Time I Got Reincarnated as a Slime", "that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime")); + appearances3.add(createFilteredSeries("That Time I Got Reincarnated as a Slime OVA", "that-time-i-got-reincarnated-as-a-slime-ova", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova")); + appearances3.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2", "that-time-i-got-reincarnated-as-a-slime-season-2", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2")); + appearances3.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2: Part II", "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii")); + appearances3.add(createFilteredSeries("The Slime Diaries: That Time I Got Reincarnated as a Slime", "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime")); + + bestWaifus.add(createFilteredWaifu( + appearances3, + "Milim Nava is one of the oldest and strongest Demon Lords, and the third Tr...", + "https://thicc.mywaifulist.moe/waifus/21495/788959e5be43dcea863ea095e7380b18e330f98d98ea21ae799b68a02c4972a7_thumb.jpeg", + 7728.0, + 1016, + "Milim Nava", + "ミリム・ナーヴァ", + null, + null, + "milim-nava-that-time-i-got-reincarnated-as-a-slime", + 85, + "Waifu", + "https://www.mywaifulist.moe/waifu/milim-nava-that-time-i-got-reincarnated-as-a-slime" + ) + ); + + List appearances4 = new ArrayList<>(); + appearances4.add(createFilteredSeries("That Time I Got Reincarnated as a Slime", "that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime")); + appearances4.add(createFilteredSeries("That Time I Got Reincarnated as a Slime OVA", "that-time-i-got-reincarnated-as-a-slime-ova", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova")); + appearances4.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2", "that-time-i-got-reincarnated-as-a-slime-season-2", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2")); + appearances4.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2: Part II", "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii")); + appearances4.add(createFilteredSeries("The Slime Diaries: That Time I Got Reincarnated as a Slime", "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime")); + + bestWaifus.add(createFilteredWaifu( + appearances4, + "Shuna is one of the third group of subordinate Monsters to be named by Rimu...", + "https://thicc.mywaifulist.moe/waifus/7729/e1f93cba40cb61513793d21a6f2772f849485d7cb6402b07a80b716e099faed8_thumb.jpeg", + 7729.0, + 998, + "Shuna", + "朱菜", + "Shuna", + "Shuna", + "shuna-that-time-i-got-reincarnated-as-a-slime", + 78, + "Waifu", + "https://www.mywaifulist.moe/waifu/shuna-that-time-i-got-reincarnated-as-a-slime" + ) + ); + + List appearances5 = new ArrayList<>(); + appearances5.add(createFilteredSeries("That Time I Got Reincarnated as a Slime", "that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime")); + appearances5.add(createFilteredSeries("That Time I Got Reincarnated as a Slime OVA", "that-time-i-got-reincarnated-as-a-slime-ova", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-ova")); + appearances5.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2", "that-time-i-got-reincarnated-as-a-slime-season-2", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2")); + appearances5.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2: Part II", "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii")); + appearances5.add(createFilteredSeries("The Slime Diaries: That Time I Got Reincarnated as a Slime", "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime")); + + bestWaifus.add(createFilteredWaifu( + appearances5, + "The main protagonist of the series and Founder and King of the city known a...", + "https://thicc.mywaifulist.moe/waifus/7730/a5c4a0dcc0f01851f6df0c551ca4e00df3af50b7d65342e02e994f2ecf62b7a8_thumb.jpeg", + 7730.0, + 550, + "Rimuru Tempest", + "リムル=テンペスト", + null, + null, + "rimuru-tempest-that-time-i-got-reincarnated-as-a-slime", + 55, + "Husbando", + "https://www.mywaifulist.moe/waifu/rimuru-tempest-that-time-i-got-reincarnated-as-a-slime" + ) + ); + + List appearances6 = new ArrayList<>(); + appearances6.add(createFilteredSeries("That Time I Got Reincarnated as a Slime", "that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime")); + appearances6.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2", "that-time-i-got-reincarnated-as-a-slime-season-2", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2")); + appearances6.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2: Part II", "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii")); + appearances6.add(createFilteredSeries("The Slime Diaries: That Time I Got Reincarnated as a Slime", "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime")); + + bestWaifus.add(createFilteredWaifu( + appearances6, + "Shizue Izawa, also known as simply Shizu, was the companion of the previous...", + "https://thicc.mywaifulist.moe/waifus/7731/c89f6d88d65ec0358babb3b35696d2648178bf745b0bb1431167f3a97d2ccd7d_thumb.jpeg", + 7731.0, + 914, + "Shizue Izawa", + "井沢静江", + "Izawa Shizue", + "Izawa Shizue", + "shizue-izawa-that-time-i-got-reincarnated-as-a-slime", + 81, + "Waifu", + "https://www.mywaifulist.moe/waifu/shizue-izawa-that-time-i-got-reincarnated-as-a-slime" + ) + ); + + List appearances7 = new ArrayList<>(); + appearances7.add(createFilteredSeries("Magia Record: Puella Magi Madoka Magica Side Story", "magia-record-puella-magi-madoka-magica-side-story", "https://www.mywaifulist.moe/series/magia-record-puella-magi-madoka-magica-side-story")); + appearances7.add(createFilteredSeries("Magia Record: Puella Magi Madoka Magica Side Story 2nd Season", "magia-record-puella-magi-madoka-magica-side-story-2nd-season", "https://www.mywaifulist.moe/series/magia-record-puella-magi-madoka-magica-side-story-2nd-season")); + + bestWaifus.add(createFilteredWaifu( + appearances7, + "A genius artist obsessed with the theme of magical girl life and death, who...", + "https://thicc.mywaifulist.moe/waifus/16490/fbc5dab5ec0381973c837261ec8f6bfa93cebe8a7471ece03627c754442f9965_thumb.png", + 16490.0, + 44, + "Alina Gray", + "アリナ グレイ", + null, + null, + "alina-gray", + 1, + "Waifu", + "https://www.mywaifulist.moe/waifu/alina-gray" + ) + ); + + List appearances8 = new ArrayList<>(); + appearances8.add(createFilteredSeries("The Great Jahy Will Not Be Defeated!", "the-great-jahy-will-not-be-defeated", "https://www.mywaifulist.moe/series/the-great-jahy-will-not-be-defeated")); + + bestWaifus.add(createFilteredWaifu( + appearances8, + "The #2 of the Demon Realm... well, was", + "https://thicc.mywaifulist.moe/waifus/18772/6d44ea024aff2a387319065b4c37a2f6eeab72deecfc5a308b2bb42a796d8bff_thumb.jpeg", + 18772.0, + 126, + "Jahy", + "ジャヒ", + null, + null, + "jahy", + 4, + "Waifu", + "https://www.mywaifulist.moe/waifu/jahy" + ) + ); + + List appearances9 = new ArrayList<>(); + appearances9.add(createFilteredSeries("That Time I Got Reincarnated as a Slime", "that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime")); + appearances9.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2", "that-time-i-got-reincarnated-as-a-slime-season-2", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2")); + appearances9.add(createFilteredSeries("That Time I Got Reincarnated as a Slime Season 2: Part II", "that-time-i-got-reincarnated-as-a-slime-season-2-part-ii", "https://www.mywaifulist.moe/series/that-time-i-got-reincarnated-as-a-slime-season-2-part-ii")); + appearances9.add(createFilteredSeries("The Slime Diaries: That Time I Got Reincarnated as a Slime", "the-slime-diaries-that-time-i-got-reincarnated-as-a-slime", "https://www.mywaifulist.moe/series/the-slime-diaries-that-time-i-got-reincarnated-as-a-slime")); + + bestWaifus.add(createFilteredWaifu( + appearances9, + "Treyni (トレイニー Toreinii) is a dryad that resides in the Jura Forest....", + "https://thicc.mywaifulist.moe/waifus/19048/f624b96166f76c3e1d5fc720ca93713e0d491fbae1634373e540d5cdd73dbcd0_thumb.png", + 19048.0, + 187, + "Treyni", + "トレイニー Protector of Treants Manager of the Great Jura Forest", + null, + null, + "treyni-that-time-i-got-reincarnated-as-a-slime", + 16, + "Waifu", + "https://www.mywaifulist.moe/waifu/treyni-that-time-i-got-reincarnated-as-a-slime" + ) + ); + + List appearances10 = new ArrayList<>(); + appearances10.add(createFilteredSeries("Obey Me!", "obey-me", "https://www.mywaifulist.moe/series/obey-me")); + + bestWaifus.add(createFilteredWaifu( + appearances10, + "Satan is the youngest member of the family but is ranked 4th due to his pow...", + "https://thicc.mywaifulist.moe/waifus/31538/5a5889850922830d32dcc800d88acc4e858c19ad7e8f30b53df9f1b3963ea068_thumb.png", + 31538.0, + 36, + "Satan", + "サタン", + "", + "", + "satan-2", + 0, + "Husbando", + "https://www.mywaifulist.moe/waifu/satan-2" + ) + ); + + return bestWaifus; + } + + private static FilteredWaifu createFilteredWaifu(List appearances, String description, String displayPicture, + Double id, Integer likes, String name, String originalName, String romaji, + String romaji_name, String slug, Integer trash, String type, String url) { + FilteredWaifu filteredWaifu = new FilteredWaifu(); + filteredWaifu.setAppearances(appearances); + filteredWaifu.setDescription(description); + filteredWaifu.setDisplayPicture(displayPicture); + filteredWaifu.setId(id); + filteredWaifu.setLikes(likes); + filteredWaifu.setName(name); + filteredWaifu.setOriginalName(originalName); + filteredWaifu.setRomaji(romaji); + filteredWaifu.setRomajiName(romaji_name); + filteredWaifu.setSlug(slug); + filteredWaifu.setTrash(trash); + filteredWaifu.setType(type); + filteredWaifu.setUrl(url); + filteredWaifu.setRelevance(1); + return filteredWaifu; + } + + + private static FilteredSeries createFilteredSeries(String name, String slug, String url) { + FilteredSeries filteredSeries = new FilteredSeries(); + filteredSeries.setName(name); + filteredSeries.setDescription(null); + filteredSeries.setDisplayPicture(null); + filteredSeries.setId(null); + filteredSeries.setOriginalName(null); + filteredSeries.setRelevance(1); + filteredSeries.setRomajiName(null); + filteredSeries.setType(null); + filteredSeries.setSlug(slug); + filteredSeries.setUrl(url); + filteredSeries.setRelevance(1); + return filteredSeries; + } +} From eba96c29b83ded600b290f28f4a18f369ce4b4f2 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sat, 3 Jul 2021 06:37:21 +0100 Subject: [PATCH 25/35] Add tests for MyWaifuClient methods --- .../java/me/goudham/MyWaifuClientTest.java | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/test/java/me/goudham/MyWaifuClientTest.java diff --git a/src/test/java/me/goudham/MyWaifuClientTest.java b/src/test/java/me/goudham/MyWaifuClientTest.java new file mode 100644 index 0000000..c4e523f --- /dev/null +++ b/src/test/java/me/goudham/MyWaifuClientTest.java @@ -0,0 +1,152 @@ +package me.goudham; + +import me.goudham.domain.waifu.FilteredWaifu; +import me.goudham.domain.waifu.Waifu; +import me.goudham.exception.APIMapperException; +import me.goudham.exception.APIResponseException; +import me.goudham.util.TestEntity; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import javax.net.ssl.SSLSession; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.*; + +class MyWaifuClientTest { + + @Mock + private HttpClient httpClient; + + private APIWrapper apiWrapper; + + private final String apiKey = "ValidAPIKey"; + + private MyWaifuClient sut; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + + sut = MyWaifuClient.createDefault(apiKey); + apiWrapper = new APIWrapper(apiKey, httpClient); + sut.setAPIWrapper(apiWrapper); + } + + @Test + void successfullyGetWaifu() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getWaifu.json"); + Waifu expectedWaifu = TestEntity.getExpectedWaifu(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response actualWaifuResponse = sut.getWaifu(1); + + assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualWaifuResponse.getBody(), is(expectedBody)); + assertThat(actualWaifuResponse.getModel(), is(expectedWaifu)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void successfullyGetBestWaifus() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getBestWaifus.json"); + List expectedBestWaifus = TestEntity.getBestWaifus(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> actualWaifuResponse = sut.getBestWaifus(); + + assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualWaifuResponse.getBody(), is(expectedBody)); + assertThat(actualWaifuResponse.getModel(), is(expectedBestWaifus)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + private String getJsonAsString(String filename) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename); + return new BufferedReader(new InputStreamReader(Objects.requireNonNull(resourceAsStream), StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); + } + + private HttpRequest buildHttpGetRequest(String apiKey, String param) { + return HttpRequest.newBuilder() + .uri(URI.create("https://mywaifulist.moe/api/v1/" + param)) + .timeout(Duration.ofSeconds(20)) + .headers("Content-Type", "application/json", "apikey", apiKey) + .GET() + .build(); + } + + private HttpResponse buildHttpResponse(int statusCode, String body) { + return new HttpResponse<>() { + @Override + public int statusCode() { + return statusCode; + } + + @Override + public HttpRequest request() { + return null; + } + + @Override + public Optional> previousResponse() { + return Optional.empty(); + } + + @Override + public HttpHeaders headers() { + return null; + } + + @Override + public String body() { + return body; + } + + @Override + public Optional sslSession() { + return Optional.empty(); + } + + @Override + public URI uri() { + return null; + } + + @Override + public HttpClient.Version version() { + return null; + } + }; + } +} + From 3366194638d25c6de680e31f3085f2252ebd3335 Mon Sep 17 00:00:00 2001 From: Hamothy <58985301+sgoudham@users.noreply.github.com> Date: Sat, 3 Jul 2021 06:40:50 +0100 Subject: [PATCH 26/35] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6d6ef0..5281e6b 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ public class Main { Response randomWaifuResponse = myWaifuClient.getRandomWaifu(); Integer randomWaifuResponseCode = randomWaifuResponse.getStatusCode(); String randomWaifuResponseBody = randomWaifuResponse.getBody(); - FilteredWaifu randomWaifu = randomWaifuResponse.model(); + FilteredWaifu randomWaifu = randomWaifuResponse.getModel(); // getSeasonalAnime() Response> seasonalAnimeResponse = myWaifuClient.getSeasonalAnime(); From c5502d8414fc0e8be2842d1e3c8552e20e0ce643 Mon Sep 17 00:00:00 2001 From: Hammy Date: Sun, 4 Jul 2021 02:42:20 +0100 Subject: [PATCH 27/35] Add test for PaginationData requests --- .../java/me/goudham/MyWaifuClientTest.java | 33 ++++-- src/test/java/me/goudham/util/TestEntity.java | 103 ++++++++++++++++++ src/test/resources/getWaifuImages.json | 79 ++++++++++++++ 3 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 src/test/resources/getWaifuImages.json diff --git a/src/test/java/me/goudham/MyWaifuClientTest.java b/src/test/java/me/goudham/MyWaifuClientTest.java index c4e523f..6161f98 100644 --- a/src/test/java/me/goudham/MyWaifuClientTest.java +++ b/src/test/java/me/goudham/MyWaifuClientTest.java @@ -1,7 +1,9 @@ package me.goudham; +import me.goudham.domain.pagination.PaginationData; import me.goudham.domain.waifu.FilteredWaifu; import me.goudham.domain.waifu.Waifu; +import me.goudham.domain.waifu.WaifuImage; import me.goudham.exception.APIMapperException; import me.goudham.exception.APIResponseException; import me.goudham.util.TestEntity; @@ -36,8 +38,6 @@ class MyWaifuClientTest { @Mock private HttpClient httpClient; - private APIWrapper apiWrapper; - private final String apiKey = "ValidAPIKey"; private MyWaifuClient sut; @@ -47,7 +47,7 @@ class MyWaifuClientTest { MockitoAnnotations.openMocks(this); sut = MyWaifuClient.createDefault(apiKey); - apiWrapper = new APIWrapper(apiKey, httpClient); + APIWrapper apiWrapper = new APIWrapper(apiKey, httpClient); sut.setAPIWrapper(apiWrapper); } @@ -80,11 +80,30 @@ class MyWaifuClientTest { doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - Response> actualWaifuResponse = sut.getBestWaifus(); + Response> actualBestWaifusResponse = sut.getBestWaifus(); - assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode)); - assertThat(actualWaifuResponse.getBody(), is(expectedBody)); - assertThat(actualWaifuResponse.getModel(), is(expectedBestWaifus)); + assertThat(actualBestWaifusResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualBestWaifusResponse.getBody(), is(expectedBody)); + assertThat(actualBestWaifusResponse.getModel(), is(expectedBestWaifus)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void successfullyGetWaifuImages() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1/images?page=1"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getWaifuImages.json"); + PaginationData expectedWaifuImages = TestEntity.getWaifuImages(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> actualWaifuImagesResponse = sut.getWaifuImages(1, 1); + + assertThat(actualWaifuImagesResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualWaifuImagesResponse.getBody(), is(expectedBody)); + assertThat(actualWaifuImagesResponse.getModel(), is(expectedWaifuImages)); verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); verifyNoMoreInteractions(httpClient); } diff --git a/src/test/java/me/goudham/util/TestEntity.java b/src/test/java/me/goudham/util/TestEntity.java index 968afeb..2a20e74 100644 --- a/src/test/java/me/goudham/util/TestEntity.java +++ b/src/test/java/me/goudham/util/TestEntity.java @@ -1,10 +1,14 @@ package me.goudham.util; +import me.goudham.domain.pagination.Links; +import me.goudham.domain.pagination.Meta; +import me.goudham.domain.pagination.PaginationData; import me.goudham.domain.series.FilteredSeries; import me.goudham.domain.series.Series; import me.goudham.domain.user.Creator; import me.goudham.domain.waifu.FilteredWaifu; import me.goudham.domain.waifu.Waifu; +import me.goudham.domain.waifu.WaifuImage; import java.util.ArrayList; import java.util.List; @@ -307,6 +311,105 @@ public class TestEntity { return bestWaifus; } + public static PaginationData getWaifuImages() { + PaginationData waifuImagesPaginationData = new PaginationData<>(); + + Meta meta = new Meta(); + meta.setCurrentPage(1); + meta.setFrom(1); + meta.setLastPage(13); + meta.setPath("https://mywaifulist.moe/api/v1/waifu/1/images"); + meta.setTo(10); + meta.setPerPage(10); + meta.setTotal(123); + + waifuImagesPaginationData.setMeta(meta); + + Links links = new Links(); + links.setFirst("https://mywaifulist.moe/api/v1/waifu/1/images?page=1"); + links.setLast("https://mywaifulist.moe/api/v1/waifu/1/images?page=13"); + links.setNext("https://mywaifulist.moe/api/v1/waifu/1/images?page=2"); + links.setPrev(null); + + waifuImagesPaginationData.setLinks(links); + + List waifuImages = new ArrayList<>(); + + waifuImages.add(createWaifuImage( + 188451, + "https://thicc.mywaifulist.moe/waifus/1/5c1be30a7b7834faef76e4b4dd8a512030a0cd3437f6a5bcc75de1ac33908092.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/5c1be30a7b7834faef76e4b4dd8a512030a0cd3437f6a5bcc75de1ac33908092_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 178162, + "https://thicc.mywaifulist.moe/waifus/1/99568b61823e34d6935926d5a589181926cb83f4c4682f5b22f299f940da7503.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/99568b61823e34d6935926d5a589181926cb83f4c4682f5b22f299f940da7503_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 164424, + "https://thicc.mywaifulist.moe/waifus/1/c6d56e30ef20371df87f8967a10192140c71f03b39238c252fe6f0d49a03c1ca.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/c6d56e30ef20371df87f8967a10192140c71f03b39238c252fe6f0d49a03c1ca_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 164423, + "https://thicc.mywaifulist.moe/waifus/1/613ea8485d61fe89e52ea4a1865dff7bcf59f9d5b0d4640b78fce7778d25b9ae.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/613ea8485d61fe89e52ea4a1865dff7bcf59f9d5b0d4640b78fce7778d25b9ae_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 164421, + "https://thicc.mywaifulist.moe/waifus/1/53675eb34be747c6ff4a1004da5bedc8a8d0baf435df7966404f5c29b8bf92ae.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/53675eb34be747c6ff4a1004da5bedc8a8d0baf435df7966404f5c29b8bf92ae_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 164422, + "https://thicc.mywaifulist.moe/waifus/1/18f67d18effe733f394c643fbf9735efbfe955497d5bcddb984ba78f66718f5c.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/18f67d18effe733f394c643fbf9735efbfe955497d5bcddb984ba78f66718f5c_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 164419, + "https://thicc.mywaifulist.moe/waifus/1/de2eca1b6ffaa99134427eef444edf84e5274dc1812b014a70871e39eb380ef8.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/de2eca1b6ffaa99134427eef444edf84e5274dc1812b014a70871e39eb380ef8_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 164420, + "https://thicc.mywaifulist.moe/waifus/1/e52536c04ebf17ac73bfa45526be95b2271e23802740591c0455e1d99f1b0f09.png", + "false", + "https://thicc.mywaifulist.moe/waifus/1/e52536c04ebf17ac73bfa45526be95b2271e23802740591c0455e1d99f1b0f09_thumb.png" + )); + waifuImages.add(createWaifuImage( + 164418, + "https://thicc.mywaifulist.moe/waifus/1/9662ce327814ceb88fd77dbfa1456dfce2b6addcf2d7bb0f9b335a35136f1d33.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/9662ce327814ceb88fd77dbfa1456dfce2b6addcf2d7bb0f9b335a35136f1d33_thumb.jpeg" + )); + waifuImages.add(createWaifuImage( + 113519, + "https://thicc.mywaifulist.moe/waifus/1/2543fd22eafb15ec6e8810c11ce25161f058bbb9315e05845f593bbb4f26f859.jpeg", + "false", + "https://thicc.mywaifulist.moe/waifus/1/2543fd22eafb15ec6e8810c11ce25161f058bbb9315e05845f593bbb4f26f859_thumb.jpeg" + )); + + waifuImagesPaginationData.setData(waifuImages); + + return waifuImagesPaginationData; + } + + private static WaifuImage createWaifuImage(Integer id, String image, String nsfw, String thumbnail) { + WaifuImage waifuImage = new WaifuImage(); + waifuImage.setId(id); + waifuImage.setImage(image); + waifuImage.setNsfw(nsfw); + waifuImage.setThumbnail(thumbnail); + return waifuImage; + } + private static FilteredWaifu createFilteredWaifu(List appearances, String description, String displayPicture, Double id, Integer likes, String name, String originalName, String romaji, String romaji_name, String slug, Integer trash, String type, String url) { diff --git a/src/test/resources/getWaifuImages.json b/src/test/resources/getWaifuImages.json new file mode 100644 index 0000000..53a4ddd --- /dev/null +++ b/src/test/resources/getWaifuImages.json @@ -0,0 +1,79 @@ +{ + "data": [ + { + "id": 188451, + "image": "https://thicc.mywaifulist.moe/waifus/1/5c1be30a7b7834faef76e4b4dd8a512030a0cd3437f6a5bcc75de1ac33908092.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/5c1be30a7b7834faef76e4b4dd8a512030a0cd3437f6a5bcc75de1ac33908092_thumb.jpeg" + }, + { + "id": 178162, + "image": "https://thicc.mywaifulist.moe/waifus/1/99568b61823e34d6935926d5a589181926cb83f4c4682f5b22f299f940da7503.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/99568b61823e34d6935926d5a589181926cb83f4c4682f5b22f299f940da7503_thumb.jpeg" + }, + { + "id": 164424, + "image": "https://thicc.mywaifulist.moe/waifus/1/c6d56e30ef20371df87f8967a10192140c71f03b39238c252fe6f0d49a03c1ca.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/c6d56e30ef20371df87f8967a10192140c71f03b39238c252fe6f0d49a03c1ca_thumb.jpeg" + }, + { + "id": 164423, + "image": "https://thicc.mywaifulist.moe/waifus/1/613ea8485d61fe89e52ea4a1865dff7bcf59f9d5b0d4640b78fce7778d25b9ae.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/613ea8485d61fe89e52ea4a1865dff7bcf59f9d5b0d4640b78fce7778d25b9ae_thumb.jpeg" + }, + { + "id": 164421, + "image": "https://thicc.mywaifulist.moe/waifus/1/53675eb34be747c6ff4a1004da5bedc8a8d0baf435df7966404f5c29b8bf92ae.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/53675eb34be747c6ff4a1004da5bedc8a8d0baf435df7966404f5c29b8bf92ae_thumb.jpeg" + }, + { + "id": 164422, + "image": "https://thicc.mywaifulist.moe/waifus/1/18f67d18effe733f394c643fbf9735efbfe955497d5bcddb984ba78f66718f5c.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/18f67d18effe733f394c643fbf9735efbfe955497d5bcddb984ba78f66718f5c_thumb.jpeg" + }, + { + "id": 164419, + "image": "https://thicc.mywaifulist.moe/waifus/1/de2eca1b6ffaa99134427eef444edf84e5274dc1812b014a70871e39eb380ef8.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/de2eca1b6ffaa99134427eef444edf84e5274dc1812b014a70871e39eb380ef8_thumb.jpeg" + }, + { + "id": 164420, + "image": "https://thicc.mywaifulist.moe/waifus/1/e52536c04ebf17ac73bfa45526be95b2271e23802740591c0455e1d99f1b0f09.png", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/e52536c04ebf17ac73bfa45526be95b2271e23802740591c0455e1d99f1b0f09_thumb.png" + }, + { + "id": 164418, + "image": "https://thicc.mywaifulist.moe/waifus/1/9662ce327814ceb88fd77dbfa1456dfce2b6addcf2d7bb0f9b335a35136f1d33.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/9662ce327814ceb88fd77dbfa1456dfce2b6addcf2d7bb0f9b335a35136f1d33_thumb.jpeg" + }, + { + "id": 113519, + "image": "https://thicc.mywaifulist.moe/waifus/1/2543fd22eafb15ec6e8810c11ce25161f058bbb9315e05845f593bbb4f26f859.jpeg", + "nsfw": false, + "thumbnail": "https://thicc.mywaifulist.moe/waifus/1/2543fd22eafb15ec6e8810c11ce25161f058bbb9315e05845f593bbb4f26f859_thumb.jpeg" + } + ], + "links": { + "first": "https://mywaifulist.moe/api/v1/waifu/1/images?page=1", + "last": "https://mywaifulist.moe/api/v1/waifu/1/images?page=13", + "next": "https://mywaifulist.moe/api/v1/waifu/1/images?page=2", + "prev": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 13, + "path": "https://mywaifulist.moe/api/v1/waifu/1/images", + "per_page": 10, + "to": 10, + "total": 123 + } +} \ No newline at end of file From 0c510e4713987ab8d3f1c19a48b1726ab0cb7c3a Mon Sep 17 00:00:00 2001 From: Hammy Date: Wed, 7 Jul 2021 01:22:42 +0100 Subject: [PATCH 28/35] Add sad path test for ObjectMapper & Add test for betaSearch --- src/main/java/me/goudham/APIMapper.java | 8 +- src/main/java/me/goudham/APIWrapper.java | 6 +- .../java/me/goudham/MyWaifuClientTest.java | 337 +++++++++++------- src/test/java/me/goudham/util/TestEntity.java | 89 ++++- src/test/resources/betaSearch.json | 34 ++ 5 files changed, 324 insertions(+), 150 deletions(-) create mode 100644 src/test/resources/betaSearch.json diff --git a/src/main/java/me/goudham/APIMapper.java b/src/main/java/me/goudham/APIMapper.java index 12d0bdb..1b543c3 100644 --- a/src/main/java/me/goudham/APIMapper.java +++ b/src/main/java/me/goudham/APIMapper.java @@ -16,13 +16,13 @@ import java.util.List; * */ class APIMapper { - private final ObjectMapper objectMapper; + private ObjectMapper objectMapper; APIMapper() { objectMapper = new ObjectMapper(); } - /** + /*\ * Convert any object passed and return as a {@link String} * * @param obj {@link Object} to write as {@link String} @@ -153,4 +153,8 @@ class APIMapper { throw new APIMapperException(exceptionMessage, throwable); } + + void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } } diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index 981e9a5..40fae3a 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -39,7 +39,7 @@ public class APIWrapper { private static final String host = "https://mywaifulist.moe/api/v1/"; private String apiKey; - private final APIMapper apiMapper; + private APIMapper apiMapper; private final HttpClient httpClient; private final Executor executor = Executors.newFixedThreadPool(10); @@ -413,7 +413,7 @@ public class APIWrapper { this.apiKey = apiKey; } - String getApiKey() { - return apiKey; + void setApiMapper(APIMapper apiMapper) { + this.apiMapper = apiMapper; } } diff --git a/src/test/java/me/goudham/MyWaifuClientTest.java b/src/test/java/me/goudham/MyWaifuClientTest.java index 6161f98..ccddc89 100644 --- a/src/test/java/me/goudham/MyWaifuClientTest.java +++ b/src/test/java/me/goudham/MyWaifuClientTest.java @@ -1,5 +1,9 @@ package me.goudham; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import me.goudham.domain.pagination.PaginationData; import me.goudham.domain.waifu.FilteredWaifu; import me.goudham.domain.waifu.Waifu; @@ -11,6 +15,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import javax.net.ssl.SSLSession; import java.io.BufferedReader; @@ -29,143 +34,211 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import static me.goudham.APIUtils.listOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; class MyWaifuClientTest { - @Mock - private HttpClient httpClient; - - private final String apiKey = "ValidAPIKey"; - - private MyWaifuClient sut; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - - sut = MyWaifuClient.createDefault(apiKey); - APIWrapper apiWrapper = new APIWrapper(apiKey, httpClient); - sut.setAPIWrapper(apiWrapper); - } - - @Test - void successfullyGetWaifu() throws IOException, InterruptedException, APIMapperException, APIResponseException { - HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1"); - int expectedStatusCode = 200; - String expectedBody = getJsonAsString("getWaifu.json"); - Waifu expectedWaifu = TestEntity.getExpectedWaifu(); - HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); - - doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - - Response actualWaifuResponse = sut.getWaifu(1); - - assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode)); - assertThat(actualWaifuResponse.getBody(), is(expectedBody)); - assertThat(actualWaifuResponse.getModel(), is(expectedWaifu)); - verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - verifyNoMoreInteractions(httpClient); - } - - @Test - void successfullyGetBestWaifus() throws IOException, InterruptedException, APIMapperException, APIResponseException { - HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best"); - int expectedStatusCode = 200; - String expectedBody = getJsonAsString("getBestWaifus.json"); - List expectedBestWaifus = TestEntity.getBestWaifus(); - HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); - - doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - - Response> actualBestWaifusResponse = sut.getBestWaifus(); - - assertThat(actualBestWaifusResponse.getStatusCode(), is(expectedStatusCode)); - assertThat(actualBestWaifusResponse.getBody(), is(expectedBody)); - assertThat(actualBestWaifusResponse.getModel(), is(expectedBestWaifus)); - verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - verifyNoMoreInteractions(httpClient); - } - - @Test - void successfullyGetWaifuImages() throws IOException, InterruptedException, APIMapperException, APIResponseException { - HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1/images?page=1"); - int expectedStatusCode = 200; - String expectedBody = getJsonAsString("getWaifuImages.json"); - PaginationData expectedWaifuImages = TestEntity.getWaifuImages(); - HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); - - doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - - Response> actualWaifuImagesResponse = sut.getWaifuImages(1, 1); - - assertThat(actualWaifuImagesResponse.getStatusCode(), is(expectedStatusCode)); - assertThat(actualWaifuImagesResponse.getBody(), is(expectedBody)); - assertThat(actualWaifuImagesResponse.getModel(), is(expectedWaifuImages)); - verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); - verifyNoMoreInteractions(httpClient); - } - - private String getJsonAsString(String filename) { - InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename); - return new BufferedReader(new InputStreamReader(Objects.requireNonNull(resourceAsStream), StandardCharsets.UTF_8)) - .lines() - .collect(Collectors.joining("\n")); - } - - private HttpRequest buildHttpGetRequest(String apiKey, String param) { - return HttpRequest.newBuilder() - .uri(URI.create("https://mywaifulist.moe/api/v1/" + param)) - .timeout(Duration.ofSeconds(20)) - .headers("Content-Type", "application/json", "apikey", apiKey) - .GET() - .build(); - } - - private HttpResponse buildHttpResponse(int statusCode, String body) { - return new HttpResponse<>() { - @Override - public int statusCode() { - return statusCode; - } - - @Override - public HttpRequest request() { - return null; - } - - @Override - public Optional> previousResponse() { - return Optional.empty(); - } - - @Override - public HttpHeaders headers() { - return null; - } - - @Override - public String body() { - return body; - } - - @Override - public Optional sslSession() { - return Optional.empty(); - } - - @Override - public URI uri() { - return null; - } - - @Override - public HttpClient.Version version() { - return null; - } - }; - } + @Mock + private HttpClient httpClient; + + @Spy + private ObjectMapper objectMapper; + + private final String apiKey = "ValidAPIKey"; + + private MyWaifuClient sut; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + + sut = MyWaifuClient.createDefault(apiKey); + APIWrapper apiWrapper = new APIWrapper(apiKey, httpClient); + APIMapper apiMapper = new APIMapper(); + + apiMapper.setObjectMapper(objectMapper); + apiWrapper.setApiMapper(apiMapper); + sut.setAPIWrapper(apiWrapper); + } + + @Test + void successfullyGetWaifu() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getWaifu.json"); + Waifu expectedWaifu = TestEntity.getExpectedWaifu(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response actualWaifuResponse = sut.getWaifu(1); + + assertThat(actualWaifuResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualWaifuResponse.getBody(), is(expectedBody)); + assertThat(actualWaifuResponse.getModel(), is(expectedWaifu)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void successfullyGetBestWaifus() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getBestWaifus.json"); + List expectedBestWaifus = TestEntity.getBestWaifus(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> actualBestWaifusResponse = sut.getBestWaifus(); + + assertThat(actualBestWaifusResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualBestWaifusResponse.getBody(), is(expectedBody)); + assertThat(actualBestWaifusResponse.getModel(), is(expectedBestWaifus)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void successfullyGetWaifuImages() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "waifu/1/images?page=1"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getWaifuImages.json"); + PaginationData expectedWaifuImages = TestEntity.getWaifuImages(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> actualWaifuImagesResponse = sut.getWaifuImages(1, 1); + + assertThat(actualWaifuImagesResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualWaifuImagesResponse.getBody(), is(expectedBody)); + assertThat(actualWaifuImagesResponse.getModel(), is(expectedWaifuImages)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void successfullyPostBetaSearch() throws IOException, InterruptedException, APIMapperException, APIResponseException { + HttpRequest expectedHttpRequest = buildHttpPostRequest(apiKey, "search/beta"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("betaSearch.json"); + List expectedBetaSearch = TestEntity.getBetaSearch(); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + + Response> actualBetaSearchResponse = sut.betaSearch("Yumeko"); + + assertThat(actualBetaSearchResponse.getStatusCode(), is(expectedStatusCode)); + assertThat(actualBetaSearchResponse.getBody(), is(expectedBody)); + assertThat(actualBetaSearchResponse.getModel(), is(expectedBetaSearch)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + @Test + void failToGetBestWaifusWhereDeserializationGoesWrong() throws IOException, InterruptedException { + HttpRequest expectedHttpRequest = buildHttpGetRequest(apiKey, "airing/best"); + int expectedStatusCode = 200; + String expectedBody = getJsonAsString("getBestWaifus.json"); + String expectedData = getData(expectedBody); + HttpResponse expectedHttpResponse = buildHttpResponse(expectedStatusCode, expectedBody); + + JavaType expectedModel = listOf(FilteredWaifu.class); + String customExceptionMessage = "If you are seeing this message, " + + "this is more than likely a fault in my logic. " + + "Please raise an issue including the printed stacktrace :D"; + String exceptionMessage = "Uh Oh Somebody Did a No No!"; + String expectedExceptionMessage = "\n\n" + customExceptionMessage + "\n\n" + exceptionMessage; + + doReturn(expectedHttpResponse).when(httpClient).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + doThrow(new JsonProcessingException(exceptionMessage) {}).when(objectMapper).readValue(expectedData, expectedModel); + + Throwable actualException = assertThrows(APIMapperException.class, () -> sut.getBestWaifus()); + + assertThat(actualException.getMessage(), is(expectedExceptionMessage)); + verify(httpClient, times(1)).send(expectedHttpRequest, HttpResponse.BodyHandlers.ofString()); + verifyNoMoreInteractions(httpClient); + } + + private String getData(String jsonBody) throws JsonProcessingException { + JsonNode parent = objectMapper.readTree(jsonBody); + return parent.get("data").toString(); + } + + private HttpRequest buildHttpPostRequest(String apiKey, String param) { + return HttpRequest.newBuilder() + .uri(URI.create("https://mywaifulist.moe/api/v1/" + param)) + .timeout(Duration.ofSeconds(20)) + .headers("Content-Type", "application/json", "apikey", apiKey) + .POST(HttpRequest.BodyPublishers.ofString("")) + .build(); + } + + + private String getJsonAsString(String filename) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(filename); + return new BufferedReader(new InputStreamReader(Objects.requireNonNull(resourceAsStream), StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); + } + + private HttpRequest buildHttpGetRequest(String apiKey, String param) { + return HttpRequest.newBuilder() + .uri(URI.create("https://mywaifulist.moe/api/v1/" + param)) + .timeout(Duration.ofSeconds(20)) + .headers("Content-Type", "application/json", "apikey", apiKey) + .GET() + .build(); + } + + private HttpResponse buildHttpResponse(int statusCode, String body) { + return new HttpResponse<>() { + @Override + public int statusCode() { + return statusCode; + } + + @Override + public HttpRequest request() { + return null; + } + + @Override + public Optional> previousResponse() { + return Optional.empty(); + } + + @Override + public HttpHeaders headers() { + return null; + } + + @Override + public String body() { + return body; + } + + @Override + public Optional sslSession() { + return Optional.empty(); + } + + @Override + public URI uri() { + return null; + } + + @Override + public HttpClient.Version version() { + return null; + } + }; + } } diff --git a/src/test/java/me/goudham/util/TestEntity.java b/src/test/java/me/goudham/util/TestEntity.java index 2a20e74..761a80c 100644 --- a/src/test/java/me/goudham/util/TestEntity.java +++ b/src/test/java/me/goudham/util/TestEntity.java @@ -101,7 +101,8 @@ public class TestEntity { "rika-furude-when-they-cry", 42, "Waifu", - "https://www.mywaifulist.moe/waifu/rika-furude-when-they-cry" + "https://www.mywaifulist.moe/waifu/rika-furude-when-they-cry", + 1 ) ); @@ -125,7 +126,8 @@ public class TestEntity { "shion-that-time-i-got-reincarnated-as-a-slime", 70, "Waifu", - "https://www.mywaifulist.moe/waifu/shion-that-time-i-got-reincarnated-as-a-slime" + "https://www.mywaifulist.moe/waifu/shion-that-time-i-got-reincarnated-as-a-slime", + 1 ) ); @@ -149,7 +151,8 @@ public class TestEntity { "milim-nava-that-time-i-got-reincarnated-as-a-slime", 85, "Waifu", - "https://www.mywaifulist.moe/waifu/milim-nava-that-time-i-got-reincarnated-as-a-slime" + "https://www.mywaifulist.moe/waifu/milim-nava-that-time-i-got-reincarnated-as-a-slime", + 1 ) ); @@ -173,7 +176,8 @@ public class TestEntity { "shuna-that-time-i-got-reincarnated-as-a-slime", 78, "Waifu", - "https://www.mywaifulist.moe/waifu/shuna-that-time-i-got-reincarnated-as-a-slime" + "https://www.mywaifulist.moe/waifu/shuna-that-time-i-got-reincarnated-as-a-slime", + 1 ) ); @@ -197,7 +201,8 @@ public class TestEntity { "rimuru-tempest-that-time-i-got-reincarnated-as-a-slime", 55, "Husbando", - "https://www.mywaifulist.moe/waifu/rimuru-tempest-that-time-i-got-reincarnated-as-a-slime" + "https://www.mywaifulist.moe/waifu/rimuru-tempest-that-time-i-got-reincarnated-as-a-slime", + 1 ) ); @@ -220,7 +225,8 @@ public class TestEntity { "shizue-izawa-that-time-i-got-reincarnated-as-a-slime", 81, "Waifu", - "https://www.mywaifulist.moe/waifu/shizue-izawa-that-time-i-got-reincarnated-as-a-slime" + "https://www.mywaifulist.moe/waifu/shizue-izawa-that-time-i-got-reincarnated-as-a-slime", + 1 ) ); @@ -241,7 +247,8 @@ public class TestEntity { "alina-gray", 1, "Waifu", - "https://www.mywaifulist.moe/waifu/alina-gray" + "https://www.mywaifulist.moe/waifu/alina-gray", + 1 ) ); @@ -261,7 +268,8 @@ public class TestEntity { "jahy", 4, "Waifu", - "https://www.mywaifulist.moe/waifu/jahy" + "https://www.mywaifulist.moe/waifu/jahy", + 1 ) ); @@ -284,7 +292,8 @@ public class TestEntity { "treyni-that-time-i-got-reincarnated-as-a-slime", 16, "Waifu", - "https://www.mywaifulist.moe/waifu/treyni-that-time-i-got-reincarnated-as-a-slime" + "https://www.mywaifulist.moe/waifu/treyni-that-time-i-got-reincarnated-as-a-slime", + 1 ) ); @@ -304,7 +313,8 @@ public class TestEntity { "satan-2", 0, "Husbando", - "https://www.mywaifulist.moe/waifu/satan-2" + "https://www.mywaifulist.moe/waifu/satan-2", + 1 ) ); @@ -401,6 +411,59 @@ public class TestEntity { return waifuImagesPaginationData; } + public static List getBetaSearch() { + List betaSearchResults = new ArrayList<>(); + + List appearances = new ArrayList<>(); + appearances.add(createFullFilteredSeries( + "Unlike many schools, attending Hyakkaou Private Academy prepares students f...", + "https://thicc.mywaifulist.moe/series/1680/13c030778b8e7e44ddccb8ca999e18d6ea2b3d547eacc1f63bf5849ade858bb0.jpeg", + 1680, + "Kakegurui: Compulsive Gambler", + "Kakegurui", + 1, + null, + "kakegurui-compulsive-gambler", + "TV", + "https://www.mywaifulist.moe/series/kakegurui-compulsive-gambler" + )); + + betaSearchResults.add(createFilteredWaifu( + appearances, + "Yumeko Jabami is the main protagonist of Kakegurui. She's a transfer studen...", + "https://thicc.mywaifulist.moe/waifus/6167/7bf292a1552161de90ff7d0c1eab2c890b2aa374903773d38d39c6c6e8d6f3d2_thumb.jpeg", + 6167.0, + 1889, + "Yumeko Jabami", + "蛇喰夢子", + "Jabami Yumeko", + "Jabami Yumeko", + "yumeko-jabami-kakegurui-compulsive-gambler", + 172, + "Waifu", + "https://www.mywaifulist.moe/waifu/yumeko-jabami-kakegurui-compulsive-gambler", + 2 + ) + ); + + return betaSearchResults; + } + + private static FilteredSeries createFullFilteredSeries(String description, String displayPicture, int id, String name, String originalName, int relevance, String romajiName, String slug, String type, String url) { + FilteredSeries filteredSeries = new FilteredSeries(); + filteredSeries.setDescription(description); + filteredSeries.setDisplayPicture(displayPicture); + filteredSeries.setId(id); + filteredSeries.setName(name); + filteredSeries.setOriginalName(originalName); + filteredSeries.setRelevance(relevance); + filteredSeries.setRomajiName(romajiName); + filteredSeries.setSlug(slug); + filteredSeries.setType(type); + filteredSeries.setUrl(url); + return filteredSeries; + } + private static WaifuImage createWaifuImage(Integer id, String image, String nsfw, String thumbnail) { WaifuImage waifuImage = new WaifuImage(); waifuImage.setId(id); @@ -411,8 +474,8 @@ public class TestEntity { } private static FilteredWaifu createFilteredWaifu(List appearances, String description, String displayPicture, - Double id, Integer likes, String name, String originalName, String romaji, - String romaji_name, String slug, Integer trash, String type, String url) { + Double id, Integer likes, String name, String originalName, String romaji, + String romaji_name, String slug, Integer trash, String type, String url, Integer relevance) { FilteredWaifu filteredWaifu = new FilteredWaifu(); filteredWaifu.setAppearances(appearances); filteredWaifu.setDescription(description); @@ -427,7 +490,7 @@ public class TestEntity { filteredWaifu.setTrash(trash); filteredWaifu.setType(type); filteredWaifu.setUrl(url); - filteredWaifu.setRelevance(1); + filteredWaifu.setRelevance(relevance); return filteredWaifu; } diff --git a/src/test/resources/betaSearch.json b/src/test/resources/betaSearch.json new file mode 100644 index 0000000..4555059 --- /dev/null +++ b/src/test/resources/betaSearch.json @@ -0,0 +1,34 @@ +{ + "data": [ + { + "appearances": [ + { + "description": "Unlike many schools, attending Hyakkaou Private Academy prepares students f...", + "display_picture": "https://thicc.mywaifulist.moe/series/1680/13c030778b8e7e44ddccb8ca999e18d6ea2b3d547eacc1f63bf5849ade858bb0.jpeg", + "id": 1680, + "name": "Kakegurui: Compulsive Gambler", + "original_name": "Kakegurui", + "relevance": 1, + "romaji_name": null, + "slug": "kakegurui-compulsive-gambler", + "type": "TV", + "url": "https://www.mywaifulist.moe/series/kakegurui-compulsive-gambler" + } + ], + "description": "Yumeko Jabami is the main protagonist of Kakegurui. She's a transfer studen...", + "display_picture": "https://thicc.mywaifulist.moe/waifus/6167/7bf292a1552161de90ff7d0c1eab2c890b2aa374903773d38d39c6c6e8d6f3d2_thumb.jpeg", + "id": 6167, + "likes": 1889, + "name": "Yumeko Jabami", + "original_name": "蛇喰夢子", + "relevance": 2, + "romaji": "Jabami Yumeko", + "romaji_name": "Jabami Yumeko", + "series": null, + "slug": "yumeko-jabami-kakegurui-compulsive-gambler", + "trash": 172, + "type": "Waifu", + "url": "https://www.mywaifulist.moe/waifu/yumeko-jabami-kakegurui-compulsive-gambler" + } + ] +} \ No newline at end of file From fab3bce794899c2c176c8223226aa9257814f319 Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 01:15:05 +0100 Subject: [PATCH 29/35] Simplify Dockerfile --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 411a3dd..1a3f72d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ FROM maven:3.8.1-adoptopenjdk-11 MAINTAINER Goudham Suresh -RUN apt-get update && apt-get install -y \ - gpg +RUN apt-get update && apt-get install -y gpg \ No newline at end of file From b2294690903f37373ee742c10e6f5db7ff174653 Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 01:15:40 +0100 Subject: [PATCH 30/35] Import GPG Keys when branch is 'release' --- Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 6974d2f..51c7b25 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -20,6 +20,9 @@ pipeline { stages { stage("Import GPG Keys") { + when { + branch 'release' + } steps { sh 'gpg --batch --import $GPG_SECRET_KEY' sh 'gpg --import-ownertrust $GPG_OWNER_TRUST' From 117ca973185eb5503ee24e5718e76245045d686e Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 02:20:27 +0100 Subject: [PATCH 31/35] Update Stage Name --- Jenkinsfile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 51c7b25..dbc6054 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -6,12 +6,6 @@ pipeline { } environment { - NEXUS_VERSION = "nexus3" - NEXUS_PROTOCOL = "https" - NEXUS_REPOSITORY = "maven-goudham" - NEXUS_URL = credentials('fe3e0c7e-bcb1-4d55-9591-f55f71f42356') - NEXUS_CREDENTIAL_ID = 'e5582b32-3507-4e88-ab7c-d16d701c46e9' - CODECOV_TOKEN = credentials('44a3c021-5cbb-4a6f-bea2-ae6c51d43038') GPG_SECRET_KEY = credentials('4dbfd4ed-bba4-44e0-8410-fbce1a9bba73') @@ -51,7 +45,7 @@ pipeline { } } } - stage("Deploy") { + stage("Deploy To OSSRH") { when { branch 'release' } From 0f89db74da4f57969b9242e58222c12b7a45408f Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 02:24:11 +0100 Subject: [PATCH 32/35] Update To Version 1.0.0 & Disable autoRelease --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f388b98..4ddb85e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.goudham MyWaifuWrapper - 0.2.0 + 1.0.0 jar MyWaifuWrapper @@ -156,7 +156,7 @@ ossrh https://s01.oss.sonatype.org/ - true + false
From 8bb61c132046c398b1ba3038273e05adecc69599 Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 02:38:18 +0100 Subject: [PATCH 33/35] Remove public access from APIWrapper.java --- src/main/java/me/goudham/APIWrapper.java | 740 +++++++++--------- .../goudham/exception/APIMapperException.java | 3 +- .../exception/APIResponseException.java | 4 +- 3 files changed, 359 insertions(+), 388 deletions(-) diff --git a/src/main/java/me/goudham/APIWrapper.java b/src/main/java/me/goudham/APIWrapper.java index 40fae3a..a786ec1 100644 --- a/src/main/java/me/goudham/APIWrapper.java +++ b/src/main/java/me/goudham/APIWrapper.java @@ -32,388 +32,362 @@ import static me.goudham.APIUtils.paginationData; /** * Returns API information to {@link MyWaifuClient} - * */ -public class APIWrapper { - private final String version = "1.0"; - private static final String host = "https://mywaifulist.moe/api/v1/"; - private String apiKey; - - private APIMapper apiMapper; - private final HttpClient httpClient; - private final Executor executor = Executors.newFixedThreadPool(10); - - /** - * Instantiates an instance of {@link APIWrapper} to retrieve API Information. - * An instance of {@link APIMapper} is created to be able to {@code deserialize} JSON to - * Java objects - * - * @param apiKey API Key to authorise API request - * @param httpClient The underlying {@link HttpClient} to use for HttpRequests - * - */ - APIWrapper(String apiKey, HttpClient httpClient) { - this.apiKey = apiKey; - this.httpClient = httpClient; - apiMapper = new APIMapper(); - } - - /** - * Create base {@link HttpRequest.Builder} with custom url, default headers and timeout - * - * @param param The end of the endpoint appended onto the host - * @return {@link HttpRequest.Builder} - * - */ - private HttpRequest.Builder getBaseRequest(String param) { - return HttpRequest.newBuilder() - .uri(URI.create(host + param)) - .timeout(Duration.ofSeconds(20)) - .headers("Content-Type", "application/json", "apikey", apiKey); - } - - /** - * Separate method for sending GET requests - * - * @param param The end of the endpoint appended onto the host - * @return {@link Result} - * @throws APIResponseException If {@link #sendRequest(HttpRequest)} cannot retrieve the proper data from the API - * - */ - Result sendGetRequest(String param) throws APIResponseException { - HttpRequest request = getBaseRequest(param).GET().build(); - return sendRequest(request); - } - - /** - * Separate method for sending POST requests - * - * @param param The end of the endpoint appended onto the host - * @param headers Headers as Key/Value pairs for POST requests - * @return {@link Result} - * @throws APIResponseException If {@link #sendRequest(HttpRequest)} cannot retrieve the proper data from the API - * @throws APIMapperException If {@link APIMapper#getObjectAsString(Object)} cannot properly serialize object - * - */ - private Result sendPostRequest(String param, Map headers) throws APIResponseException, APIMapperException { - HttpRequest request = getBaseRequest(param) - .POST(HttpRequest.BodyPublishers.ofString(apiMapper.getObjectAsString(headers))) - .build(); - return sendRequest(request); - } - - /** - * Handles sending a request to the API asynchronously using {@link HttpRequest} - * and the underlying {@link HttpClient} - * - * @param httpRequest The {@link HttpRequest} to be sent by the {@link HttpClient} - * @return {@link Result} - * @throws APIResponseException If the {@link CompletableFuture Response} - * cannot be decoded or the thread was interrupted while waiting to receive the data - * - */ - private Result sendRequest(HttpRequest httpRequest) throws APIResponseException { - CompletableFuture futureResult = CompletableFuture.supplyAsync(() -> { - try { - return httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); - } catch (IOException | InterruptedException exp) { - exp.printStackTrace(); - } - return null; - }, executor).thenApply(httpResponse -> new Result(httpResponse.statusCode(), httpResponse.body())); - - try { - return futureResult.get(); - } catch (InterruptedException | ExecutionException exp) { - throw new APIResponseException(exp.getMessage(), exp); - } - } - - /** - * Retrieve detailed information about the {@link Waifu} by sending request to API - * - * @param waifuId The id of the {@link Waifu} - * @return {@link Response} of {@link Waifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response getWaifu(String waifuId) throws APIResponseException, APIMapperException { - Result waifuResult = sendGetRequest("waifu/" + waifuId); - return apiMapper.deserialize(waifuResult, Waifu.class); - } - - /** - * Retrieve paginated images from the gallery, in sets of 10, by sending request to API - * - * @param waifuId The id of the {@link Waifu} - * @param pageNum The page number of the gallery - * @return {@link Response} of {@link WaifuImage} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getWaifuImages(String waifuId, String pageNum) throws APIResponseException, APIMapperException { - Result waifuImagesResult = sendGetRequest("waifu/" + waifuId + "/images?page=" + pageNum); - return apiMapper.deserializeToPaginationData(waifuImagesResult, paginationData(WaifuImage.class)); - } - - /** - * Retrieve an array of {@link FilteredWaifu}'s, sorted alphabetically, by sending request to API - * - * @param pageNum The page number of the gallery - * @return {@link Response} of {@link PaginationData} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getWaifusByPage(String pageNum) throws APIResponseException, APIMapperException { - Result waifusByPageResult = sendGetRequest("waifu?page=" + pageNum); - return apiMapper.deserializeToPaginationData(waifusByPageResult, paginationData(FilteredWaifu.class)); - } - - /** - * Retrieve the Waifu of the Day by sending request to API - * - * @return {@link Response} of {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response getDailyWaifu() throws APIResponseException, APIMapperException { - Result dailyWaifuResult = sendGetRequest("meta/daily"); - return apiMapper.deserialize(dailyWaifuResult, FilteredWaifu.class); - } - - /** - * Retrieve a Random Waifu from the Website by sending request to API - * - * @return {@link Response} of {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response getRandomWaifu() throws APIResponseException, APIMapperException { - Result randomWaifuResult = sendGetRequest("meta/random"); - return apiMapper.deserialize(randomWaifuResult, FilteredWaifu.class); - } - - /** - * Retrieve a List of Currently Airing Anim by sending request to API - * - * @return {@link Response} of {@link List} with {@link FilteredSeries} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getSeasonalAnime() throws APIResponseException, APIMapperException { - Result seasonalAnimeResult = sendGetRequest("airing"); - return apiMapper.deserializeToList(seasonalAnimeResult, listOf(FilteredSeries.class)); - } - - /** - * Retrieve the Best Waifus of the Current Season by sending request to API - * - * @return {@link Response} of {@link List} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getBestWaifus() throws APIResponseException, APIMapperException { - Result waifuResults = sendGetRequest("airing/best"); - return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); - } - - /** - * Retrieve a List of Popular Waifus (Raw Count of Total Votes) of the Current Season - * by sending request to API - * - * @return {@link Response} of {@link List} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getPopularWaifus() throws APIResponseException, APIMapperException { - Result waifuResults = sendGetRequest("airing/popular"); - return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); - } - - /** - * Retrieve the Most Disliked Waifus of the Current Season by sending request to API - * - * @return {@link Response} of {@link List} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getTrashWaifus() throws APIResponseException, APIMapperException { - Result waifuResults = sendGetRequest("airing/trash"); - return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); - } - - /** - * Retrieve detailed information about a given {@link Series} by sending request to API - * - * @param seriesId The id of the {@link Series} - * @return {@link Response} of {@link Series} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response getSeries(String seriesId) throws APIResponseException, APIMapperException { - Result seriesResult = sendGetRequest("series/" + seriesId); - return apiMapper.deserialize(seriesResult, Series.class); - } - - /** - * Retrieve paginated information about a Series by sending request to API - * - * @param pageNum The page number of the gallery - * @return {@link Response} of {@link PaginationData} with {@link FilteredSeries} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getSeriesByPage(String pageNum) throws APIResponseException, APIMapperException { - Result seriesPageResult = sendGetRequest("series?page=" + pageNum); - return apiMapper.deserializeToPaginationData(seriesPageResult, paginationData(FilteredSeries.class)); - } - - /** - * Retrieve the List of Anime that Aired in a given Season and Year by sending request to API - * - * @param season The specified season from {@link Season} - * @param year The specified year - * @return {@link Response} of {@link List} with {@link FilteredSeries} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getAllSeries(Season season, Integer year) throws APIResponseException, APIMapperException { - Result allSeriesResult = sendGetRequest("airing/" + season.getSeason() + "/" + year); - return apiMapper.deserializeToList(allSeriesResult, listOf(FilteredSeries.class)); - } - - /** - * Retrieve a set of Waifus for a given {@link Series} by sending request to API - * - * @param seriesId The id of the {@link Series} - * @return {@link Response} of {@link List} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getSeriesWaifus(String seriesId) throws APIResponseException, APIMapperException { - Result allWaifusFromSeriesResults = sendGetRequest("series/" + seriesId + "/waifus"); - return apiMapper.deserializeToList(allWaifusFromSeriesResults, listOf(FilteredWaifu.class)); - } - - /** - * Retrieve information about the {@link User} by sending request to API - * - * @param userId The id of the {@link User} - * @return {@link Response} of {@link User} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response getUserProfile(String userId) throws APIResponseException, APIMapperException { - Result userProfileResult = sendGetRequest("user/" + userId); - return apiMapper.deserialize(userProfileResult, User.class); - } - - /** - * Retrieve the Waifus Created, Liked, or Trashed for the given {@link User} id by sending request to API - * - * @param userId The id of the {@link User} - * @param listType The specified action E.g {@link WaifuListType#LIKED} - * @param pageNum The page number of the gallery - * @return {@link Response} of {@link PaginationData} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getUserWaifus(String userId, String listType, String pageNum) throws APIResponseException, APIMapperException { - Result userWaifusResult = sendGetRequest("user/" + userId + "/" + listType + "?page=" + pageNum); - return apiMapper.deserializeToPaginationData(userWaifusResult, paginationData(FilteredWaifu.class)); - } - - /** - * Retrieve a List of all {@link UserList}'s shown by sending request to API - * - * @param userId The id of the {@link User} - * @return {@link Response} of {@link List} with {@link UserList} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> getUserLists(String userId) throws APIResponseException, APIMapperException { - Result userProfileResult = sendGetRequest("user/" + userId + "/lists"); - return apiMapper.deserializeToList(userProfileResult, listOf(UserList.class)); - } - - /** - * Retrieve the Specific {@link UserList}, with {@link Waifu}'s by sending request to API - * - * @param userId The id of the {@link User} - * @param listId The id of the {@link UserList} - * @return {@link Response} of {@link UserList} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response getUserList(String userId, String listId) throws APIResponseException, APIMapperException { - Result userProfileResult = sendGetRequest("user/" + userId + "/lists/" + listId); - return apiMapper.deserialize(userProfileResult, UserList.class); - } - - /** - * Retrieve a {@link List} of {@link FilteredWaifu} with a search term by sending a POST request to the API - * - * @param searchString {@link String} that should be searched for - * @return {@link Response} of {@link List} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> betaSearch(String searchString) throws APIMapperException, APIResponseException { - Result betaSearchResult = sendPostRequest("search/beta", Map.of("term", searchString)); - return apiMapper.deserializeToList(betaSearchResult, listOf(FilteredWaifu.class)); - } - - /** - * Retrieve a List of {@link FilteredWaifu}'s given a query, by sending POST request to API - * - * @param waifuName The name of the Waifu - * @return {@link Response} of {@link List} with {@link FilteredWaifu} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> searchWaifus(String waifuName) throws APIMapperException, APIResponseException { - Result searchWaifusResult = sendPostRequest("search/waifus", Map.of("term", waifuName)); - return apiMapper.deserializeToList(searchWaifusResult, listOf(FilteredWaifu.class)); - } - - /** - * Retrieve a List of {@link FilteredSeries}'s given a query, by sending POST request to API - * - * @param seriesName The name of the Series - * @return {@link Response} of {@link List} with {@link FilteredSeries} - * @throws APIResponseException If {@link APIWrapper} could not return information properly - * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model - * - */ - Response> searchSeries(String seriesName) throws APIMapperException, APIResponseException { - Result searchSeriesResult = sendPostRequest("search/series", Map.of("term", seriesName)); - return apiMapper.deserializeToList(searchSeriesResult, listOf(FilteredSeries.class)); - } - - void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - void setApiMapper(APIMapper apiMapper) { - this.apiMapper = apiMapper; - } +class APIWrapper { + private final String version = "1.0"; + private static final String host = "https://mywaifulist.moe/api/v1/"; + private String apiKey; + + private APIMapper apiMapper; + private final HttpClient httpClient; + private final Executor executor = Executors.newFixedThreadPool(10); + + /** + * Instantiates an instance of {@link APIWrapper} to retrieve API Information. + * An instance of {@link APIMapper} is created to be able to {@code deserialize} JSON to + * Java objects + * + * @param apiKey API Key to authorise API request + * @param httpClient The underlying {@link HttpClient} to use for HttpRequests + */ + APIWrapper(String apiKey, HttpClient httpClient) { + this.apiKey = apiKey; + this.httpClient = httpClient; + apiMapper = new APIMapper(); + } + + /** + * Create base {@link HttpRequest.Builder} with custom url, default headers and timeout + * + * @param param The end of the endpoint appended onto the host + * @return {@link HttpRequest.Builder} + */ + private HttpRequest.Builder getBaseRequest(String param) { + return HttpRequest.newBuilder() + .uri(URI.create(host + param)) + .timeout(Duration.ofSeconds(20)) + .headers("Content-Type", "application/json", "apikey", apiKey); + } + + /** + * Separate method for sending GET requests + * + * @param param The end of the endpoint appended onto the host + * @return {@link Result} + * @throws APIResponseException If {@link #sendRequest(HttpRequest)} cannot retrieve the proper data from the API + */ + Result sendGetRequest(String param) throws APIResponseException { + HttpRequest request = getBaseRequest(param).GET().build(); + return sendRequest(request); + } + + /** + * Separate method for sending POST requests + * + * @param param The end of the endpoint appended onto the host + * @param headers Headers as Key/Value pairs for POST requests + * @return {@link Result} + * @throws APIResponseException If {@link #sendRequest(HttpRequest)} cannot retrieve the proper data from the API + * @throws APIMapperException If {@link APIMapper#getObjectAsString(Object)} cannot properly serialize object + */ + private Result sendPostRequest(String param, Map headers) throws APIResponseException, APIMapperException { + HttpRequest request = getBaseRequest(param) + .POST(HttpRequest.BodyPublishers.ofString(apiMapper.getObjectAsString(headers))) + .build(); + return sendRequest(request); + } + + /** + * Handles sending a request to the API asynchronously using {@link HttpRequest} + * and the underlying {@link HttpClient} + * + * @param httpRequest The {@link HttpRequest} to be sent by the {@link HttpClient} + * @return {@link Result} + * @throws APIResponseException If the {@link CompletableFuture Response} + * cannot be decoded or the thread was interrupted while waiting to receive the data + */ + private Result sendRequest(HttpRequest httpRequest) throws APIResponseException { + CompletableFuture futureResult = CompletableFuture.supplyAsync(() -> { + try { + return httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + } catch (IOException | InterruptedException exp) { + exp.printStackTrace(); + } + return null; + }, executor).thenApply(httpResponse -> new Result(httpResponse.statusCode(), httpResponse.body())); + + try { + return futureResult.get(); + } catch (InterruptedException | ExecutionException exp) { + throw new APIResponseException(exp.getMessage(), exp); + } + } + + /** + * Retrieve detailed information about the {@link Waifu} by sending request to API + * + * @param waifuId The id of the {@link Waifu} + * @return {@link Response} of {@link Waifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response getWaifu(String waifuId) throws APIResponseException, APIMapperException { + Result waifuResult = sendGetRequest("waifu/" + waifuId); + return apiMapper.deserialize(waifuResult, Waifu.class); + } + + /** + * Retrieve paginated images from the gallery, in sets of 10, by sending request to API + * + * @param waifuId The id of the {@link Waifu} + * @param pageNum The page number of the gallery + * @return {@link Response} of {@link WaifuImage} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getWaifuImages(String waifuId, String pageNum) throws APIResponseException, APIMapperException { + Result waifuImagesResult = sendGetRequest("waifu/" + waifuId + "/images?page=" + pageNum); + return apiMapper.deserializeToPaginationData(waifuImagesResult, paginationData(WaifuImage.class)); + } + + /** + * Retrieve an array of {@link FilteredWaifu}'s, sorted alphabetically, by sending request to API + * + * @param pageNum The page number of the gallery + * @return {@link Response} of {@link PaginationData} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getWaifusByPage(String pageNum) throws APIResponseException, APIMapperException { + Result waifusByPageResult = sendGetRequest("waifu?page=" + pageNum); + return apiMapper.deserializeToPaginationData(waifusByPageResult, paginationData(FilteredWaifu.class)); + } + + /** + * Retrieve the Waifu of the Day by sending request to API + * + * @return {@link Response} of {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response getDailyWaifu() throws APIResponseException, APIMapperException { + Result dailyWaifuResult = sendGetRequest("meta/daily"); + return apiMapper.deserialize(dailyWaifuResult, FilteredWaifu.class); + } + + /** + * Retrieve a Random Waifu from the Website by sending request to API + * + * @return {@link Response} of {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response getRandomWaifu() throws APIResponseException, APIMapperException { + Result randomWaifuResult = sendGetRequest("meta/random"); + return apiMapper.deserialize(randomWaifuResult, FilteredWaifu.class); + } + + /** + * Retrieve a List of Currently Airing Anim by sending request to API + * + * @return {@link Response} of {@link List} with {@link FilteredSeries} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getSeasonalAnime() throws APIResponseException, APIMapperException { + Result seasonalAnimeResult = sendGetRequest("airing"); + return apiMapper.deserializeToList(seasonalAnimeResult, listOf(FilteredSeries.class)); + } + + /** + * Retrieve the Best Waifus of the Current Season by sending request to API + * + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getBestWaifus() throws APIResponseException, APIMapperException { + Result waifuResults = sendGetRequest("airing/best"); + return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); + } + + /** + * Retrieve a List of Popular Waifus (Raw Count of Total Votes) of the Current Season + * by sending request to API + * + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getPopularWaifus() throws APIResponseException, APIMapperException { + Result waifuResults = sendGetRequest("airing/popular"); + return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); + } + + /** + * Retrieve the Most Disliked Waifus of the Current Season by sending request to API + * + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getTrashWaifus() throws APIResponseException, APIMapperException { + Result waifuResults = sendGetRequest("airing/trash"); + return apiMapper.deserializeToList(waifuResults, listOf(FilteredWaifu.class)); + } + + /** + * Retrieve detailed information about a given {@link Series} by sending request to API + * + * @param seriesId The id of the {@link Series} + * @return {@link Response} of {@link Series} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response getSeries(String seriesId) throws APIResponseException, APIMapperException { + Result seriesResult = sendGetRequest("series/" + seriesId); + return apiMapper.deserialize(seriesResult, Series.class); + } + + /** + * Retrieve paginated information about a Series by sending request to API + * + * @param pageNum The page number of the gallery + * @return {@link Response} of {@link PaginationData} with {@link FilteredSeries} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getSeriesByPage(String pageNum) throws APIResponseException, APIMapperException { + Result seriesPageResult = sendGetRequest("series?page=" + pageNum); + return apiMapper.deserializeToPaginationData(seriesPageResult, paginationData(FilteredSeries.class)); + } + + /** + * Retrieve the List of Anime that Aired in a given Season and Year by sending request to API + * + * @param season The specified season from {@link Season} + * @param year The specified year + * @return {@link Response} of {@link List} with {@link FilteredSeries} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getAllSeries(Season season, Integer year) throws APIResponseException, APIMapperException { + Result allSeriesResult = sendGetRequest("airing/" + season.getSeason() + "/" + year); + return apiMapper.deserializeToList(allSeriesResult, listOf(FilteredSeries.class)); + } + + /** + * Retrieve a set of Waifus for a given {@link Series} by sending request to API + * + * @param seriesId The id of the {@link Series} + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getSeriesWaifus(String seriesId) throws APIResponseException, APIMapperException { + Result allWaifusFromSeriesResults = sendGetRequest("series/" + seriesId + "/waifus"); + return apiMapper.deserializeToList(allWaifusFromSeriesResults, listOf(FilteredWaifu.class)); + } + + /** + * Retrieve information about the {@link User} by sending request to API + * + * @param userId The id of the {@link User} + * @return {@link Response} of {@link User} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response getUserProfile(String userId) throws APIResponseException, APIMapperException { + Result userProfileResult = sendGetRequest("user/" + userId); + return apiMapper.deserialize(userProfileResult, User.class); + } + + /** + * Retrieve the Waifus Created, Liked, or Trashed for the given {@link User} id by sending request to API + * + * @param userId The id of the {@link User} + * @param listType The specified action E.g {@link WaifuListType#LIKED} + * @param pageNum The page number of the gallery + * @return {@link Response} of {@link PaginationData} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getUserWaifus(String userId, String listType, String pageNum) throws APIResponseException, APIMapperException { + Result userWaifusResult = sendGetRequest("user/" + userId + "/" + listType + "?page=" + pageNum); + return apiMapper.deserializeToPaginationData(userWaifusResult, paginationData(FilteredWaifu.class)); + } + + /** + * Retrieve a List of all {@link UserList}'s shown by sending request to API + * + * @param userId The id of the {@link User} + * @return {@link Response} of {@link List} with {@link UserList} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> getUserLists(String userId) throws APIResponseException, APIMapperException { + Result userProfileResult = sendGetRequest("user/" + userId + "/lists"); + return apiMapper.deserializeToList(userProfileResult, listOf(UserList.class)); + } + + /** + * Retrieve the Specific {@link UserList}, with {@link Waifu}'s by sending request to API + * + * @param userId The id of the {@link User} + * @param listId The id of the {@link UserList} + * @return {@link Response} of {@link UserList} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response getUserList(String userId, String listId) throws APIResponseException, APIMapperException { + Result userProfileResult = sendGetRequest("user/" + userId + "/lists/" + listId); + return apiMapper.deserialize(userProfileResult, UserList.class); + } + + /** + * Retrieve a {@link List} of {@link FilteredWaifu} with a search term by sending a POST request to the API + * + * @param searchString {@link String} that should be searched for + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> betaSearch(String searchString) throws APIMapperException, APIResponseException { + Result betaSearchResult = sendPostRequest("search/beta", Map.of("term", searchString)); + return apiMapper.deserializeToList(betaSearchResult, listOf(FilteredWaifu.class)); + } + + /** + * Retrieve a List of {@link FilteredWaifu}'s given a query, by sending POST request to API + * + * @param waifuName The name of the Waifu + * @return {@link Response} of {@link List} with {@link FilteredWaifu} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> searchWaifus(String waifuName) throws APIMapperException, APIResponseException { + Result searchWaifusResult = sendPostRequest("search/waifus", Map.of("term", waifuName)); + return apiMapper.deserializeToList(searchWaifusResult, listOf(FilteredWaifu.class)); + } + + /** + * Retrieve a List of {@link FilteredSeries}'s given a query, by sending POST request to API + * + * @param seriesName The name of the Series + * @return {@link Response} of {@link List} with {@link FilteredSeries} + * @throws APIResponseException If {@link APIWrapper} could not return information properly + * @throws APIMapperException If {@link APIMapper} could not correctly {@code deserialize} model + */ + Response> searchSeries(String seriesName) throws APIMapperException, APIResponseException { + Result searchSeriesResult = sendPostRequest("search/series", Map.of("term", seriesName)); + return apiMapper.deserializeToList(searchSeriesResult, listOf(FilteredSeries.class)); + } + + void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + void setApiMapper(APIMapper apiMapper) { + this.apiMapper = apiMapper; + } } diff --git a/src/main/java/me/goudham/exception/APIMapperException.java b/src/main/java/me/goudham/exception/APIMapperException.java index 9e5dd04..e44e225 100644 --- a/src/main/java/me/goudham/exception/APIMapperException.java +++ b/src/main/java/me/goudham/exception/APIMapperException.java @@ -1,10 +1,9 @@ package me.goudham.exception; -import me.goudham.APIWrapper; import me.goudham.Response; /** - * Thrown when {@link APIWrapper} fails to deserialize json into Java POJO's ({@link Response#getModel()}) + * Thrown when {@code APIWrapper} fails to deserialize json into Java POJO's ({@link Response#getModel()}) * */ public class APIMapperException extends Throwable { diff --git a/src/main/java/me/goudham/exception/APIResponseException.java b/src/main/java/me/goudham/exception/APIResponseException.java index f1a3439..018e513 100644 --- a/src/main/java/me/goudham/exception/APIResponseException.java +++ b/src/main/java/me/goudham/exception/APIResponseException.java @@ -1,9 +1,7 @@ package me.goudham.exception; -import me.goudham.APIWrapper; - /** - * Thrown when {@link APIWrapper} fails to return API information + * Thrown when {@code APIWrapper} fails to return API information * */ public class APIResponseException extends Throwable { From 8ece8af4f9e6a0f48adc2d3c28f5f4dc0541a8d2 Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 02:42:01 +0100 Subject: [PATCH 34/35] Clarify that this is unofficial --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5281e6b..85bbafa 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ![pull-requests]

MyWaifuWrapper

-

An Asynchronous Java API Wrapper for MyWaifuList

+

An Unofficial Asynchronous Java API Wrapper for MyWaifuList

# Summary From fa74d880a664ac32123b8887dcb5ed48db113667 Mon Sep 17 00:00:00 2001 From: Hammy Date: Fri, 9 Jul 2021 02:46:59 +0100 Subject: [PATCH 35/35] Update hamcrest library to 2.2 --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 4ddb85e..0b1f086 100644 --- a/pom.xml +++ b/pom.xml @@ -67,9 +67,8 @@
org.hamcrest - hamcrest-all - 1.3 - test + hamcrest + 2.2 org.jetbrains