diff --git a/pom.xml b/pom.xml index 294419cc..2fd25d34 100644 --- a/pom.xml +++ b/pom.xml @@ -22,11 +22,11 @@ 6.9.0.202403050737-r - + - com.sparkjava - spark-core - 2.9.4 + io.javalin + javalin + 6.3.0 diff --git a/src/main/java/edu/byu/cs/controller/AdminController.java b/src/main/java/edu/byu/cs/controller/AdminController.java index b4c3fff5..20e38ffc 100755 --- a/src/main/java/edu/byu/cs/controller/AdminController.java +++ b/src/main/java/edu/byu/cs/controller/AdminController.java @@ -1,106 +1,63 @@ package edu.byu.cs.controller; -import edu.byu.cs.canvas.CanvasException; import edu.byu.cs.canvas.model.CanvasSection; -import edu.byu.cs.dataAccess.DataAccessException; -import edu.byu.cs.dataAccess.ItemNotFoundException; +import edu.byu.cs.controller.exception.ResourceNotFoundException; import edu.byu.cs.model.User; import edu.byu.cs.service.AdminService; -import edu.byu.cs.util.Serializer; +import io.javalin.http.Handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import spark.Route; import java.io.OutputStream; import java.util.Collection; import static edu.byu.cs.util.JwtUtils.generateToken; -import static spark.Spark.halt; public class AdminController { private static final Logger LOGGER = LoggerFactory.getLogger(AdminController.class); - public static final Route usersGet = (req, res) -> { - Collection users; - try { - users = AdminService.getUsers(); - } catch (DataAccessException e) { - halt(500); - return null; - } - - res.type("application/json"); - res.status(200); - - return Serializer.serialize(users); - + public static final Handler usersGet = ctx -> { + Collection users = AdminService.getUsers(); + ctx.json(users); }; - public static final Route testModeGet = (req, res) -> { - User testStudent; - try { - testStudent = AdminService.updateTestStudent(); - } catch (CanvasException | DataAccessException e) { - halt(500); - return null; - } - - res.cookie("/", "token", generateToken(testStudent.netId()), 14400, false, false); - - res.status(200); - - return null; + public static final Handler testModeGet = ctx -> { + User testStudent = AdminService.updateTestStudent(); + ctx.cookie("token", generateToken(testStudent.netId()), 14400); }; - public static final Route commitAnalyticsGet = (req, res) -> { - String option = req.params(":option"); - String data; - + public static final Handler commitAnalyticsGet = ctx -> { + String option = ctx.pathParam("option"); try { - data = AdminService.getCommitAnalytics(option); + String data = AdminService.getCommitAnalytics(option); + ctx.result(data); + } catch (IllegalStateException e) { + LOGGER.error(e.getMessage()); + throw new ResourceNotFoundException(e.getMessage(), e); } catch (Exception e) { LOGGER.error(e.getMessage()); - if (e instanceof IllegalStateException) res.status(404); - else res.status(500); - return e.getMessage(); + throw e; } - - res.status(200); - - return data; }; - public static final Route honorCheckerZipGet = (req, res) -> { - String sectionStr = req.params(":section"); + public static final Handler honorCheckerZipGet = ctx -> { + String sectionStr = ctx.pathParam("section"); - try (OutputStream os = res.raw().getOutputStream()) { + try (OutputStream os = ctx.res().getOutputStream()) { AdminService.streamHonorCheckerZip(sectionStr, os); } catch (Exception e) { LOGGER.error("Error compiling honor checker", e); - res.status(500); - return e.getMessage(); + throw e; } - res.status(200); - - res.header("Content-Type", "application/zip"); - res.header("Content-Disposition", "attachment; filename=" + "downloaded_file.zip"); - - return res.raw(); - + ctx.header("Content-Type", "application/zip"); + ctx.header("Content-Disposition", "attachment; filename=" + "downloaded_file.zip"); }; - public static Route sectionsGet = (req, res) -> { - try { - CanvasSection[] sections = AdminService.getAllSections(); - res.type("application/json"); - res.status(200); - return Serializer.serialize(sections); - } catch (CanvasException e) { - res.status(500); - return e.getMessage(); - } + public static Handler sectionsGet = ctx -> { + CanvasSection[] sections = AdminService.getAllSections(); + ctx.json(sections); }; } diff --git a/src/main/java/edu/byu/cs/controller/AuthController.java b/src/main/java/edu/byu/cs/controller/AuthController.java index 2a0a2975..c01fd835 100644 --- a/src/main/java/edu/byu/cs/controller/AuthController.java +++ b/src/main/java/edu/byu/cs/controller/AuthController.java @@ -1,39 +1,38 @@ package edu.byu.cs.controller; +import edu.byu.cs.controller.exception.BadRequestException; +import edu.byu.cs.controller.exception.InternalServerException; +import edu.byu.cs.controller.exception.ResourceForbiddenException; +import edu.byu.cs.controller.exception.UnauthorizedException; import edu.byu.cs.dataAccess.DaoService; import edu.byu.cs.dataAccess.DataAccessException; import edu.byu.cs.dataAccess.UserDao; import edu.byu.cs.model.User; -import edu.byu.cs.util.Serializer; +import io.javalin.http.Handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import spark.Filter; -import spark.Route; import static edu.byu.cs.util.JwtUtils.validateToken; -import static spark.Spark.halt; public class AuthController { private static final Logger LOGGER = LoggerFactory.getLogger(AuthController.class); /** - * A filter that verifies that the request has a valid JWT in the Authorization header. + * A handler that verifies that the request has a valid JWT in the Authorization header. * If the request is valid, the netId is added to the session for later use. */ - public static final Filter verifyAuthenticatedMiddleware = (req, res) -> { - String token = req.cookie("token"); + public static final Handler verifyAuthenticatedMiddleware = ctx -> { + String token = ctx.cookie("token"); if (token == null) { - halt(401); - return; + throw new UnauthorizedException(); } String netId = validateToken(token); // token is expired or invalid if (netId == null) { - res.cookie("/", "token", "", 0, false, false); - halt(401); - return; + ctx.cookie("token", "", 0); + throw new UnauthorizedException(); } UserDao userDao = DaoService.getUserDao(); @@ -42,33 +41,36 @@ public class AuthController { user = userDao.getUser(netId); } catch (DataAccessException e) { LOGGER.error("Error getting user from database", e); - halt(500); - return; + throw new InternalServerException("Error getting user from database", e); } if (user == null) { LOGGER.error("Received request from unregistered user. This shouldn't be possible: {}", netId); - halt(400, "You must register first."); - return; + throw new BadRequestException("You must register first."); } - req.session().attribute("user", user); + ctx.sessionAttribute("user", user); }; - public static final Filter verifyAdminMiddleware = (req, res) -> { - User user = req.session().attribute("user"); + public static final Handler verifyAdminMiddleware = ctx -> { + User user = ctx.sessionAttribute("user"); + + if (user == null) { + throw new UnauthorizedException("No user credentials found"); + } if (user.role() != User.Role.ADMIN) { - halt(403); + throw new ResourceForbiddenException(); } }; - public static final Route meGet = (req, res) -> { - User user = req.session().attribute("user"); - - res.status(200); - res.type("application/json"); - return Serializer.serialize(user); + public static final Handler meGet = ctx -> { + User user = ctx.sessionAttribute("user"); + if (user == null) { + ctx.result("No user found."); + } else { + ctx.json(user); + } }; } diff --git a/src/main/java/edu/byu/cs/controller/CasController.java b/src/main/java/edu/byu/cs/controller/CasController.java index 0ba5d204..0df4f1f0 100644 --- a/src/main/java/edu/byu/cs/controller/CasController.java +++ b/src/main/java/edu/byu/cs/controller/CasController.java @@ -1,66 +1,53 @@ package edu.byu.cs.controller; import edu.byu.cs.canvas.CanvasException; -import edu.byu.cs.controller.exception.BadRequestException; -import edu.byu.cs.controller.exception.InternalServerException; -import edu.byu.cs.dataAccess.DataAccessException; import edu.byu.cs.model.User; import edu.byu.cs.properties.ApplicationProperties; import edu.byu.cs.service.CasService; -import spark.Route; +import io.javalin.http.Handler; +import io.javalin.http.HttpStatus; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import static edu.byu.cs.util.JwtUtils.generateToken; -import static spark.Spark.halt; public class CasController { - public static final Route callbackGet = (req, res) -> { - String ticket = req.queryParams("ticket"); + public static final Handler callbackGet = ctx -> { + String ticket = ctx.queryParam("ticket"); User user; try { user = CasService.callback(ticket); - } catch (InternalServerException | DataAccessException e) { - halt(500); - return null; - } catch (BadRequestException e) { - halt(400); - return null; } catch (CanvasException e) { String errorUrlParam = URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8); - res.redirect(ApplicationProperties.frontendUrl() + "/login?error=" + errorUrlParam, 302); - halt(500); - return null; + ctx.redirect(ApplicationProperties.frontendUrl() + "/login?error=" + errorUrlParam, HttpStatus.FOUND); + return; } // FIXME: secure cookie with httpOnly - res.cookie("/", "token", generateToken(user.netId()), 14400, false, false); - res.redirect(ApplicationProperties.frontendUrl(), 302); - return null; + ctx.cookie("token", generateToken(user.netId()), 14400); + ctx.redirect(ApplicationProperties.frontendUrl(), HttpStatus.FOUND); }; - public static final Route loginGet = (req, res) -> { + public static final Handler loginGet = ctx -> { // check if already logged in - if (req.cookie("token") != null) { - res.redirect(ApplicationProperties.frontendUrl(), 302); - return null; + if (ctx.cookie("token") != null) { + ctx.redirect(ApplicationProperties.frontendUrl(), HttpStatus.FOUND); + return; } - res.redirect(CasService.BYU_CAS_URL + "/login" + "?service=" + ApplicationProperties.casCallbackUrl()); - return null; + ctx.redirect(CasService.BYU_CAS_URL + "/login" + "?service=" + ApplicationProperties.casCallbackUrl()); }; - public static final Route logoutPost = (req, res) -> { - if (req.cookie("token") == null) { - res.redirect(ApplicationProperties.frontendUrl(), 401); - return null; + public static final Handler logoutPost = ctx -> { + if (ctx.cookie("token") == null) { + ctx.redirect(ApplicationProperties.frontendUrl(), HttpStatus.UNAUTHORIZED); + return; } // TODO: call cas logout endpoint with ticket - res.removeCookie("/", "token"); - res.redirect(ApplicationProperties.frontendUrl(), 200); - return null; + ctx.removeCookie("token", "/"); + ctx.redirect(ApplicationProperties.frontendUrl(), HttpStatus.OK); }; } diff --git a/src/main/java/edu/byu/cs/controller/ConfigController.java b/src/main/java/edu/byu/cs/controller/ConfigController.java index 5bba024e..4e3af51c 100644 --- a/src/main/java/edu/byu/cs/controller/ConfigController.java +++ b/src/main/java/edu/byu/cs/controller/ConfigController.java @@ -1,75 +1,60 @@ package edu.byu.cs.controller; import com.google.gson.JsonObject; +import edu.byu.cs.controller.exception.BadRequestException; +import edu.byu.cs.controller.exception.UnauthorizedException; import edu.byu.cs.dataAccess.DataAccessException; import edu.byu.cs.model.*; import edu.byu.cs.model.request.ConfigPenaltyUpdateRequest; import edu.byu.cs.service.ConfigService; import edu.byu.cs.util.Serializer; -import spark.Route; +import io.javalin.http.Handler; import java.util.ArrayList; -import static spark.Spark.halt; - public class ConfigController { - public static final Route getConfigAdmin = (req, res) -> { - try { - JsonObject response = ConfigService.getPrivateConfig(); - res.status(200); - return response; - } catch (DataAccessException e) { - res.status(500); - res.body(e.getMessage()); - return res; - } + public static final Handler getConfigAdmin = ctx -> { + JsonObject configJsonObj = ConfigService.getPrivateConfig(); + ctx.result(configJsonObj.toString()); }; - public static final Route getConfigStudent = (req, res) -> { - String response = ConfigService.getPublicConfig().toString(); - - res.status(200); - return response; + public static final Handler getConfigStudent = ctx -> { + JsonObject configJsonObj = ConfigService.getPublicConfig(); + ctx.result(configJsonObj.toString()); }; - public static final Route updateLivePhases = (req, res) -> { - JsonObject jsonObject = Serializer.deserialize(req.body(), JsonObject.class); + public static final Handler updateLivePhases = ctx -> { + JsonObject jsonObject = Serializer.deserialize(ctx.body(), JsonObject.class); ArrayList phasesArray = Serializer.deserialize(jsonObject.get("phases"), ArrayList.class); - User user = req.session().attribute("user"); - ConfigService.updateLivePhases(phasesArray, user); + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); + } - res.status(200); - return ""; + ConfigService.updateLivePhases(phasesArray, user); }; - public static final Route scheduleShutdown = (req, res) -> { - User user = req.session().attribute("user"); + public static final Handler scheduleShutdown = ctx -> { + User user = ctx.sessionAttribute("user"); - JsonObject jsonObject = Serializer.deserialize(req.body(), JsonObject.class); + JsonObject jsonObject = ctx.bodyAsClass(JsonObject.class); String shutdownTimestampString = Serializer.deserialize(jsonObject.get("shutdownTimestamp"), String.class); Integer shutdownWarningMilliseconds = Serializer.deserialize(jsonObject.get("shutdownWarningMilliseconds"), Integer.class); try { ConfigService.scheduleShutdown(user, shutdownTimestampString); ConfigService.setShutdownWarningDuration(user, shutdownWarningMilliseconds); - } catch (DataAccessException e) { - halt(500, e.getMessage()); - return null; } catch (IllegalArgumentException e) { - halt(400, e.getMessage()); - return null; + throw new BadRequestException(e.getMessage()); } - - res.status(200); - return ""; }; - public static final Route updateBannerMessage = (req, res) -> { - User user = req.session().attribute("user"); + public static final Handler updateBannerMessage = ctx -> { + User user = ctx.sessionAttribute("user"); - JsonObject jsonObject = Serializer.deserialize(req.body(), JsonObject.class); + JsonObject jsonObject = Serializer.deserialize(ctx.body(), JsonObject.class); String expirationString = Serializer.deserialize(jsonObject.get("bannerExpiration"), String.class); String message = Serializer.deserialize(jsonObject.get("bannerMessage"), String.class); @@ -79,51 +64,36 @@ public class ConfigController { try { ConfigService.updateBannerMessage(user, expirationString, message, link, color); } catch (IllegalArgumentException e) { - halt(400, e.getMessage()); - return null; + throw new BadRequestException(e.getMessage(), e); } - - res.status(200); - return ""; }; - public static final Route updateCourseIdsPost = (req, res) -> { - SetCourseIdsRequest setCourseIdsRequest = Serializer.deserialize(req.body(), SetCourseIdsRequest.class); + public static final Handler updateCourseIdsPost = ctx -> { + SetCourseIdsRequest setCourseIdsRequest = ctx.bodyAsClass(SetCourseIdsRequest.class); - User user = req.session().attribute("user"); + User user = ctx.sessionAttribute("user"); // Course Number try { ConfigService.updateCourseIds(user, setCourseIdsRequest); } catch (DataAccessException e) { - res.status(400); - res.body(e.getMessage()); - return res; + ctx.status(400); + ctx.result(e.getMessage()); } - - res.status(200); - return ""; }; - public static final Route updateCourseIdsUsingCanvasGet = (req, res) -> { - User user = req.session().attribute("user"); + public static final Handler updateCourseIdsUsingCanvasGet = ctx -> { + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); + } ConfigService.updateCourseIdsUsingCanvas(user); - res.status(200); - return ""; }; - public static final Route updatePenalties = (req, res) -> { - User user = req.session().attribute("user"); - - ConfigPenaltyUpdateRequest request = Serializer.deserialize(req.body(), ConfigPenaltyUpdateRequest.class); - - try { - ConfigService.processPenaltyUpdates(user, request); - } catch (DataAccessException e) { - res.status(500); - res.body(e.getMessage()); - } + public static final Handler updatePenalties = ctx -> { + User user = ctx.sessionAttribute("user"); + ConfigPenaltyUpdateRequest request = ctx.bodyAsClass(ConfigPenaltyUpdateRequest.class); - return ""; + ConfigService.processPenaltyUpdates(user, request); }; } diff --git a/src/main/java/edu/byu/cs/controller/SubmissionController.java b/src/main/java/edu/byu/cs/controller/SubmissionController.java index 53908357..2c499e2a 100644 --- a/src/main/java/edu/byu/cs/controller/SubmissionController.java +++ b/src/main/java/edu/byu/cs/controller/SubmissionController.java @@ -1,6 +1,7 @@ package edu.byu.cs.controller; import edu.byu.cs.controller.exception.BadRequestException; +import edu.byu.cs.controller.exception.UnauthorizedException; import edu.byu.cs.controller.netmodel.ApprovalRequest; import edu.byu.cs.controller.netmodel.GradeRequest; import edu.byu.cs.dataAccess.*; @@ -9,212 +10,136 @@ import edu.byu.cs.model.User; import edu.byu.cs.service.SubmissionService; import edu.byu.cs.util.Serializer; +import io.javalin.http.Context; +import io.javalin.http.Handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import spark.Request; -import spark.Route; import java.util.*; -import static spark.Spark.halt; - public class SubmissionController { private static final Logger LOGGER = LoggerFactory.getLogger(SubmissionController.class); - public static final Route submitPost = (req, res) -> { - User user = req.session().attribute("user"); - - GradeRequest request = validateAndUnpackRequest(req); - if (request == null) { - return null; - } - - try { - SubmissionService.submit(user, request); - } catch (BadRequestException e) { - halt(400, e.getMessage()); - return null; - } catch (DataAccessException e) { - halt(500, e.getMessage()); - return null; - } + public static final Handler submitPost = ctx -> { + User user = ctx.sessionAttribute("user"); - res.status(200); - return ""; + GradeRequest request = validateAndUnpackRequest(ctx); + SubmissionService.submit(user, request); }; - public static final Route adminRepoSubmitPost = (req, res) -> { - User user = req.session().attribute("user"); - - GradeRequest request = validateAndUnpackRequest(req); - if (request == null) { - return null; + public static final Handler adminRepoSubmitPost = ctx -> { + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); } - try { - SubmissionService.adminRepoSubmit(user.netId(), request); - } catch (BadRequestException e) { - halt(400, e.getMessage()); - return null; - } catch (DataAccessException e) { - halt(500, e.getMessage()); - return null; - } + GradeRequest request = validateAndUnpackRequest(ctx); - res.status(200); - return ""; + SubmissionService.adminRepoSubmit(user.netId(), request); }; - private static GradeRequest validateAndUnpackRequest(Request req) throws DataAccessException { - User user = req.session().attribute("user"); + private static GradeRequest validateAndUnpackRequest(Context ctx) throws DataAccessException, BadRequestException, UnauthorizedException { + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); + } String netId = user.netId(); if (DaoService.getQueueDao().isAlreadyInQueue(netId)) { - halt(400, "You are already in the queue"); - return null; + throw new BadRequestException("You are already in the queue"); } GradeRequest request; try { - request = Serializer.deserialize(req.body(), GradeRequest.class); + request = ctx.bodyAsClass(GradeRequest.class); } catch (Serializer.SerializationException e) { - halt(400, "Request must be valid json"); - return null; + throw new BadRequestException("Request must be valid json", e); } if (request == null || request.phase() == null) { - halt(400, "Request is invalid"); - return null; + throw new BadRequestException("Request is invalid"); } if (user.repoUrl() == null && user.role() == User.Role.STUDENT) { - halt(400, "Student has no provided repo url"); - return null; + throw new BadRequestException("Student has not provided repo url"); } return request; } - public static final Route submitGet = (req, res) -> { - User user = req.session().attribute("user"); - String netId = user.netId(); - - boolean inQueue = SubmissionService.isAlreadyInQueue(netId); - - res.status(200); - - return Serializer.serialize(Map.of("inQueue", inQueue)); - }; - - public static final Route latestSubmissionForMeGet = (req, res) -> { - User user = req.session().attribute("user"); - - Submission submission; - try { - submission = SubmissionService.getLastSubmissionForUser(user.netId()); - } catch (DataAccessException e) { - halt(500); - return null; + public static final Handler submitGet = ctx -> { + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); } - - res.status(200); - res.type("application/json"); - - return Serializer.serialize(submission); + boolean inQueue = SubmissionService.isAlreadyInQueue(user.netId()); + ctx.json(Map.of("inQueue", inQueue)); }; - public static final Route submissionXGet = (req, res) -> { - String phaseString = req.params(":phase"); - Phase phase = null; - - if (phaseString != null) { - try { - phase = Phase.valueOf(phaseString); - } catch (IllegalArgumentException e) { - LOGGER.error("Invalid phase", e); - halt(400, "Invalid phase"); - } + public static final Handler latestSubmissionForMeGet = ctx -> { + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); } - - User user = req.session().attribute("user"); - Collection submissions; - try { - submissions = SubmissionService.getXSubmissionsForUser(user.netId(), phase); - } catch (DataAccessException e) { - halt(500); - return null; - } - - res.status(200); - res.type("application/json"); - - return Serializer.serialize(submissions); + Submission submission = SubmissionService.getLastSubmissionForUser(user.netId()); + ctx.json(submission); }; - public static final Route latestSubmissionsGet = (req, res) -> { - String countString = req.params(":count"); - int count = countString == null ? -1 : Integer.parseInt(countString); // if they don't give a count, set it to -1, which gets all latest submissions + public static final Handler submissionXGet = ctx -> { + String phaseString = ctx.pathParam("phase"); + Phase phase; - Collection submissions = null; try { - submissions = SubmissionService.getLatestSubmissions(count); - } catch (DataAccessException e) { - halt(500); + phase = Phase.valueOf(phaseString); + } catch (IllegalArgumentException e) { + LOGGER.error("Invalid phase", e); + throw new BadRequestException("Invalid phase", e); } - res.status(200); - res.type("application/json"); - - return Serializer.serialize(submissions); + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); + } + Collection submissions = SubmissionService.getXSubmissionsForUser(user.netId(), phase); + ctx.json(submissions); }; - public static final Route submissionsActiveGet = (req, res) -> { - List inQueue = null; - List currentlyGrading = null; - try { - inQueue = SubmissionService.getActiveInQueue(); - currentlyGrading = SubmissionService.getCurrentlyGrading(); - } catch (DataAccessException e) { - halt(500); + public static final Handler latestSubmissionsGet = ctx -> { + int count = -1; + if (ctx.pathParamMap().containsKey("count")) { + String countString = ctx.pathParam("count"); + count = Integer.parseInt(countString); } - res.status(200); - res.type("application/json"); - - return Serializer.serialize(Map.of("currentlyGrading", currentlyGrading, "inQueue", inQueue)); + Collection submissions = SubmissionService.getLatestSubmissions(count); + ctx.json(submissions); }; - public static final Route studentSubmissionsGet = (req, res) -> { - String netId = req.params(":netId"); - - Collection submissions = null; - try { - submissions = SubmissionService.getSubmissionsForUser(netId); - } catch (DataAccessException e) { - halt(500); - } - - res.status(200); - res.type("application/json"); + public static final Handler submissionsActiveGet = ctx -> { + List inQueue = SubmissionService.getActiveInQueue(); + List currentlyGrading = SubmissionService.getCurrentlyGrading(); + ctx.json(Map.of("currentlyGrading", currentlyGrading, "inQueue", inQueue)); + }; - return Serializer.serialize(submissions); + public static final Handler studentSubmissionsGet = ctx -> { + String netId = ctx.pathParam("netId"); + Collection submissions = SubmissionService.getSubmissionsForUser(netId); + ctx.json(submissions); }; - public static final Route approveSubmissionPost = (req, res) -> { - User adminUser = req.session().attribute("user"); - ApprovalRequest request = Serializer.deserialize(req.body(), ApprovalRequest.class); + public static final Handler approveSubmissionPost = ctx -> { + User adminUser = ctx.sessionAttribute("user"); + if (adminUser == null) { + throw new UnauthorizedException("No user credentials found"); + } + ApprovalRequest request = ctx.bodyAsClass(ApprovalRequest.class); SubmissionService.approveSubmission(adminUser.netId(), request); - return "{}"; }; - public static final Route submissionsReRunPost = (req, res) -> { + public static final Handler submissionsReRunPost = ctx -> { SubmissionService.reRunSubmissionsInQueue(); - - res.status(200); - res.type("application/json"); - - return Serializer.serialize(Map.of("message", "re-running submissions in queue")); + ctx.json(Map.of("message", "re-running submissions in queue")); }; } diff --git a/src/main/java/edu/byu/cs/controller/UserController.java b/src/main/java/edu/byu/cs/controller/UserController.java index 48e8a7c6..97bf4d01 100644 --- a/src/main/java/edu/byu/cs/controller/UserController.java +++ b/src/main/java/edu/byu/cs/controller/UserController.java @@ -4,68 +4,48 @@ import com.google.gson.JsonObject; import edu.byu.cs.controller.exception.BadRequestException; import edu.byu.cs.controller.exception.InternalServerException; -import edu.byu.cs.controller.exception.PriorRepoClaimBlockageException; +import edu.byu.cs.controller.exception.UnauthorizedException; +import edu.byu.cs.controller.exception.WordOfWisdomViolationException; import edu.byu.cs.model.RepoUpdate; import edu.byu.cs.model.User; import edu.byu.cs.service.UserService; -import edu.byu.cs.util.Serializer; -import spark.Request; -import spark.Response; -import spark.Route; +import io.javalin.http.Context; +import io.javalin.http.Handler; import java.util.Collection; -import static spark.Spark.halt; - public class UserController { - public static final Route repoPatch = (req, res) -> { - User user = req.session().attribute("user"); - applyRepoPatch(user.netId(), null, req, res); - return "Successfully updated repoUrl"; - }; - - public static final Route repoPatchAdmin = (req, res) -> { - User admin = req.session().attribute("user"); - String studentNetId = req.params(":netId"); - applyRepoPatch(studentNetId, admin.netId(), req, res); - return "Successfully updated repoUrl for user: " + studentNetId; + public static final Handler repoPatch = ctx -> { + User user = ctx.sessionAttribute("user"); + if (user == null) { + throw new UnauthorizedException("No user credentials found"); + } + applyRepoPatch(user.netId(), null, ctx); + ctx.result("Successfully updated repoUrl"); }; - public static final Route repoHistoryAdminGet = (req, res) -> { - String repoUrl = req.queryParams("repoUrl"); - String netId = req.queryParams("netId"); - - Collection updates; - try { - updates = UserService.adminGetRepoHistory(repoUrl, netId); - } catch (BadRequestException e) { - halt(422, "You must provide either a repoUrl or a netId"); - return null; - } catch (InternalServerException e) { - halt(500, "There was an internal server error getting repo updates"); - return null; + public static final Handler repoPatchAdmin = ctx -> { + User admin = ctx.sessionAttribute("user"); + if (admin == null) { + throw new UnauthorizedException("No user credentials found"); } + String studentNetId = ctx.pathParam("netId"); + applyRepoPatch(studentNetId, admin.netId(), ctx); + ctx.result("Successfully updated repoUrl for user: " + studentNetId); + }; - res.status(200); - res.type("application/json"); + public static final Handler repoHistoryAdminGet = ctx -> { + String repoUrl = ctx.queryParam("repoUrl"); + String netId = ctx.queryParam("netId"); - return Serializer.serialize(updates); + Collection updates = UserService.adminGetRepoHistory(repoUrl, netId); + ctx.json(updates); }; - private static void applyRepoPatch(String studentNetId, String adminNetId, Request req, Response res) { - JsonObject jsonObject = new Gson().fromJson(req.body(), JsonObject.class); + private static void applyRepoPatch(String studentNetId, String adminNetId, Context ctx) + throws WordOfWisdomViolationException, InternalServerException, BadRequestException { + JsonObject jsonObject = new Gson().fromJson(ctx.body(), JsonObject.class); String repoUrl = new Gson().fromJson(jsonObject.get("repoUrl"), String.class); - - try { - UserService.updateRepoUrl(studentNetId, repoUrl, adminNetId); - } catch (BadRequestException e) { - halt(400, e.getMessage()); - } catch (PriorRepoClaimBlockageException e) { - halt(418, e.getMessage()); - } catch (InternalServerException e) { - halt(500, e.getMessage()); - } - - res.status(200); + UserService.updateRepoUrl(studentNetId, repoUrl, adminNetId); } } diff --git a/src/main/java/edu/byu/cs/controller/WebSocketController.java b/src/main/java/edu/byu/cs/controller/WebSocketController.java index e4c91694..d92cc4e1 100644 --- a/src/main/java/edu/byu/cs/controller/WebSocketController.java +++ b/src/main/java/edu/byu/cs/controller/WebSocketController.java @@ -3,29 +3,30 @@ import edu.byu.cs.dataAccess.DataAccessException; import edu.byu.cs.util.JwtUtils; import edu.byu.cs.util.Serializer; -import org.eclipse.jetty.websocket.api.CloseException; +import io.javalin.websocket.WsErrorContext; +import io.javalin.websocket.WsMessageContext; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Map; +import java.util.concurrent.TimeUnit; -@WebSocket public class WebSocketController { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketController.class); - @OnWebSocketError - public void onError(Session session, Throwable t) { - if (!(t instanceof CloseException)) - LOGGER.error("WebSocket error: ", t); + public static void onError(WsErrorContext ctx) { + if (!(ctx.error() instanceof IOException)) { + LOGGER.error("WebSocket error: ", ctx.error()); + } } - @OnWebSocketMessage - public void onMessage(Session session, String message) { + public static void onMessage(WsMessageContext ctx) { + Session session = ctx.session; + String message = ctx.message(); String netId; + ctx.enableAutomaticPings(20, TimeUnit.SECONDS); try { netId = JwtUtils.validateToken(message); } catch (Exception e) { @@ -42,8 +43,7 @@ public void onMessage(Session session, String message) { return; } - if (TrafficController.sessions.get(netId).contains(session)) - return; + if (TrafficController.sessions.get(netId).contains(session)) return; TrafficController.sessions.get(netId).add(session); try { @@ -76,12 +76,7 @@ public static void send(Session session, Map message) { * @param message the error message */ public static void sendError(Session session, String message) { - send( - session, - Map.of( - "type", "error", - "message", message - )); + send(session, Map.of("type", "error", "message", message)); } diff --git a/src/main/java/edu/byu/cs/controller/exception/PriorRepoClaimBlockageException.java b/src/main/java/edu/byu/cs/controller/exception/PriorRepoClaimBlockageException.java deleted file mode 100644 index c72d5c42..00000000 --- a/src/main/java/edu/byu/cs/controller/exception/PriorRepoClaimBlockageException.java +++ /dev/null @@ -1,9 +0,0 @@ -package edu.byu.cs.controller.exception; - -public class PriorRepoClaimBlockageException extends Exception { - private static final String secrets = "btw im a teapot"; - - public PriorRepoClaimBlockageException(String message) { - super(message); - } -} diff --git a/src/main/java/edu/byu/cs/controller/exception/ResourceForbiddenException.java b/src/main/java/edu/byu/cs/controller/exception/ResourceForbiddenException.java new file mode 100644 index 00000000..337a21f9 --- /dev/null +++ b/src/main/java/edu/byu/cs/controller/exception/ResourceForbiddenException.java @@ -0,0 +1,7 @@ +package edu.byu.cs.controller.exception; + +public class ResourceForbiddenException extends Exception { + public ResourceForbiddenException() { + super(); + } +} diff --git a/src/main/java/edu/byu/cs/controller/exception/ResourceNotFoundException.java b/src/main/java/edu/byu/cs/controller/exception/ResourceNotFoundException.java new file mode 100644 index 00000000..eff45eab --- /dev/null +++ b/src/main/java/edu/byu/cs/controller/exception/ResourceNotFoundException.java @@ -0,0 +1,11 @@ +package edu.byu.cs.controller.exception; + +public class ResourceNotFoundException extends Exception { + public ResourceNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/edu/byu/cs/controller/exception/UnauthorizedException.java b/src/main/java/edu/byu/cs/controller/exception/UnauthorizedException.java new file mode 100644 index 00000000..2499d889 --- /dev/null +++ b/src/main/java/edu/byu/cs/controller/exception/UnauthorizedException.java @@ -0,0 +1,11 @@ +package edu.byu.cs.controller.exception; + +public class UnauthorizedException extends Exception { + public UnauthorizedException() { + super(); + } + + public UnauthorizedException(String message) { + super(message); + } +} diff --git a/src/main/java/edu/byu/cs/controller/exception/UnprocessableEntityException.java b/src/main/java/edu/byu/cs/controller/exception/UnprocessableEntityException.java new file mode 100644 index 00000000..b768b61d --- /dev/null +++ b/src/main/java/edu/byu/cs/controller/exception/UnprocessableEntityException.java @@ -0,0 +1,7 @@ +package edu.byu.cs.controller.exception; + +public class UnprocessableEntityException extends Exception { + public UnprocessableEntityException(String message) { + super(message); + } +} diff --git a/src/main/java/edu/byu/cs/controller/exception/WordOfWisdomViolationException.java b/src/main/java/edu/byu/cs/controller/exception/WordOfWisdomViolationException.java new file mode 100644 index 00000000..050bf7c3 --- /dev/null +++ b/src/main/java/edu/byu/cs/controller/exception/WordOfWisdomViolationException.java @@ -0,0 +1,9 @@ +package edu.byu.cs.controller.exception; + +public class WordOfWisdomViolationException extends Exception { + private static final String shortAndStout = "yo im a teapot"; + + public WordOfWisdomViolationException(String message) { + super(message); + } +} diff --git a/src/main/java/edu/byu/cs/server/SerializerAdapter.java b/src/main/java/edu/byu/cs/server/SerializerAdapter.java new file mode 100644 index 00000000..ef91855d --- /dev/null +++ b/src/main/java/edu/byu/cs/server/SerializerAdapter.java @@ -0,0 +1,25 @@ +package edu.byu.cs.server; + +import edu.byu.cs.util.Serializer; +import io.javalin.json.JsonMapper; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Type; + +/** + * A Serializer class adapter which implements Javalin's JsonMapper interface. + * Allows Javalin's built-in serialization to use Serializer. + */ +public class SerializerAdapter implements JsonMapper { + @NotNull + @Override + public T fromJsonString(@NotNull String json, @NotNull Type targetType) { + return Serializer.deserialize(json, targetType); + } + + @NotNull + @Override + public String toJsonString(@NotNull Object obj, @NotNull Type type) { + return Serializer.serialize(obj, type); + } +} diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index c2deb6be..8821834b 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -1,14 +1,20 @@ package edu.byu.cs.server; import edu.byu.cs.controller.WebSocketController; +import edu.byu.cs.controller.exception.*; import edu.byu.cs.server.endpointprovider.EndpointProvider; +import io.javalin.Javalin; +import io.javalin.http.ExceptionHandler; +import io.javalin.http.HandlerType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static spark.Spark.*; +import static io.javalin.apibuilder.ApiBuilder.*; public class Server { + private Javalin app; + private static final Logger LOGGER = LoggerFactory.getLogger(Server.class); private final EndpointProvider provider; @@ -23,101 +29,128 @@ public int start(int desiredPort) { } private int setupEndpoints(int port) { - port(port); + app = Javalin.create(config -> { + config.staticFiles.add("/frontend/dist"); + + config.jsonMapper(new SerializerAdapter()); + + config.router.apiBuilder(() -> { + before(provider.beforeAll()); - webSocket("/ws", WebSocketController.class); - webSocketIdleTimeoutMillis(300000); + path("/auth", () -> { + get("/callback", provider.callbackGet()); + get("/login", provider.loginGet()); - staticFiles.location("/frontend/dist"); + // all routes after this point require authentication + post("/logout", provider.logoutPost()); + }); - before(provider.beforeAll()); + path("/api", () -> { + before("/*", ctx -> { + if (ctx.method() != HandlerType.OPTIONS) provider.verifyAuthenticatedMiddleware().handle(ctx); + }); - path("/auth", () -> { - get("/callback", provider.callbackGet()); - get("/login", provider.loginGet()); + patch("/repo", provider.repoPatch()); - // all routes after this point require authentication - post("/logout", provider.logoutPost()); - }); + get("/submit", provider.submitGet()); + post("/submit", provider.submitPost()); - path("/api", () -> { - before("/*", (req, res) -> { - if (!req.requestMethod().equals("OPTIONS")) provider.verifyAuthenticatedMiddleware().handle(req, res); - }); + get("/latest", provider.latestSubmissionForMeGet()); - patch("/repo", provider.repoPatch()); + get("/submission", provider.submissionXGet()); + get("/submission/{phase}", provider.submissionXGet()); - get("/submit", provider.submitGet()); - post("/submit", provider.submitPost()); + get("/me", provider.meGet()); - get("/latest", provider.latestSubmissionForMeGet()); + get("/config", provider.getConfigStudent()); - get("/submission", provider.submissionXGet()); - get("/submission/:phase", provider.submissionXGet()); + path("/admin", () -> { + before("/*", ctx -> { + if (ctx.method() != HandlerType.OPTIONS) provider.verifyAdminMiddleware().handle(ctx); + }); - get("/me", provider.meGet()); + patch("/repo/{netId}", provider.repoPatchAdmin()); - get("/config", provider.getConfigStudent()); + get("/repo/history", provider.repoHistoryAdminGet()); - path("/admin", () -> { - before("/*", (req, res) -> { - if (!req.requestMethod().equals("OPTIONS")) provider.verifyAdminMiddleware().handle(req, res); - }); + get("/users", provider.usersGet()); - patch("/repo/:netId", provider.repoPatchAdmin()); + post("/submit", provider.adminRepoSubmitPost()); - get("/repo/history", provider.repoHistoryAdminGet()); + path("/submissions", () -> { + post("/approve", provider.approveSubmissionPost()); - get("/users", provider.usersGet()); + get("/latest", provider.latestSubmissionsGet()); - post("/submit", provider.adminRepoSubmitPost()); + get("/latest/{count}", provider.latestSubmissionsGet()); - path("/submissions", () -> { - post("/approve", provider.approveSubmissionPost()); + get("/active", provider.submissionsActiveGet()); - get("/latest", provider.latestSubmissionsGet()); + get("/student/{netId}", provider.studentSubmissionsGet()); - get("/latest/:count", provider.latestSubmissionsGet()); + post("/rerun", provider.submissionsReRunPost()); + }); - get("/active", provider.submissionsActiveGet()); + get("/test_mode", provider.testModeGet()); - get("/student/:netId", provider.studentSubmissionsGet()); + get("/analytics/commit", provider.commitAnalyticsGet()); - post("/rerun", provider.submissionsReRunPost()); - }); + get("/analytics/commit/{option}", provider.commitAnalyticsGet()); - get("/test_mode", provider.testModeGet()); + get("/honorChecker/zip/{section}", provider.honorCheckerZipGet()); - get("/analytics/commit", provider.commitAnalyticsGet()); + get("/sections", provider.sectionsGet()); - get("/analytics/commit/:option", provider.commitAnalyticsGet()); + path("/config", () -> { + get("", provider.getConfigAdmin()); - get("/honorChecker/zip/:section", provider.honorCheckerZipGet()); + post("/phases", provider.updateLivePhases()); + post("/phases/shutdown", provider.scheduleShutdown()); - get("/sections", provider.sectionsGet()); + post("/banner", provider.updateBannerMessage()); - path("/config", () -> { - get("", provider.getConfigAdmin()); + post("/courseIds", provider.updateCourseIdsPost()); + get("/courseIds", provider.updateCourseIdsUsingCanvasGet()); - post("/phases", provider.updateLivePhases()); - post("/phases/shutdown", provider.scheduleShutdown()); - post("/banner", provider.updateBannerMessage()); + post("/penalties", provider.updatePenalties()); + }); + }); + }); - post("/courseIds", provider.updateCourseIdsPost()); - get("/courseIds", provider.updateCourseIdsUsingCanvasGet()); + get("/*", provider.defaultGet()); - post("/penalties", provider.updatePenalties()); - }); - }); - }); + after(provider.afterAll()); + }); + }) - // spark's notFound method does not work - get("/*", provider.defaultGet()); + .options("/*", provider.defaultOptions()) - after(provider.afterAll()); + .ws("/ws", (wsConfig) -> { + wsConfig.onError(WebSocketController::onError); + wsConfig.onMessage(WebSocketController::onMessage); + }) - init(); + .exception(BadRequestException.class, haltWithCode(400)) + .exception(UnauthorizedException.class, haltWithCode(401)) + .exception(ResourceForbiddenException.class, haltWithCode(403)) + .exception(ResourceNotFoundException.class, haltWithCode(404)) + .exception(WordOfWisdomViolationException.class, haltWithCode(418)) + .exception(UnprocessableEntityException.class, haltWithCode(422)) + .exception(Exception.class, haltWithCode(500)) + + .start(port); + + return app.port(); + } - return port(); + private static ExceptionHandler haltWithCode(int statusCode) { + return (e, ctx) -> { + ctx.status(statusCode); + if (e.getMessage() != null) { + ctx.result(e.getMessage()); + } else { + ctx.result("An unknown %d error occurred.".formatted(statusCode)); + } + }; } } diff --git a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java index b89c1d6a..5cb8d5da 100644 --- a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java +++ b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java @@ -1,64 +1,92 @@ package edu.byu.cs.server.endpointprovider; -import spark.Filter; -import spark.Route; +import io.javalin.http.Handler; public interface EndpointProvider { // Wildcard endpoints - Filter beforeAll(); - Filter afterAll(); + Handler beforeAll(); - Route defaultGet(); + Handler afterAll(); + + Handler defaultGet(); + + Handler defaultOptions(); // AdminController - Route usersGet(); - Route testModeGet(); - Route commitAnalyticsGet(); - Route honorCheckerZipGet(); - Route sectionsGet(); + Handler usersGet(); + + Handler testModeGet(); + + Handler commitAnalyticsGet(); + + Handler honorCheckerZipGet(); + + Handler sectionsGet(); // AuthController - Filter verifyAuthenticatedMiddleware(); - Filter verifyAdminMiddleware(); - Route meGet(); + Handler verifyAuthenticatedMiddleware(); + + Handler verifyAdminMiddleware(); + + Handler meGet(); // CasController - Route callbackGet(); - Route loginGet(); - Route logoutPost(); + Handler callbackGet(); + + Handler loginGet(); + + Handler logoutPost(); // ConfigController - Route getConfigAdmin(); - Route getConfigStudent(); - Route updateLivePhases(); - Route scheduleShutdown(); - Route updateBannerMessage(); - Route updateCourseIdsPost(); - Route updateCourseIdsUsingCanvasGet(); - Route updatePenalties(); + Handler getConfigAdmin(); + + Handler getConfigStudent(); + + Handler updateLivePhases(); + + Handler scheduleShutdown(); + + Handler updateBannerMessage(); + + Handler updateCourseIdsPost(); + + Handler updateCourseIdsUsingCanvasGet(); + + Handler updatePenalties(); // SubmissionController - Route submitPost(); - Route adminRepoSubmitPost(); - Route submitGet(); - Route latestSubmissionForMeGet(); - Route submissionXGet(); - Route latestSubmissionsGet(); - Route submissionsActiveGet(); - Route studentSubmissionsGet(); - Route approveSubmissionPost(); - Route submissionsReRunPost(); + Handler submitPost(); + + Handler adminRepoSubmitPost(); + + Handler submitGet(); + + Handler latestSubmissionForMeGet(); + + Handler submissionXGet(); + + Handler latestSubmissionsGet(); + + Handler submissionsActiveGet(); + + Handler studentSubmissionsGet(); + + Handler approveSubmissionPost(); + + Handler submissionsReRunPost(); // UserController - Route repoPatch(); - Route repoPatchAdmin(); - Route repoHistoryAdminGet(); + Handler repoPatch(); + + Handler repoPatchAdmin(); + + Handler repoHistoryAdminGet(); } diff --git a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java index 1ba31d02..bf55d975 100644 --- a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java +++ b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java @@ -2,210 +2,214 @@ import edu.byu.cs.controller.*; import edu.byu.cs.properties.ApplicationProperties; -import spark.Filter; -import spark.Route; + +import io.javalin.http.Handler; +import io.javalin.http.HttpStatus; public class EndpointProviderImpl implements EndpointProvider { // Wildcard endpoints @Override - public Filter beforeAll() { - return (request, response) -> { - response.header("Access-Control-Allow-Headers", "Authorization,Content-Type"); - response.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS"); - response.header("Access-Control-Allow-Credentials", "true"); - response.header("Access-Control-Allow-Origin", ApplicationProperties.frontendUrl()); + public Handler beforeAll() { + return ctx -> { + ctx.header("Access-Control-Allow-Headers", "Authorization,Content-Type"); + ctx.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS"); + ctx.header("Access-Control-Allow-Credentials", "true"); + ctx.header("Access-Control-Allow-Origin", ApplicationProperties.frontendUrl()); }; } @Override - public Filter afterAll() { - return (req, res) -> {}; + public Handler afterAll() { + return ctx -> {}; } @Override - public Route defaultGet() { - return (req, res) -> { - if (req.pathInfo().equals("/ws")) - return null; - - String urlParams = req.queryString(); - urlParams = urlParams == null ? "" : "?" + urlParams; - res.redirect("/" + urlParams, 302); - return null; + public Handler defaultGet() { + return ctx -> { + if (!ctx.path().equals("/ws")) { + String urlParams = ctx.queryString(); + urlParams = urlParams == null ? "" : "?" + urlParams; + ctx.redirect("/" + urlParams, HttpStatus.FOUND); + } }; } + @Override + public Handler defaultOptions() { + return ctx -> {}; + } + // AdminController @Override - public Route usersGet() { + public Handler usersGet() { return AdminController.usersGet; } @Override - public Route testModeGet() { + public Handler testModeGet() { return AdminController.testModeGet; } @Override - public Route commitAnalyticsGet() { + public Handler commitAnalyticsGet() { return AdminController.commitAnalyticsGet; } @Override - public Route honorCheckerZipGet() { + public Handler honorCheckerZipGet() { return AdminController.honorCheckerZipGet; } @Override - public Route sectionsGet() { + public Handler sectionsGet() { return AdminController.sectionsGet; } // AuthController @Override - public Filter verifyAuthenticatedMiddleware() { + public Handler verifyAuthenticatedMiddleware() { return AuthController.verifyAuthenticatedMiddleware; } @Override - public Filter verifyAdminMiddleware() { + public Handler verifyAdminMiddleware() { return AuthController.verifyAdminMiddleware; } @Override - public Route meGet() { + public Handler meGet() { return AuthController.meGet; } // CasController @Override - public Route callbackGet() { + public Handler callbackGet() { return CasController.callbackGet; } @Override - public Route loginGet() { + public Handler loginGet() { return CasController.loginGet; } @Override - public Route logoutPost() { + public Handler logoutPost() { return CasController.logoutPost; } // ConfigController @Override - public Route getConfigAdmin() { + public Handler getConfigAdmin() { return ConfigController.getConfigAdmin; } @Override - public Route getConfigStudent() { + public Handler getConfigStudent() { return ConfigController.getConfigStudent; } @Override - public Route updateLivePhases() { + public Handler updateLivePhases() { return ConfigController.updateLivePhases; } @Override - public Route scheduleShutdown() { + public Handler scheduleShutdown() { return ConfigController.scheduleShutdown; } @Override - public Route updateBannerMessage() { + public Handler updateBannerMessage() { return ConfigController.updateBannerMessage; } @Override - public Route updateCourseIdsPost() { + public Handler updateCourseIdsPost() { return ConfigController.updateCourseIdsPost; } @Override - public Route updateCourseIdsUsingCanvasGet() { + public Handler updateCourseIdsUsingCanvasGet() { return ConfigController.updateCourseIdsUsingCanvasGet; } @Override - public Route updatePenalties() { + public Handler updatePenalties() { return ConfigController.updatePenalties; } // SubmissionController @Override - public Route submitPost() { + public Handler submitPost() { return SubmissionController.submitPost; } @Override - public Route adminRepoSubmitPost() { + public Handler adminRepoSubmitPost() { return SubmissionController.adminRepoSubmitPost; } @Override - public Route submitGet() { + public Handler submitGet() { return SubmissionController.submitGet; } @Override - public Route latestSubmissionForMeGet() { + public Handler latestSubmissionForMeGet() { return SubmissionController.latestSubmissionForMeGet; } @Override - public Route submissionXGet() { + public Handler submissionXGet() { return SubmissionController.submissionXGet; } @Override - public Route latestSubmissionsGet() { + public Handler latestSubmissionsGet() { return SubmissionController.latestSubmissionsGet; } @Override - public Route submissionsActiveGet() { + public Handler submissionsActiveGet() { return SubmissionController.submissionsActiveGet; } @Override - public Route studentSubmissionsGet() { + public Handler studentSubmissionsGet() { return SubmissionController.studentSubmissionsGet; } @Override - public Route approveSubmissionPost() { + public Handler approveSubmissionPost() { return SubmissionController.approveSubmissionPost; } @Override - public Route submissionsReRunPost() { + public Handler submissionsReRunPost() { return SubmissionController.submissionsReRunPost; } // UserController @Override - public Route repoPatch() { + public Handler repoPatch() { return UserController.repoPatch; } @Override - public Route repoPatchAdmin() { + public Handler repoPatchAdmin() { return UserController.repoPatchAdmin; } @Override - public Route repoHistoryAdminGet() { + public Handler repoHistoryAdminGet() { return UserController.repoHistoryAdminGet; } } diff --git a/src/main/java/edu/byu/cs/service/AdminService.java b/src/main/java/edu/byu/cs/service/AdminService.java index fdc8aefb..41edf6fa 100644 --- a/src/main/java/edu/byu/cs/service/AdminService.java +++ b/src/main/java/edu/byu/cs/service/AdminService.java @@ -4,9 +4,9 @@ import edu.byu.cs.canvas.CanvasException; import edu.byu.cs.canvas.CanvasService; import edu.byu.cs.canvas.model.CanvasSection; +import edu.byu.cs.controller.exception.ResourceNotFoundException; import edu.byu.cs.dataAccess.DaoService; import edu.byu.cs.dataAccess.DataAccessException; -import edu.byu.cs.dataAccess.ItemNotFoundException; import edu.byu.cs.dataAccess.UserDao; import edu.byu.cs.honorChecker.HonorCheckerCompiler; import edu.byu.cs.model.User; @@ -37,7 +37,7 @@ public static Collection getUsers() throws DataAccessException { return users; } - public static void updateUser(User user) throws DataAccessException, ItemNotFoundException { + public static void updateUser(User user) throws DataAccessException, ResourceNotFoundException { UserDao userDao = DaoService.getUserDao(); User existingUser; try { @@ -48,7 +48,7 @@ public static void updateUser(User user) throws DataAccessException, ItemNotFoun } if (existingUser == null) { - ItemNotFoundException e = new ItemNotFoundException("user not found"); + ResourceNotFoundException e = new ResourceNotFoundException("user not found"); LOGGER.error("user not found", e); throw e; } diff --git a/src/main/java/edu/byu/cs/service/UserService.java b/src/main/java/edu/byu/cs/service/UserService.java index bf4fb954..7c04691a 100644 --- a/src/main/java/edu/byu/cs/service/UserService.java +++ b/src/main/java/edu/byu/cs/service/UserService.java @@ -2,7 +2,8 @@ import edu.byu.cs.controller.exception.BadRequestException; import edu.byu.cs.controller.exception.InternalServerException; -import edu.byu.cs.controller.exception.PriorRepoClaimBlockageException; +import edu.byu.cs.controller.exception.UnprocessableEntityException; +import edu.byu.cs.controller.exception.WordOfWisdomViolationException; import edu.byu.cs.dataAccess.DaoService; import edu.byu.cs.dataAccess.DataAccessException; import edu.byu.cs.model.RepoUpdate; @@ -24,15 +25,17 @@ public class UserService { private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class); - public static void updateRepoUrl(String studentNetId, String repoUrl, String adminNetId) throws BadRequestException, InternalServerException, PriorRepoClaimBlockageException { + public static void updateRepoUrl(String studentNetId, String repoUrl, String adminNetId) + throws BadRequestException, InternalServerException, WordOfWisdomViolationException { String cleanRepoUrl = requireCleanRepoUrl(repoUrl); setRepoUrl(studentNetId, cleanRepoUrl, adminNetId); } - public static Collection adminGetRepoHistory(String repoUrl, String netId) throws BadRequestException, InternalServerException { + public static Collection adminGetRepoHistory(String repoUrl, String netId) + throws InternalServerException, UnprocessableEntityException { Collection updates = new ArrayList<>(); if (repoUrl == null && netId == null) { - throw new BadRequestException("You must provide either a repoUrl or a netId"); + throw new UnprocessableEntityException("You must provide either a repoUrl or a netId"); } try { @@ -50,7 +53,8 @@ public static Collection adminGetRepoHistory(String repoUrl, String return updates; } - private static void setRepoUrl(String studentNetId, String repoUrl, String adminNetId) throws BadRequestException, InternalServerException, PriorRepoClaimBlockageException { + private static void setRepoUrl(String studentNetId, String repoUrl, String adminNetId) + throws BadRequestException, InternalServerException, WordOfWisdomViolationException { try { if (!isValidRepoUrl(repoUrl)) { throw new BadRequestException("Invalid Github Repo Url. Check if the link is valid and points directly to a Github Repo."); @@ -70,10 +74,10 @@ private static void setRepoUrl(String studentNetId, String repoUrl, String admin } if (historicalUpdate != null) { if (adminNetId != null) { - throw new PriorRepoClaimBlockageException("Repo is blocked because of a prior claim: " + historicalUpdate); + throw new WordOfWisdomViolationException("Repo is blocked because of a prior claim: " + historicalUpdate); } else { LOGGER.info("Student {} was blocked from updating their url because of a prior claim: {}", studentNetId, historicalUpdate); - throw new PriorRepoClaimBlockageException("Please talk to a TA to submit this url"); + throw new WordOfWisdomViolationException("Please talk to a TA to submit this url"); } } diff --git a/src/main/java/edu/byu/cs/util/Serializer.java b/src/main/java/edu/byu/cs/util/Serializer.java index 827696da..4f984c74 100644 --- a/src/main/java/edu/byu/cs/util/Serializer.java +++ b/src/main/java/edu/byu/cs/util/Serializer.java @@ -31,6 +31,14 @@ public static String serialize(Object obj) { } } + public static String serialize(Object obj, Type type) { + try { + return GSON.toJson(obj, type); + } catch (Exception e) { + throw new SerializationException(e); + } + } + public static T deserialize(JsonElement jsonElement, Class classOfT) { return deserialize(jsonElement.toString(), classOfT); } @@ -42,6 +50,14 @@ public static T deserialize(String jsonStr, Class classOfT) { } } + public static T deserialize(String jsonStr, Type targetType) { + try { + return GSON.fromJson(jsonStr, targetType); + } catch (Exception e) { + throw new SerializationException(e); + } + } + public static T deserialize(Reader reader, Class classOfT) { try { return GSON.fromJson(reader, classOfT);