Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class AbstractusException extends RuntimeException {

private final int errorCode;


public AbstractusException(String message, int errorCode) {
this.message = message;
this.errorCode = errorCode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package AbstractusAPI.http.query;

public record Endpoint(String... path) {

public String getPath() {
return "/" + String.join("/", path);
}
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,94 @@
package AbstractusAPI.http.request;

import AbstractusAPI.exception.AbstractusException;
import AbstractusAPI.exception.client_error.*;
import AbstractusAPI.exception.server_error.*;
import AbstractusAPI.exception.server_error.HTTPVersionNotSupportedException;

import okhttp3.Response;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

/**
* Basic implementation of {@link RequestValidator}. Handles most
* status codes that can be returned from an API.
*/
public class BasicRequestValidator implements RequestValidator {

private static final Map<Integer, Class<? extends AbstractusException>> CLIENT_ERROR_MAP = new HashMap<>();
private static final Map<Integer, Class<? extends AbstractusException>> SERVER_ERROR_MAP = new HashMap<>();

static {
// Client error mappings
CLIENT_ERROR_MAP.put(400, BadRequestException.class);
CLIENT_ERROR_MAP.put(401, UnauthorizedException.class);
CLIENT_ERROR_MAP.put(402, PaymentRequiredException.class);
CLIENT_ERROR_MAP.put(403, ForbiddenException.class);
CLIENT_ERROR_MAP.put(404, NotFoundException.class);
CLIENT_ERROR_MAP.put(405, MethodNotAllowedException.class);
CLIENT_ERROR_MAP.put(406, NotAcceptableException.class);
CLIENT_ERROR_MAP.put(407, ProxyAuthenticationRequiredException.class);
CLIENT_ERROR_MAP.put(408, RequestTimeoutException.class);
CLIENT_ERROR_MAP.put(409, ConflictException.class);
CLIENT_ERROR_MAP.put(410, GoneException.class);
CLIENT_ERROR_MAP.put(411, LengthRequiredException.class);
CLIENT_ERROR_MAP.put(412, PreconditionFailedException.class);
CLIENT_ERROR_MAP.put(413, PayloadTooLargeException.class);
CLIENT_ERROR_MAP.put(414, URITooLongException.class);
CLIENT_ERROR_MAP.put(415, UnsupportedMediaTypeException.class);
CLIENT_ERROR_MAP.put(416, RangeNotSatisfiableException.class);
CLIENT_ERROR_MAP.put(417, ExpectationFailedException.class);
CLIENT_ERROR_MAP.put(418, TeapotException.class);
CLIENT_ERROR_MAP.put(419, MisdirectedRequestException.class);
CLIENT_ERROR_MAP.put(422, UnprocessableEntityException.class);
CLIENT_ERROR_MAP.put(423, LockedException.class);
CLIENT_ERROR_MAP.put(424, FailedDependencyException.class);
CLIENT_ERROR_MAP.put(425, TooEarlyException.class);
CLIENT_ERROR_MAP.put(426, UpgradeRequiredException.class);
CLIENT_ERROR_MAP.put(428, PreconditionRequiredException.class);
CLIENT_ERROR_MAP.put(429, RateLimitingException.class);
CLIENT_ERROR_MAP.put(431, RequestHeaderFieldsTooLargeException.class);
CLIENT_ERROR_MAP.put(451, UnavailableForLegalReasonsException.class);

// Server error mappings
SERVER_ERROR_MAP.put(500, InternalServerErrorException.class);
SERVER_ERROR_MAP.put(501, NotImplementedException.class);
SERVER_ERROR_MAP.put(502, BadGatewayException.class);
SERVER_ERROR_MAP.put(503, ServiceUnavailableException.class);
SERVER_ERROR_MAP.put(504, GatewayTimeoutException.class);
SERVER_ERROR_MAP.put(505, HTTPVersionNotSupportedException.class);
SERVER_ERROR_MAP.put(506, VariantAlsoNegotiatesException.class);
SERVER_ERROR_MAP.put(507, InsufficientStorageException.class);
SERVER_ERROR_MAP.put(508, LoopDetectedException.class);
SERVER_ERROR_MAP.put(510, NotExtendedException.class);
SERVER_ERROR_MAP.put(511, NetworkAuthenticationException.class);
}

@Override
public boolean validate(Response response, JSONObject returnObject) {
if (response.isSuccessful()) {
return true;
} else {
String failString = returnObject.getString("cause");

switch (response.code()) {
String failString = returnObject.optString("cause", "Unknown error");
int statusCode = response.code();

// client errors
case 400 -> throw new BadRequestException(failString);
case 401 -> throw new UnauthorizedException(failString);
case 402 -> throw new PaymentRequiredException(failString);
case 403 -> throw new ForbiddenException(failString);
case 404 -> throw new NotFoundException(failString);
case 405 -> throw new MethodNotAllowedException(failString);
case 406 -> throw new NotAcceptableException(failString);
case 407 -> throw new ProxyAuthenticationRequiredException(failString);
case 408 -> throw new RequestTimeoutException(failString);
case 409 -> throw new ConflictException(failString);
case 410 -> throw new GoneException(failString);
case 411 -> throw new LengthRequiredException(failString);
case 412 -> throw new PreconditionFailedException(failString);
case 413 -> throw new PayloadTooLargeException(failString);
case 414 -> throw new URITooLongException(failString);
case 415 -> throw new UnsupportedMediaTypeException(failString);
case 416 -> throw new RangeNotSatisfiableException(failString);
case 417 -> throw new ExpectationFailedException(failString);
case 418 -> throw new TeapotException(failString);
case 419 -> throw new MisdirectedRequestException(failString);
case 422 -> throw new UnprocessableEntityException(failString);
case 423 -> throw new LockedException(failString);
case 424 -> throw new FailedDependencyException(failString);
case 425 -> throw new TooEarlyException(failString);
case 426 -> throw new UpgradeRequiredException(failString);
case 428 -> throw new PreconditionRequiredException(failString);
case 429 -> throw new RateLimitingException(failString);
case 431 -> throw new RequestHeaderFieldsTooLargeException(failString);
case 451 -> throw new UnavailableForLegalReasonsException(failString);

// server errors
case 500 -> throw new InternalServerErrorException(failString);
case 501 -> throw new NotImplementedException(failString);
case 502 -> throw new BadGatewayException(failString);
case 503 -> throw new ServiceUnavailableException(failString);
case 504 -> throw new GatewayTimeoutException(failString);
case 505 -> throw new HTTPVersionNotSupportedException(failString);
case 506 -> throw new VariantAlsoNegotiatesException(failString);
case 507 -> throw new InsufficientStorageException(failString);
case 508 -> throw new LoopDetectedException(failString);
case 510 -> throw new NotExtendedException(failString);
case 511 -> throw new NetworkAuthenticationException(failString);

default -> {
return false;
}
if (CLIENT_ERROR_MAP.containsKey(statusCode)) {
throwException(CLIENT_ERROR_MAP.get(statusCode), failString);
} else if (SERVER_ERROR_MAP.containsKey(statusCode)) {
throwException(SERVER_ERROR_MAP.get(statusCode), failString);
} else {
return false;
}
}
return false;
}

private void throwException (Class <? extends AbstractusException> exceptionClass, String message){
try {
throw exceptionClass.getConstructor(String.class).newInstance(message);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to instantiate exception for status code", e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package AbstractusAPI.http.request;

import AbstractusAPI.http.query.Endpoint;
import AbstractusAPI.http.query.Query;
import AbstractusAPI.http.query.QueryParameter;
import okhttp3.OkHttpClient;
Expand All @@ -22,6 +23,7 @@ public class RequestController {
private final String hostname;
private final RequestFactory requestFactory;


public RequestController(String origin, String hostname) {
this.origin = origin;
this.hostname = hostname;
Expand All @@ -46,24 +48,57 @@ public RequestController(String origin, String hostname, OkHttpClient client, Re
this.requestFactory = new RequestFactory(client, validator);
}

public void setAutoClearCache(boolean autoClear) {
requestFactory.setAutoClearCache(autoClear);
}

public CompletableFuture<JSONObject> sendRequestAsync() {
try {
URL url = new URL(origin, hostname, "/");
Query query = new Query(url);
query.addParameter(queryParameters.toArray(new QueryParameter[0]));
return requestFactory.sendAsync(query);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}

public CompletableFuture<JSONObject> sendRequestAsync(String endpoint, QueryParameter... params) {
public CompletableFuture<JSONObject> sendRequestAsync(QueryParameter... params) {
try {
URL url = new URL(origin, hostname, "/" + endpoint);
URL url = new URL(origin, hostname, "/");
Query query = new Query(url);
query.addParameter(params);
query.addParameter(queryParameters.toArray(new QueryParameter[queryParameters.size()]));
query.addParameter(queryParameters.toArray(new QueryParameter[0]));
return requestFactory.sendAsync(query);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}


public CompletableFuture<JSONObject> sendRequestAsync(Endpoint endpoint, QueryParameter... params) {
try {

// Create the URL object
URL url = new URL(origin, hostname, endpoint.getPath());

// Create and configure the Query object
Query query = new Query(url);
query.addParameter(params);
query.addParameter(queryParameters.toArray(new QueryParameter[0]));

// Send the request asynchronously
return requestFactory.sendAsync(query);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}

public CompletableFuture<JSONObject> sendRequestAsync(String endpoint) {
public CompletableFuture<JSONObject> sendRequestAsync(Endpoint endpoint) {
try {
URL url = new URL(origin, hostname, "/" + endpoint);
URL url = new URL(origin, hostname, endpoint.getPath());
Query query = new Query(url);
query.addParameter(queryParameters.toArray(new QueryParameter[queryParameters.size()]));
query.addParameter(queryParameters.toArray(new QueryParameter[0]));
return requestFactory.sendAsync(query);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
Expand All @@ -75,18 +110,18 @@ public void clearCache() {
}

public void setApiKey(UUID apiKey) {
addPermanentQueryParameter(new QueryParameter("key", apiKey.toString()));
addQueryParameter(new QueryParameter("key", apiKey.toString()));
}

/**
* Adds a parameter to every request being made.
* @param parameter The parameter to add.
*/
public void addPermanentQueryParameter(QueryParameter parameter) {
public void addQueryParameter(QueryParameter parameter) {
queryParameters.add(parameter);
}

public void removePermanentQueryParameter(QueryParameter parameter) {
public void removeQueryParameter(QueryParameter parameter) {
queryParameters.remove(parameter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class RequestFactory {

private final OkHttpClient client;
private final RequestValidator validator;
private boolean autoClearCache;

protected RequestFactory() {
this.validator = new BasicRequestValidator();
Expand All @@ -41,6 +42,10 @@ protected RequestFactory(OkHttpClient client, RequestValidator validator) {
this.validator = validator;
}

protected void setAutoClearCache(boolean autoClear) {
this.autoClearCache = autoClear;
}

private OkHttpClient getClient() {
File httpCacheDirectory = new File("./cacheDir", "http-cache");
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Expand Down Expand Up @@ -69,6 +74,8 @@ public void clearCache() {
public CompletableFuture<JSONObject> sendAsync(Query query) {
Request request = new Request.Builder().url(query.createRequest()).build();

if(autoClearCache) clearCache();

return CompletableFuture.supplyAsync(() -> {
try {
Response response = client.newCall(request).execute();
Expand Down