Merge pull request #7 from sgoudham/main

Add support for retriving current airing anime
pull/8/head
Hamothy 4 years ago committed by GitHub
commit 6466a40b30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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 <T> The type of entity to be returned. E.g {@link Waifu} or {@link Series}
* @return {@link JavaType}
*
*/
private <T> JavaType listOf(Class<T> 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 <T> 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
*
*/
<T> Response<T> deserialize(Result result, Class<T> 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 <T> 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
*/
<T> Response<List<T>> deserializeToList(Result result, Class<T> entity) throws APIMapperException {
Integer statusCode = result.getStatusCode();
String body = result.getBody();
List<T> 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()}
* <br>
* 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);
}
}

@ -1,8 +1,6 @@
package org.goudham.me; package org.goudham.me;
import com.fasterxml.jackson.core.JsonProcessingException; import org.goudham.me.api.entity.series.FilteredSeries;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.goudham.me.api.entity.series.Series; import org.goudham.me.api.entity.series.Series;
import org.goudham.me.api.entity.waifu.Waifu; import org.goudham.me.api.entity.waifu.Waifu;
import org.goudham.me.exception.APIMapperException; import org.goudham.me.exception.APIMapperException;
@ -13,6 +11,7 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.time.Duration; import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -20,19 +19,24 @@ import java.util.concurrent.ExecutionException;
/** /**
* Returns API information to {@link MyWaifuClient} * Returns API information to {@link MyWaifuClient}
*/ */
public class MyWaifuWrapper { public class APIWrapper {
private final String version = "1.0"; private final String version = "1.0";
private static final String host = "https://mywaifulist.moe/api/v1/"; private static final String host = "https://mywaifulist.moe/api/v1/";
private final String apiKey; 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 * @param apiKey API Key to authorise API request
*
*/ */
MyWaifuWrapper(String apiKey) { APIWrapper(String apiKey) {
this.apiKey = apiKey; this.apiKey = apiKey;
apiMapper = new APIMapper();
} }
@ -70,35 +74,18 @@ public class MyWaifuWrapper {
return new Result(responseCode, responseBody); return new Result(responseCode, responseBody);
} }
private <T> Response<T> getPopulatedResponse(Result result, Class<T> 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<Waifu> getWaifu(HttpClient httpClient, String param) throws APIResponseException, APIMapperException { Response<Waifu> getWaifu(HttpClient httpClient, String param) throws APIResponseException, APIMapperException {
Result waifuResult = sendRequest(httpClient, "waifu/" + param); Result waifuResult = sendRequest(httpClient, "waifu/" + param);
return getPopulatedResponse(waifuResult, Waifu.class); return apiMapper.deserialize(waifuResult, Waifu.class);
} }
Response<Series> getSeries(HttpClient httpClient, String param) throws APIResponseException, APIMapperException { Response<Series> getSeries(HttpClient httpClient, String param) throws APIResponseException, APIMapperException {
Result seriesResult = sendRequest(httpClient, "series/" + param); Result seriesResult = sendRequest(httpClient, "series/" + param);
return getPopulatedResponse(seriesResult, Series.class); return apiMapper.deserialize(seriesResult, Series.class);
}
Response<List<FilteredSeries>> getAiringAnime(HttpClient httpClient) throws APIResponseException, APIMapperException {
Result seriesResult = sendRequest(httpClient, "airing");
return apiMapper.deserializeToList(seriesResult, FilteredSeries.class);
} }
} }

@ -1,5 +1,6 @@
package org.goudham.me; 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.series.Series;
import org.goudham.me.api.entity.waifu.Waifu; import org.goudham.me.api.entity.waifu.Waifu;
import org.goudham.me.exception.APIMapperException; import org.goudham.me.exception.APIMapperException;
@ -12,6 +13,7 @@ import java.net.CookieHandler;
import java.net.ProxySelector; import java.net.ProxySelector;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.time.Duration; import java.time.Duration;
import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
/** /**
@ -19,20 +21,20 @@ import java.util.concurrent.Executor;
* *
* *
* <p> Main entry point for retrieving information from MyWaifuList.</p> * <p> Main entry point for retrieving information from MyWaifuList.</p>
* <p> {@link MyWaifuWrapper} is utilised to make the API requests </p> * <p> {@link APIWrapper} is utilised to make the API requests </p>
*/ */
public class MyWaifuClient { public class MyWaifuClient {
private final MyWaifuWrapper myWaifuWrapper; private final APIWrapper APIWrapper;
private HttpClient httpClient; private HttpClient httpClient;
/** /**
* Creates an instance of MyWaifuClient * Creates an instance of {@link MyWaifuClient}
* *
* <p>See <a href="https://mywaifulist.docs.stoplight.io/">MyWaifuList</a> for obtaining an API Key</p> * <p>See <a href="https://mywaifulist.docs.stoplight.io/">MyWaifuList</a> for obtaining an API Key</p>
* @param apiKey API Key to authorise API request * @param apiKey API Key to authorise API request
*/ */
MyWaifuClient(@NotNull String apiKey) { MyWaifuClient(@NotNull String apiKey) {
myWaifuWrapper = new MyWaifuWrapper(apiKey); APIWrapper = new APIWrapper(apiKey);
} }
/** /**
@ -53,15 +55,19 @@ public class MyWaifuClient {
} }
public Response<Waifu> getWaifu(String slug) throws APIResponseException, APIMapperException { public Response<Waifu> getWaifu(String slug) throws APIResponseException, APIMapperException {
return myWaifuWrapper.getWaifu(httpClient, slug); return APIWrapper.getWaifu(httpClient, slug);
} }
public Response<Waifu> getWaifu(Integer id) throws APIResponseException, APIMapperException { public Response<Waifu> getWaifu(Integer id) throws APIResponseException, APIMapperException {
return myWaifuWrapper.getWaifu(httpClient, String.valueOf(id)); return APIWrapper.getWaifu(httpClient, String.valueOf(id));
} }
public Response<Series> getSeries(Integer id) throws APIMapperException, APIResponseException { public Response<Series> getSeries(Integer id) throws APIMapperException, APIResponseException {
return myWaifuWrapper.getSeries(httpClient, String.valueOf(id)); return APIWrapper.getSeries(httpClient, String.valueOf(id));
}
public Response<List<FilteredSeries>> getAiringAnime() throws APIMapperException, APIResponseException {
return APIWrapper.getAiringAnime(httpClient);
} }
/** /**
@ -78,12 +84,12 @@ public class MyWaifuClient {
*/ */
public static class Builder { public static class Builder {
private final String apiKey; private final String apiKey;
private final MyWaifuWrapper myWaifuWrapper; private final APIWrapper APIWrapper;
private HttpClient.Builder httpClientBuilder; private HttpClient.Builder httpClientBuilder;
public Builder(String apiKey) { public Builder(String apiKey) {
this.apiKey = apiKey; this.apiKey = apiKey;
myWaifuWrapper = new MyWaifuWrapper(apiKey); APIWrapper = new APIWrapper(apiKey);
} }
public Builder withCookieHandler(CookieHandler cookieHandler) { public Builder withCookieHandler(CookieHandler cookieHandler) {

@ -4,8 +4,7 @@ import org.goudham.me.api.entity.series.Series;
import org.goudham.me.api.entity.waifu.Waifu; 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}.
* <br>
* E.g {@link MyWaifuClient#getWaifu(Integer)} * E.g {@link MyWaifuClient#getWaifu(Integer)}
* <br> * <br>
* Given a successful response, {@link #entity} will be populated with the requested entity. * Given a successful response, {@link #entity} will be populated with the requested entity.

@ -1,11 +1,10 @@
package org.goudham.me.exception; package org.goudham.me.exception;
import org.goudham.me.APIWrapper;
import org.goudham.me.MyWaifuWrapper;
import org.goudham.me.Response; 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 { public class APIMapperException extends Throwable {

@ -1,10 +1,9 @@
package org.goudham.me.exception; package org.goudham.me.exception;
import org.goudham.me.APIWrapper;
import org.goudham.me.MyWaifuWrapper;
/** /**
* Thrown when {@link MyWaifuWrapper} fails to return API information * Thrown when {@link APIWrapper} fails to return API information
* *
*/ */
public class APIResponseException extends Throwable { public class APIResponseException extends Throwable {

Loading…
Cancel
Save