From da992e864bf38501c692b177972b1a137e3998b7 Mon Sep 17 00:00:00 2001 From: Colin McDonald Date: Thu, 14 Jul 2016 20:16:05 -0400 Subject: [PATCH] Make POST /users/:id/login fully asynchronous --- src/main/java/net/frozenorb/apiv3/APIv3.java | 2 +- .../java/net/frozenorb/apiv3/model/User.java | 37 ++++++++ .../apiv3/route/users/POSTUsersIdLogin.java | 92 ++++++++++++------- 3 files changed, 95 insertions(+), 36 deletions(-) diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 8a6adfb..82ebd7d 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -372,7 +372,7 @@ public final class APIv3 extends AbstractVerticle { http.get("/users/:userId/verifyPassword").blockingHandler(new GETUsersIdVerifyPassword(), false); http.post("/users/:userId/changePassword").blockingHandler(new POSTUsersIdChangePassword(), false); http.post("/users/:userId/confirmPhone").blockingHandler(new POSTUsersIdConfirmPhone(), false); - http.post("/users/:userId/login").blockingHandler(new POSTUsersIdLogin()); + http.post("/users/:userId/login").handler(new POSTUsersIdLogin()); http.post("/users/:userId/notify").blockingHandler(new POSTUsersIdNotify(), false); http.post("/users/:userId/passwordReset").blockingHandler(new POSTUsersIdPasswordReset(), false); http.post("/users/:userId/registerEmail").blockingHandler(new POSTUsersIdRegisterEmail(), false); diff --git a/src/main/java/net/frozenorb/apiv3/model/User.java b/src/main/java/net/frozenorb/apiv3/model/User.java index 27db18e..33794fd 100644 --- a/src/main/java/net/frozenorb/apiv3/model/User.java +++ b/src/main/java/net/frozenorb/apiv3/model/User.java @@ -79,6 +79,43 @@ public final class User { } } + + public static void findOrCreateById(UUID id, String username, SingleResultCallback callback) { + if (!UuidUtils.isAcceptableUuid(id)) { + callback.onResult(null, null); + return; + } + + usersCollection.find(new Document("_id", id)).first(SyncUtils.vertxWrap((user, error) -> { + if (error != null) { + callback.onResult(null, error); + return; + } + + if (user != null) { + callback.onResult(user, null); + return; + } + + User created = new User(id, username); + + created.checkNameCollisions((ignored, nameCollisionsError) -> { + if (nameCollisionsError != null) { + callback.onResult(null, nameCollisionsError); + return; + } + + created.insert((ignored2, insertUserError) -> { + if (insertUserError != null) { + callback.onResult(null, insertUserError); + } else { + callback.onResult(created, null); + } + }); + }); + })); + } + public static void findByPhone(String phoneNumber, SingleResultCallback callback) { usersCollection.find(new Document("$or", ImmutableList.of( new Document("phone", phoneNumber), diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java index aa769f2..567a249 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdLogin.java @@ -1,6 +1,7 @@ package net.frozenorb.apiv3.route.users; import com.google.common.collect.ImmutableMap; +import com.mongodb.async.SingleResultCallback; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; @@ -12,7 +13,6 @@ import net.frozenorb.apiv3.model.Server; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.util.ErrorUtils; import net.frozenorb.apiv3.util.IpUtils; -import net.frozenorb.apiv3.util.SyncUtils; import net.frozenorb.apiv3.util.UuidUtils; import java.util.UUID; @@ -23,12 +23,11 @@ public final class POSTUsersIdLogin implements Handler { UUID uuid = UuidUtils.parseUuid(ctx.request().getParam("userId")); if (!UuidUtils.isAcceptableUuid(uuid)) { - ErrorUtils.respondInvalidInput(ctx, "UUID \"" + uuid + "\" is not valid - must be version 4 UUID."); + ErrorUtils.respondInvalidInput(ctx, "Uuid \"" + uuid + "\" is not valid."); return; } JsonObject requestBody = ctx.getBodyAsJson(); - User user = SyncUtils.runBlocking(v -> User.findById(uuid, v)); String currentUsername = requestBody.getString("username"); String userIp = requestBody.getString("userIp"); Actor actor = ctx.get("actor"); @@ -45,49 +44,72 @@ public final class POSTUsersIdLogin implements Handler { return; } - if (user == null) { - user = new User(uuid, currentUsername); - User finalUser = user; + User.findOrCreateById(uuid, currentUsername, (user, findUserError) -> { + if (findUserError != null) { + ErrorUtils.respondInternalError(ctx, findUserError); + return; + } - SyncUtils.runBlocking(v -> finalUser.checkNameCollisions(v)); - SyncUtils.runBlocking(v -> finalUser.insert(v)); - } + incrementIpLog(user, userIp, (ignored, ipLogError) -> { + if (ipLogError != null) { + ErrorUtils.respondInternalError(ctx, ipLogError); + return; + } - User finalUser = user; - IpLogEntry ipLogEntry = SyncUtils.runBlocking(v -> IpLogEntry.findByUserAndIp(finalUser, userIp, v)); + updateUsername(user, currentUsername, (ignored2, updateUsernameError) -> { + if (updateUsernameError != null) { + ErrorUtils.respondInternalError(ctx, updateUsernameError); + return; + } - // We use a little bit more verbose code here to save on the - // overhead of a .insert() immediately followed by a .save() - if (ipLogEntry == null) { - ipLogEntry = new IpLogEntry(user, userIp); - ipLogEntry.used(); + user.seenOnServer(actorServer); + user.save((ignored3, saveUserError) -> { + if (saveUserError != null) { + ErrorUtils.respondInternalError(ctx, saveUserError); + return; + } - IpLogEntry finalIpLogEntry = ipLogEntry; - SyncUtils.runBlocking(v -> finalIpLogEntry.insert(v)); - } else { - ipLogEntry.used(); + user.getLoginInfo(actorServer, userIp, (loginInfo, loginInfoError) -> { + if (loginInfoError != null) { + ErrorUtils.respondInternalError(ctx, loginInfoError); + } else { + APIv3.respondJson(ctx, 200, loginInfo); + } + }); + }); + }); + }); + }); + } - IpLogEntry finalIpLogEntry = ipLogEntry; - SyncUtils.runBlocking(v -> finalIpLogEntry.save(v)); - } + public void incrementIpLog(User user, String userIp, SingleResultCallback callback) { + IpLogEntry.findByUserAndIp(user, userIp, (existingEntry, error) -> { + if (error != null) { + callback.onResult(null, error); + return; + } + if (existingEntry != null) { + existingEntry.used(); + existingEntry.save(callback); + return; + } + + IpLogEntry inserted = new IpLogEntry(user, userIp); + inserted.used(); + inserted.insert(callback); + }); + } + + public void updateUsername(User user, String currentUsername, SingleResultCallback callback) { String lastUsername = user.getLastUsername(); user.updateUsername(currentUsername); if (!currentUsername.equals(lastUsername)) { - SyncUtils.runBlocking(v -> finalUser.checkNameCollisions(v)); + user.checkNameCollisions(callback); + } else { + callback.onResult(null, null); } - - finalUser.seenOnServer(actorServer); - SyncUtils.runBlocking(v -> finalUser.save(v)); - - user.getLoginInfo(actorServer, userIp, (loginInfo, error) -> { - if (error != null) { - ErrorUtils.respondInternalError(ctx, error); - } else { - APIv3.respondJson(ctx, 200, loginInfo); - } - }); } } \ No newline at end of file