diff --git a/src/main/java/org/goudham/me/APIMapper.java b/src/main/java/org/goudham/me/APIMapper.java new file mode 100644 index 0000000..b398f76 --- /dev/null +++ b/src/main/java/org/goudham/me/APIMapper.java @@ -0,0 +1,125 @@ +package org.goudham.me; + +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 com.fasterxml.jackson.databind.type.TypeFactory; +import org.goudham.me.api.entity.series.FilteredSeries; +import org.goudham.me.api.entity.series.Series; +import org.goudham.me.api.entity.waifu.Waifu; +import org.goudham.me.exception.APIMapperException; + +import java.util.List; + +/** + * Maps API Information to Java POJO's + * + */ +class APIMapper { + private final ObjectMapper objectMapper; + + APIMapper() { + objectMapper = new ObjectMapper(); + } + + /** + * Honestly I don't really know how this works + * + * @param entity The actual class of the given entity. E.g {@link Waifu#getClass()} + * @param The type of entity to be returned. E.g {@link Waifu} or {@link Series} + * @return {@link JavaType} + * + */ + private JavaType listOf(Class entity) { + return TypeFactory.defaultInstance().constructCollectionType(List.class, entity); + } + + /** + * Using the given {@code entity}, {@link ObjectMapper} deserializes the given Json + * into a Java POJO + * + * @param result The result of the previous API response + * @param entity The actual class of the given entity. E.g {@link Waifu#getClass()} + * @param The type of entity 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 deserialize(Result result, Class entity) throws APIMapperException { + Integer statusCode = result.getStatusCode(); + String body = result.getBody(); + T newEntity = null; + + if (statusCode == 200) { + try { + String data = getJsonTree(body); + newEntity = objectMapper.readValue(data, entity); + } catch (JsonProcessingException jpe) { + throwAPIMapperException(jpe); + } + } + + return new Response<>(statusCode, body, newEntity); + } + + /** + * Using the given {@code entity}, {@link ObjectMapper} deserializes the given Json + * into a Java POJO. This method enables support for retrieving {@link List} of entities + * + * @param result The result of the previous API response + * @param entity The actual class of the given entity. E.g {@link Waifu#getClass()} + * @param List of entities to be returned. E.g {@link List} of {@link FilteredSeries} + * @return {@link Response} + * @throws APIMapperException If {@link ObjectMapper} is not able to deserialize JSON to Java POJO properly + */ + Response> deserializeToList(Result result, Class entity) throws APIMapperException { + Integer statusCode = result.getStatusCode(); + String body = result.getBody(); + List listOfEntity = null; + + if (statusCode == 200) { + try { + String data = getJsonTree(body); + listOfEntity = objectMapper.readValue(data, listOf(entity)); + } catch (JsonProcessingException jpe) { + throwAPIMapperException(jpe); + } + } + + return new Response<>(statusCode, body, listOfEntity); + } + + /** + * Helper method for reducing duplicate code in {@code deserialize()} + * and {@code deserializeToList()} + *
+ * Returns the proper json data to deserialize + * + * @param jsonBody jsonBody returned by the API + * @return {@link String} + * @throws JsonProcessingException If {@link ObjectMapper} is not able to + * read the given {@code jsonBody} + */ + private String getJsonTree(String jsonBody) throws JsonProcessingException { + JsonNode parent = objectMapper.readTree(jsonBody); + return parent.get("data").toString(); + } + + + /** + * Helper method for reducing duplicate code in {@code deserialize()} + * and {@code deserializeToList()} + * + * @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, " + + "this is more than likely a fault in my logic. " + + "Please raise an issue including the printed stacktrace :D"; + String exceptionMessage = "\n\n" + customExceptionMessage + "\n\n" + throwable.getMessage(); + + throw new APIMapperException(exceptionMessage, throwable); + } +} diff --git a/src/main/java/org/goudham/me/MyWaifuWrapper.java b/src/main/java/org/goudham/me/APIWrapper.java similarity index 64% rename from src/main/java/org/goudham/me/MyWaifuWrapper.java rename to src/main/java/org/goudham/me/APIWrapper.java index aa71979..8838ffe 100644 --- a/src/main/java/org/goudham/me/MyWaifuWrapper.java +++ b/src/main/java/org/goudham/me/APIWrapper.java @@ -1,8 +1,6 @@ package org.goudham.me; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import org.goudham.me.api.entity.series.FilteredSeries; import org.goudham.me.api.entity.series.Series; import org.goudham.me.api.entity.waifu.Waifu; import org.goudham.me.exception.APIMapperException; @@ -13,6 +11,7 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -20,19 +19,24 @@ import java.util.concurrent.ExecutionException; /** * Returns API information to {@link MyWaifuClient} */ -public class MyWaifuWrapper { +public class APIWrapper { private final String version = "1.0"; private static final String host = "https://mywaifulist.moe/api/v1/"; private final String apiKey; - private final ObjectMapper objectMapper = new ObjectMapper(); + private final APIMapper apiMapper; /** - * Instantiates an instance of {@link MyWaifuWrapper} to retrieve API Information + * Instantiates an instance of {@link APIWrapper} to retrieve API Information. + * An instance of {@link APIMapper} is created to be able to {@link APIMapper#deserialize(Result, Class)} JSON to + * Java objects + * * @param apiKey API Key to authorise API request + * */ - MyWaifuWrapper(String apiKey) { + APIWrapper(String apiKey) { this.apiKey = apiKey; + apiMapper = new APIMapper(); } @@ -70,35 +74,18 @@ public class MyWaifuWrapper { return new Result(responseCode, responseBody); } - private Response getPopulatedResponse(Result result, Class entity) throws APIMapperException { - Integer statusCode = result.getStatusCode(); - String body = result.getBody(); - T newEntity = null; - - if (statusCode == 200) { - try { - JsonNode parent = objectMapper.readTree(body); - String data = parent.get("data").toString(); - newEntity = objectMapper.readValue(data, entity); - } catch (JsonProcessingException jpe) { - 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 = "\n\n" + customExceptionMessage + "\n\n" + jpe.getMessage(); - - throw new APIMapperException(exceptionMessage, jpe); - } - } - - return new Response<>(statusCode, body, newEntity); - } - Response getWaifu(HttpClient httpClient, String param) throws APIResponseException, APIMapperException { Result waifuResult = sendRequest(httpClient, "waifu/" + param); - return getPopulatedResponse(waifuResult, Waifu.class); + return apiMapper.deserialize(waifuResult, Waifu.class); } Response getSeries(HttpClient httpClient, String param) throws APIResponseException, APIMapperException { Result seriesResult = sendRequest(httpClient, "series/" + param); - return getPopulatedResponse(seriesResult, Series.class); + return apiMapper.deserialize(seriesResult, Series.class); + } + + Response> getAiringAnime(HttpClient httpClient) throws APIResponseException, APIMapperException { + Result seriesResult = sendRequest(httpClient, "airing"); + return apiMapper.deserializeToList(seriesResult, FilteredSeries.class); } } diff --git a/src/main/java/org/goudham/me/MyWaifuClient.java b/src/main/java/org/goudham/me/MyWaifuClient.java index fbcff31..ce5ce2d 100644 --- a/src/main/java/org/goudham/me/MyWaifuClient.java +++ b/src/main/java/org/goudham/me/MyWaifuClient.java @@ -1,5 +1,6 @@ package org.goudham.me; +import org.goudham.me.api.entity.series.FilteredSeries; import org.goudham.me.api.entity.series.Series; import org.goudham.me.api.entity.waifu.Waifu; import org.goudham.me.exception.APIMapperException; @@ -12,6 +13,7 @@ import java.net.CookieHandler; import java.net.ProxySelector; import java.net.http.HttpClient; import java.time.Duration; +import java.util.List; import java.util.concurrent.Executor; /** @@ -19,20 +21,20 @@ import java.util.concurrent.Executor; * * *

Main entry point for retrieving information from MyWaifuList.

- *

{@link MyWaifuWrapper} is utilised to make the API requests

+ *

{@link APIWrapper} is utilised to make the API requests

*/ public class MyWaifuClient { - private final MyWaifuWrapper myWaifuWrapper; + private final APIWrapper APIWrapper; private HttpClient httpClient; /** - * Creates an instance of MyWaifuClient + * Creates an instance of {@link MyWaifuClient} * *

See MyWaifuList for obtaining an API Key

* @param apiKey API Key to authorise API request */ MyWaifuClient(@NotNull String apiKey) { - myWaifuWrapper = new MyWaifuWrapper(apiKey); + APIWrapper = new APIWrapper(apiKey); } /** @@ -53,15 +55,19 @@ public class MyWaifuClient { } public Response getWaifu(String slug) throws APIResponseException, APIMapperException { - return myWaifuWrapper.getWaifu(httpClient, slug); + return APIWrapper.getWaifu(httpClient, slug); } public Response getWaifu(Integer id) throws APIResponseException, APIMapperException { - return myWaifuWrapper.getWaifu(httpClient, String.valueOf(id)); + return APIWrapper.getWaifu(httpClient, String.valueOf(id)); } public Response getSeries(Integer id) throws APIMapperException, APIResponseException { - return myWaifuWrapper.getSeries(httpClient, String.valueOf(id)); + return APIWrapper.getSeries(httpClient, String.valueOf(id)); + } + + public Response> getAiringAnime() throws APIMapperException, APIResponseException { + return APIWrapper.getAiringAnime(httpClient); } /** @@ -78,12 +84,12 @@ public class MyWaifuClient { */ public static class Builder { private final String apiKey; - private final MyWaifuWrapper myWaifuWrapper; + private final APIWrapper APIWrapper; private HttpClient.Builder httpClientBuilder; public Builder(String apiKey) { this.apiKey = apiKey; - myWaifuWrapper = new MyWaifuWrapper(apiKey); + APIWrapper = new APIWrapper(apiKey); } public Builder withCookieHandler(CookieHandler cookieHandler) { diff --git a/src/main/java/org/goudham/me/Response.java b/src/main/java/org/goudham/me/Response.java index 56fa3fc..61c9079 100644 --- a/src/main/java/org/goudham/me/Response.java +++ b/src/main/java/org/goudham/me/Response.java @@ -4,8 +4,7 @@ import org.goudham.me.api.entity.series.Series; import org.goudham.me.api.entity.waifu.Waifu; /** - * This is returned to the User when called by methods in {@link MyWaifuClient} - *
+ * This is returned to the User when called by methods in {@link MyWaifuClient}. * E.g {@link MyWaifuClient#getWaifu(Integer)} *
* Given a successful response, {@link #entity} will be populated with the requested entity. diff --git a/src/main/java/org/goudham/me/exception/APIMapperException.java b/src/main/java/org/goudham/me/exception/APIMapperException.java index c016e9f..79bad38 100644 --- a/src/main/java/org/goudham/me/exception/APIMapperException.java +++ b/src/main/java/org/goudham/me/exception/APIMapperException.java @@ -1,11 +1,10 @@ package org.goudham.me.exception; - -import org.goudham.me.MyWaifuWrapper; +import org.goudham.me.APIWrapper; import org.goudham.me.Response; /** - * Thrown when {@link MyWaifuWrapper} fails to unmarshal json into Java POJO's ({@link Response#getEntity()}) + * Thrown when {@link APIWrapper} fails to deserialize json into Java POJO's ({@link Response#getEntity()}) * */ public class APIMapperException extends Throwable { diff --git a/src/main/java/org/goudham/me/exception/APIResponseException.java b/src/main/java/org/goudham/me/exception/APIResponseException.java index bfecdaf..53259d2 100644 --- a/src/main/java/org/goudham/me/exception/APIResponseException.java +++ b/src/main/java/org/goudham/me/exception/APIResponseException.java @@ -1,10 +1,9 @@ package org.goudham.me.exception; - -import org.goudham.me.MyWaifuWrapper; +import org.goudham.me.APIWrapper; /** - * Thrown when {@link MyWaifuWrapper} fails to return API information + * Thrown when {@link APIWrapper} fails to return API information * */ public class APIResponseException extends Throwable {