Skip to content

Commit

Permalink
refactor(server): decouple Dropwizard resources from JAX-RS contract …
Browse files Browse the repository at this point in the history
…interfaces pt 2
  • Loading branch information
fushar committed Jun 24, 2023
1 parent bdbda15 commit a0ceae2
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package judgels.jerahmeel.problemset.problem;

import static com.google.common.base.Preconditions.checkArgument;
import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static judgels.service.ServiceUtils.checkAllowed;
import static judgels.service.ServiceUtils.checkFound;

Expand All @@ -14,6 +16,14 @@
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import judgels.jerahmeel.api.problem.ProblemDifficulty;
Expand All @@ -24,7 +34,6 @@
import judgels.jerahmeel.api.problemset.problem.ProblemReportResponse;
import judgels.jerahmeel.api.problemset.problem.ProblemSetProblem;
import judgels.jerahmeel.api.problemset.problem.ProblemSetProblemData;
import judgels.jerahmeel.api.problemset.problem.ProblemSetProblemService;
import judgels.jerahmeel.api.problemset.problem.ProblemSetProblemWorksheet;
import judgels.jerahmeel.api.problemset.problem.ProblemSetProblemsResponse;
import judgels.jerahmeel.difficulty.ProblemDifficultyStore;
Expand All @@ -43,45 +52,28 @@
import judgels.service.api.actor.AuthHeader;
import judgels.uriel.api.contest.ContestInfo;

public class ProblemSetProblemResource implements ProblemSetProblemService {
private final ActorChecker actorChecker;
private final RoleChecker roleChecker;
private final ProblemSetStore problemSetStore;
private final ProblemSetProblemStore problemStore;
private final ProblemDifficultyStore difficultyStore;
private final UserClient userClient;
private final ProblemClient problemClient;
private final ContestClient contestClient;
private final StatsStore statsStore;

@Context UriInfo baseUriInfo;

@Inject
public ProblemSetProblemResource(
ActorChecker actorChecker,
RoleChecker roleChecker,
ProblemSetStore problemSetStore,
ProblemSetProblemStore problemStore,
ProblemDifficultyStore difficultyStore,
UserClient userClient,
ProblemClient problemClient,
ContestClient contestClient,
StatsStore statsStore) {

this.actorChecker = actorChecker;
this.roleChecker = roleChecker;
this.problemSetStore = problemSetStore;
this.problemStore = problemStore;
this.difficultyStore = difficultyStore;
this.userClient = userClient;
this.problemClient = problemClient;
this.contestClient = contestClient;
this.statsStore = statsStore;
}

@Override
@Path("/api/v2/problemsets/{problemSetJid}/problems")
public class ProblemSetProblemResource {
@Inject protected ActorChecker actorChecker;
@Inject protected RoleChecker roleChecker;
@Inject protected ProblemSetStore problemSetStore;
@Inject protected ProblemSetProblemStore problemStore;
@Inject protected ProblemDifficultyStore difficultyStore;
@Inject protected UserClient userClient;
@Inject protected ProblemClient problemClient;
@Inject protected ContestClient contestClient;
@Inject protected StatsStore statsStore;

@Inject public ProblemSetProblemResource() {}

@PUT
@Consumes(APPLICATION_JSON)
@UnitOfWork
public void setProblems(AuthHeader authHeader, String problemSetJid, List<ProblemSetProblemData> data) {
public void setProblems(
@HeaderParam(AUTHORIZATION) AuthHeader authHeader,
@PathParam("problemSetJid") String problemSetJid,
List<ProblemSetProblemData> data) {

String actorJid = actorChecker.check(authHeader);
checkFound(problemSetStore.getProblemSetByJid(problemSetJid));
checkAllowed(roleChecker.isAdmin(actorJid));
Expand Down Expand Up @@ -128,9 +120,13 @@ public void setProblems(AuthHeader authHeader, String problemSetJid, List<Proble
problemClient.setProblemVisibilityTagsByJids(problemVisibilitiesMap);
}

@Override
@GET
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public ProblemSetProblemsResponse getProblems(Optional<AuthHeader> authHeader, String problemSetJid) {
public ProblemSetProblemsResponse getProblems(
@HeaderParam(AUTHORIZATION) Optional<AuthHeader> authHeader,
@PathParam("problemSetJid") String problemSetJid) {

String actorJid = actorChecker.check(authHeader);
checkFound(problemSetStore.getProblemSetByJid(problemSetJid));

Expand All @@ -155,27 +151,31 @@ public ProblemSetProblemsResponse getProblems(Optional<AuthHeader> authHeader, S
.build();
}

@Override
@GET
@Path("/{problemAlias}")
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public ProblemSetProblem getProblem(
Optional<AuthHeader> authHeader,
String problemSetJid,
String problemAlias) {
@HeaderParam(AUTHORIZATION) Optional<AuthHeader> authHeader,
@PathParam("problemSetJid") String problemSetJid,
@PathParam("problemAlias") String problemAlias) {

actorChecker.check(authHeader);
checkFound(problemSetStore.getProblemSetByJid(problemSetJid));

return checkFound(problemStore.getProblemByAlias(problemSetJid, problemAlias));
}

@Override
@GET
@Path("/{problemAlias}/worksheet")
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public ProblemSetProblemWorksheet getProblemWorksheet(
UriInfo uriInfo,
Optional<AuthHeader> authHeader,
String problemSetJid,
String problemAlias,
Optional<String> language) {
@Context UriInfo uriInfo,
@HeaderParam(AUTHORIZATION) Optional<AuthHeader> authHeader,
@PathParam("problemSetJid") String problemSetJid,
@PathParam("problemAlias") String problemAlias,
@QueryParam("language") Optional<String> language) {

actorChecker.check(authHeader);
checkFound(problemSetStore.getProblemSetByJid(problemSetJid));
Expand Down Expand Up @@ -211,12 +211,14 @@ public ProblemSetProblemWorksheet getProblemWorksheet(
}
}

@Override
@GET
@Path("/{problemAlias}/report")
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public ProblemReportResponse getProblemReport(
Optional<AuthHeader> authHeader,
String problemSetJid,
String problemAlias) {
@HeaderParam(AUTHORIZATION) Optional<AuthHeader> authHeader,
@PathParam("problemSetJid") String problemSetJid,
@PathParam("problemAlias") String problemAlias) {

String actorJid = actorChecker.check(authHeader);
checkFound(problemSetStore.getProblemSetByJid(problemSetJid));
Expand Down Expand Up @@ -253,13 +255,15 @@ public ProblemReportResponse getProblemReport(
.build();
}

@Override
@GET
@Path("/{problemAlias}/editorial")
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public ProblemEditorialResponse getProblemEditorial(
UriInfo uriInfo,
String problemSetJid,
String problemAlias,
Optional<String> language) {
@Context UriInfo uriInfo,
@PathParam("problemSetJid") String problemSetJid,
@PathParam("problemAlias") String problemAlias,
@QueryParam("language") Optional<String> language) {

checkFound(problemSetStore.getProblemSetByJid(problemSetJid));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package judgels.jophiel.profile;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static judgels.service.ServiceUtils.checkFound;

import io.dropwizard.hibernate.UnitOfWork;
Expand All @@ -9,36 +10,48 @@
import java.util.Optional;
import java.util.Set;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import judgels.jophiel.api.profile.BasicProfile;
import judgels.jophiel.api.profile.Profile;
import judgels.jophiel.api.profile.ProfileService;
import judgels.persistence.api.Page;

public class ProfileResource implements ProfileService {
private final Clock clock;
private final ProfileStore profileStore;
@Path("/api/v2/profiles")
public class ProfileResource {
@Inject protected Clock clock;
@Inject protected ProfileStore profileStore;

@Inject
public ProfileResource(Clock clock, ProfileStore profileStore) {
this.clock = clock;
this.profileStore = profileStore;
}
@Inject public ProfileResource() {}

@Override
@POST
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public Map<String, Profile> getProfiles(Set<String> userJids, Optional<Long> time) {
return profileStore.getProfiles(userJids, time.map(Instant::ofEpochMilli).orElse(clock.instant()));
}

@Override
@GET
@Path("/top")
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public Page<Profile> getTopRatedProfiles(Optional<Integer> pageNumber, Optional<Integer> pageSize) {
public Page<Profile> getTopRatedProfiles(
@QueryParam("page") Optional<Integer> pageNumber,
@QueryParam("pageSize") Optional<Integer> pageSize) {

return profileStore.getTopRatedProfiles(clock.instant(), pageNumber.orElse(1), pageSize.orElse(50));
}

@Override
@GET
@Path("/{userJid}/basic")
@Produces(APPLICATION_JSON)
@UnitOfWork(readOnly = true)
public BasicProfile getBasicProfile(String userJid) {
public BasicProfile getBasicProfile(@PathParam("userJid") String userJid) {
return checkFound(profileStore.getBasicProfile(clock.instant(), userJid));
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package judgels.jophiel.session;

import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static judgels.service.ServiceUtils.checkFound;

import io.dropwizard.hibernate.UnitOfWork;
import java.util.Optional;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import judgels.jophiel.api.session.Credentials;
import judgels.jophiel.api.session.GoogleCredentials;
import judgels.jophiel.api.session.Session;
import judgels.jophiel.api.session.SessionErrors;
import judgels.jophiel.api.session.SessionService;
import judgels.jophiel.api.user.User;
import judgels.jophiel.auth.google.GoogleAuth;
import judgels.jophiel.user.UserRoleChecker;
Expand All @@ -19,35 +25,22 @@
import judgels.service.actor.ActorChecker;
import judgels.service.api.actor.AuthHeader;

public class SessionResource implements SessionService {
private final ActorChecker actorChecker;
private final UserRoleChecker roleChecker;
private final UserStore userStore;
private final UserRegistrationEmailStore userRegistrationEmailStore;
private final SessionStore sessionStore;
private final SessionConfiguration sessionConfiguration;
private final Optional<GoogleAuth> googleAuth;
@Path("/api/v2/session")
public class SessionResource {
@Inject protected ActorChecker actorChecker;
@Inject protected UserRoleChecker roleChecker;
@Inject protected UserStore userStore;
@Inject protected UserRegistrationEmailStore userRegistrationEmailStore;
@Inject protected SessionStore sessionStore;
@Inject protected SessionConfiguration sessionConfiguration;
@Inject protected Optional<GoogleAuth> googleAuth;

@Inject
public SessionResource(
ActorChecker actorChecker,
UserRoleChecker roleChecker,
UserStore userStore,
UserRegistrationEmailStore userRegistrationEmailStore,
SessionStore sessionStore,
SessionConfiguration sessionConfiguration,
Optional<GoogleAuth> googleAuth) {
@Inject public SessionResource() {}

this.actorChecker = actorChecker;
this.roleChecker = roleChecker;
this.userStore = userStore;
this.userRegistrationEmailStore = userRegistrationEmailStore;
this.sessionStore = sessionStore;
this.sessionConfiguration = sessionConfiguration;
this.googleAuth = googleAuth;
}

@Override
@POST
@Path("/login")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@UnitOfWork
public Session logIn(Credentials credentials) {
User user = userStore.getUserByUsernameAndPassword(credentials.getUsernameOrEmail(), credentials.getPassword())
Expand All @@ -71,7 +64,10 @@ public Session logIn(Credentials credentials) {
return sessionStore.createSession(SessionTokenGenerator.newToken(), user.getJid());
}

@Override
@POST
@Path("/login-google")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@UnitOfWork
public Session logInWithGoogle(GoogleCredentials credentials) {
String email = checkFound(googleAuth).verifyIdToken(credentials.getIdToken()).getEmail();
Expand All @@ -80,9 +76,10 @@ public Session logInWithGoogle(GoogleCredentials credentials) {
return sessionStore.createSession(SessionTokenGenerator.newToken(), user.getJid());
}

@Override
@POST
@Path("/logout")
@UnitOfWork
public void logOut(AuthHeader authHeader) {
public void logOut(@HeaderParam(AUTHORIZATION) AuthHeader authHeader) {
String actorJid = actorChecker.check(authHeader);
if (!roleChecker.canAdminister(actorJid) && sessionConfiguration.getDisableLogout()) {
throw SessionErrors.logoutDisabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import judgels.uriel.api.contest.Contest;
import judgels.uriel.api.contest.problem.ContestProblem;
import judgels.uriel.api.contest.submission.ContestSubmissionConfig;
import judgels.uriel.api.contest.submission.bundle.ContestItemSubmissionService;
import judgels.uriel.api.contest.submission.bundle.ContestItemSubmissionsResponse;
import judgels.uriel.api.contest.submission.bundle.ContestSubmissionSummaryResponse;
import judgels.uriel.contest.ContestRoleChecker;
Expand All @@ -62,7 +61,7 @@
import org.slf4j.MarkerFactory;

@Path("/api/v2/contests/submissions/bundle")
public class ContestItemSubmissionResource implements ContestItemSubmissionService {
public class ContestItemSubmissionResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ContestItemSubmissionResource.class);
private static final Marker ITEM_SUBMISSION_MARKER = MarkerFactory.getMarker("ITEM_SUBMISSION");
private static final int PAGE_SIZE = 20;
Expand Down

0 comments on commit a0ceae2

Please sign in to comment.