Skip to content

Commit

Permalink
Moved the Birthdays Endpoint to Completable Futures
Browse files Browse the repository at this point in the history
  • Loading branch information
beanbeanjuice committed Jun 24, 2024
1 parent 0a25ea3 commit 4fae2f0
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 284 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ public class Birthday {
* @param day The {@link Integer day} of the {@link Birthday}.
* @param isAlreadyMentioned False, if the user's birthday has not been mentioned by cafeBot.
*/
public Birthday(final BirthdayMonth month, final int day, final String timeZone,
final boolean isAlreadyMentioned) throws InvalidTimeZoneException, BirthdayOverfillException {
public Birthday(final BirthdayMonth month, final int day, final String timeZone, final boolean isAlreadyMentioned) {
this.month = month;
this.day = day;

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.beanbeanjuice.cafeapi.wrapper.exception.api.CafeException;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

/**
* A class used to retrieve random pictures from the {@link CafeAPI CafeAPI}.
Expand All @@ -35,24 +37,23 @@ public InteractionPicturesEndpoint(final CafeAPI cafeAPI) {

/**
* Retrieves a random {@link String interactionURL} for a specified {@link InteractionType}.
*
* @param type The {@link InteractionType} specified.
* @return The {@link String url} to the {@link Interaction Interaction} image.
* @throws AuthorizationException Thrown when the {@link String apiKey} is invalid.
* @throws ResponseException Thrown when there is a generic server-side {@link CafeException CafeException}.
* @throws TeaPotException Thrown when an invalid {@link InteractionType} is entered.
* @return A {@link CompletableFuture} containing an {@link Optional} {@link String url}.
*/
public String getRandomInteractionPicture(final InteractionType type)
throws AuthorizationException, ResponseException, TeaPotException {
public CompletableFuture<Optional<String>> getRandomInteractionPicture(final InteractionType type) {
Optional<String> potentialString = type.getKawaiiAPIString();

if (potentialString.isPresent()) return cafeAPI.getKawaiiAPI().getGifEndpoint().getGIF(potentialString.get()).orElseThrow();
if (potentialString.isPresent()) return cafeAPI.getKawaiiAPI().getGifEndpoint().getGIF(potentialString.get());

Request request = RequestBuilder.create(RequestRoute.CAFEBOT, RequestType.GET)
return RequestBuilder.create(RequestRoute.CAFEBOT, RequestType.GET)
.setRoute("/interaction_pictures/" + type)
.setAuthorization(apiKey)
.build().orElseThrow();

return request.getData().get("url").asText();
.buildAsync()
.thenApplyAsync((optionalRequest) -> {
if (optionalRequest.isPresent()) return Optional.of(optionalRequest.get().getData().get("url").asText());
throw new CompletionException("Unable to get a random interaction picture. Request is empty.", null);
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -34,8 +33,6 @@ public class RequestBuilder {

private final String apiURL;
private String apiKey;
private Consumer<Request> successConsumer;
private Consumer<Exception> errorConsumer;

/**
* Creates a new {@link RequestBuilder}.
Expand Down Expand Up @@ -84,10 +81,7 @@ public RequestBuilder setAuthorization(final String apiKey) {
return this;
}

/**
* Builds the {@link RequestBuilder}.
* @return An {@link Optional} containing the resulting {@link Request}.
*/
// TODO: Change this to private.
public Optional<Request> build() {
try {
URIBuilder uriBuilder = new URIBuilder(apiURL + route);
Expand All @@ -111,15 +105,10 @@ public Optional<Request> build() {
case 500 -> throw new ResponseException(request);
}

if (successConsumer != null) successConsumer.accept(request);

return Optional.of(request);
} catch (URISyntaxException | ExecutionException | InterruptedException | IOException e) {
Logger.getLogger(RequestBuilder.class.getName()).log(Level.WARNING, "Error queuing request: " + e.getMessage());
return Optional.empty();
} catch (Exception e) {
if (errorConsumer != null) errorConsumer.accept(e);
else throw e;
}

// try {
Expand Down Expand Up @@ -155,34 +144,14 @@ public Optional<Request> build() {
// } catch (URISyntaxException | IOException e) {
// return Optional.empty();
// }
return Optional.empty();
}

/**
* Builds and runs the {@link Request} with a provided {@link Consumer<Request> function}.
* @param consumer The {@link Consumer<Request> function} to run.
* @return An {@link Optional} containing the {@link Request}.
*/
public Optional<Request> build(final Consumer<Request> consumer) {
this.successConsumer = consumer;
return build();
}

/**
* Builds the {@link Request} asynchronously on a separate {@link Thread}.
*/
public void buildAsync() {
Thread thread = new Thread(this::build);
thread.start();
}

/**
* Builds the {@link Request} asynchronously on a separate {@link Thread}.
* @param consumer The {@link Consumer} function to run on the {@link Request} once finished.
*/
public void buildAsync(final Consumer<Request> consumer) {
this.successConsumer = consumer;
buildAsync();
public CompletableFuture<Optional<Request>> buildAsync() {
ExecutorService exec = Executors.newSingleThreadExecutor();
return CompletableFuture.supplyAsync(this::build, exec);
}

private HttpResponse get(final SimpleHttpRequest request) throws ExecutionException, InterruptedException, IOException {
Expand All @@ -198,24 +167,6 @@ private HttpResponse get(final SimpleHttpRequest request) throws ExecutionExcept
return response;
}

/**
* The function to run upon a successful 200 return code.
* @param consumer The {@link Consumer} function taking in a {@link Request} to run on.
*/
public RequestBuilder onSuccess(Consumer<Request> consumer) {
this.successConsumer = consumer;
return this;
}

/**
* The function to run upon a failure. This is any code other than 200.
* @param consumer The {@link Consumer} function taking in a {@link Exception} to run on.
*/
public RequestBuilder onError(Consumer<Exception> consumer) {
this.errorConsumer = consumer;
return this;
}

// /**
// * Retrieves the {@link HttpResponse} for a {@link RequestType GET} request.
// * @return The {@link RequestType GET} {@link HttpResponse}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,81 +13,127 @@
import org.junit.jupiter.api.Test;

import java.util.Optional;
import java.util.concurrent.ExecutionException;

public class BirthdayTest {

@Test
@DisplayName("Birthdays Endpoint Test")
public void testBirthdaysEndpoint() {
public void testBirthdaysEndpoint() throws ExecutionException, InterruptedException {
CafeAPI cafeAPI = new CafeAPI("beanbeanjuice", System.getenv("API_PASSWORD"), RequestLocation.BETA);

// Makes sure the user's birthday doesn't exist before starting.
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().removeUserBirthday("178272524533104642"));
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().removeUserBirthday("178272524533104642").get());

// Makes sure the user's birthday cannot be found.
Assertions.assertThrows(NotFoundException.class, () -> cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642"));
try {
cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
Assertions.fail();
} catch (Exception e) {
Assertions.assertInstanceOf(NotFoundException.class, e.getCause());
}

// Makes sure the user's birthday can be created.
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().createUserBirthday("178272524533104642", new Birthday(BirthdayMonth.DECEMBER, 31, "EST", false)));
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().createUserBirthday("178272524533104642", new Birthday(BirthdayMonth.DECEMBER, 31, "EST", false)).get());

// Makes sure the user's birthday cannot be duplicated.
Assertions.assertThrows(ConflictException.class, () -> cafeAPI.getBirthdaysEndpoint().createUserBirthday("178272524533104642", new Birthday(BirthdayMonth.DECEMBER, 20, "EST", false)));
try {
cafeAPI.getBirthdaysEndpoint().createUserBirthday("178272524533104642", new Birthday(BirthdayMonth.DECEMBER, 20, "EST", false)).get();
Assertions.fail();
} catch (Exception e) {
Assertions.assertInstanceOf(ConflictException.class, e.getCause());
}

// Makes sure the month is the same.
Assertions.assertEquals(BirthdayMonth.DECEMBER, cafeAPI.getBirthdaysEndpoint().getAllBirthdays().get("178272524533104642").getMonth());
Assertions.assertEquals(BirthdayMonth.DECEMBER, cafeAPI.getBirthdaysEndpoint().getAllBirthdays().get().get("178272524533104642").getMonth());

// Makes sure the date is the same.
Assertions.assertTrue(() -> {
Optional<Birthday> optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642");
Optional<Birthday> optionalBirthday = null;
try {
optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
} catch (Exception e) {
Assertions.fail();
}
Assertions.assertNotNull(optionalBirthday);
Assertions.assertTrue(optionalBirthday.isPresent());
return optionalBirthday.get().getDay() == 31;
});

// Makes sure a TeaPotException is thrown when there are more days than in the month.
Assertions.assertThrows(BirthdayOverfillException.class, () -> cafeAPI.getBirthdaysEndpoint().updateUserBirthday("178272524533104642", new Birthday(BirthdayMonth.FEBRUARY, 30, "EST", false)));
Assertions.assertThrows(BirthdayOverfillException.class, () -> cafeAPI.getBirthdaysEndpoint().updateUserBirthday("178272524533104642", new Birthday(BirthdayMonth.FEBRUARY, 30, "EST", false)).get());

// Makes sure only a valid month can be set
Assertions.assertThrows(TeaPotException.class, () -> cafeAPI.getBirthdaysEndpoint().updateUserBirthday("178272524533104642", new Birthday(BirthdayMonth.ERROR, 15, "EST", false)));
Assertions.assertThrows(TeaPotException.class, () -> cafeAPI.getBirthdaysEndpoint().updateUserBirthday("178272524533104642", new Birthday(BirthdayMonth.ERROR, 15, "EST", false)).get());

// Makes sure the birthday can be changed.
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().updateUserBirthday("178272524533104642", new Birthday(BirthdayMonth.FEBRUARY, 29, "UTC", false)));
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().updateUserBirthday("178272524533104642", new Birthday(BirthdayMonth.FEBRUARY, 29, "UTC", false)).get());

// Makes sure the changed month is the same.
Assertions.assertTrue(() -> {
Optional<Birthday> birthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642");
Assertions.assertTrue(birthday.isPresent());
return birthday.get().getMonth() == BirthdayMonth.FEBRUARY;
Optional<Birthday> optionalBirthday = null;
try {
optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
} catch (Exception e) {
Assertions.fail();
}
Assertions.assertNotNull(optionalBirthday);
Assertions.assertTrue(optionalBirthday.isPresent());
return optionalBirthday.get().getMonth() == BirthdayMonth.FEBRUARY;
});

// Makes sure the changed day is the same.
Assertions.assertTrue(() -> {
Optional<Birthday> optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642");
Optional<Birthday> optionalBirthday = null;
try {
optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
} catch (Exception e) {
Assertions.fail();
}
Assertions.assertNotNull(optionalBirthday);
Assertions.assertTrue(optionalBirthday.isPresent());
return optionalBirthday.get().getDay() == 29;
});

// Makes sure that alreadyMentioned is false by default.
Assertions.assertFalse(() -> {
Optional<Birthday> optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642");
Optional<Birthday> optionalBirthday = null;
try {
optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
} catch (Exception e) {
Assertions.fail();
}
Assertions.assertNotNull(optionalBirthday);
Assertions.assertTrue(optionalBirthday.isPresent());
return optionalBirthday.get().isAlreadyMentioned();
});

// Makes sure alreadyMentioned can be updated.
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().updateUserBirthdayMention("178272524533104642", true));
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().updateUserBirthdayMention("178272524533104642", true).get());

// Makes sure alreadyMentioned HAS updated.
Assertions.assertTrue(() -> {
Optional<Birthday> optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642");
Optional<Birthday> optionalBirthday = null;
try {
optionalBirthday = cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
} catch (Exception e) {
Assertions.fail();
}
Assertions.assertNotNull(optionalBirthday);
Assertions.assertTrue(optionalBirthday.isPresent());
return optionalBirthday.get().isAlreadyMentioned();
});

// Makes sure the user's birthday can be removed.
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().removeUserBirthday("178272524533104642"));
Assertions.assertTrue(cafeAPI.getBirthdaysEndpoint().removeUserBirthday("178272524533104642").get());

// Makes sure the user no longer exists.
Assertions.assertThrows(NotFoundException.class, () -> cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642"));
try {
cafeAPI.getBirthdaysEndpoint().getUserBirthday("178272524533104642").get();
Assertions.fail();
} catch (Exception e) {
Assertions.assertInstanceOf(NotFoundException.class, e.getCause());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import com.beanbeanjuice.cafeapi.wrapper.CafeAPI;
import com.beanbeanjuice.cafeapi.wrapper.endpoints.interactions.InteractionType;
import com.beanbeanjuice.cafeapi.wrapper.exception.api.AuthorizationException;
import com.beanbeanjuice.cafeapi.wrapper.requests.RequestLocation;
import com.beanbeanjuice.cafeapi.wrapper.endpoints.interactions.pictures.InteractionPicturesEndpoint;
import com.beanbeanjuice.kawaiiapi.wrapper.KawaiiAPI;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
* Tests the {@link InteractionPicturesEndpoint InteractionPictures}.
* This confirms that the {@link CafeAPI} and {@link KawaiiAPI KawaiiAPI} are both working.
Expand All @@ -27,11 +32,33 @@ public void testInteractionPicturesEndpoint() {
Assertions.assertDoesNotThrow(() -> {
for (InteractionType type : InteractionType.values()) {
type.getKawaiiAPIString().ifPresentOrElse(
(kawaiiAPIString) -> Assertions.assertTrue(cafeAPI.getInteractionPicturesEndpoint().getRandomInteractionPicture(type).startsWith("https://api.kawaii.red/gif/")),
(kawaiiAPIString) -> {
try {
Assertions.assertTrue(cafeAPI.getInteractionPicturesEndpoint().getRandomInteractionPicture(type).get().get().startsWith("https://api.kawaii.red/gif/"));
} catch (Exception e) {
Assertions.fail();
}
},
() -> Assertions.assertNotNull(cafeAPI.getInteractionPicturesEndpoint().getRandomInteractionPicture(type))
);
}
});
}

@Test
@DisplayName("Interaction Pictures Endpoint Authorization Error Test")
public void testInteractionPicturesAuthorizationError() throws InterruptedException {
CafeAPI cafeAPI = new CafeAPI("beanbeanjuice", "fake", RequestLocation.BETA);

AtomicReference<Throwable> cause = new AtomicReference<>();

cafeAPI.getInteractionPicturesEndpoint().getRandomInteractionPicture(InteractionType.STAB).exceptionally((exception) -> {
cause.set(exception.getCause());
return Optional.empty();
});

Thread.sleep(1000);
Assertions.assertInstanceOf(AuthorizationException.class, cause.get());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public RequestBuilder setAuthorization(final String apiKey) {
* Builds and runs the {@link Request}.
* @return An {@link Optional} containing the {@link Request}.
*/
public Optional<Request> build() {
private Optional<Request> build() {
try {
SimpleHttpRequest httpRequest = SimpleRequestBuilder.get(apiURL).build();
SimpleHttpResponse httpResponse = (SimpleHttpResponse) get(httpRequest);
Expand Down

0 comments on commit 4fae2f0

Please sign in to comment.