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);