package edu.lmu.cs.headmaster.ws.resource; import java.net.URI; import java.util.List; import javax.ws.rs.Path; import javax.ws.rs.core.Response; import org.joda.time.DateTime; import edu.lmu.cs.headmaster.ws.dao.UserDao; import edu.lmu.cs.headmaster.ws.domain.Event; import edu.lmu.cs.headmaster.ws.domain.Student; import edu.lmu.cs.headmaster.ws.domain.StudentRecord; import edu.lmu.cs.headmaster.ws.service.StudentService; import edu.lmu.cs.headmaster.ws.types.ClassYear; import edu.lmu.cs.headmaster.ws.types.Term; import edu.lmu.cs.headmaster.ws.util.ServiceException; /** * The sole implementation of the student resource. */ @Path("/students") public class StudentResourceImpl extends AbstractResource implements StudentResource { private StudentService studentService; /** * Creates a student resource with the injected dao. */ public StudentResourceImpl(UserDao userDao, StudentService studentService) { super(userDao); this.studentService = studentService; } /** * Returns students according to the search parameters * * @param query the query * @param skip the number of initial results to skip * @param max the maximum number of results to display * @return the (paginated) set of students matching the query parameters */ @Override public List<Student> getStudents(String query, Boolean active, Boolean transferStudent, ClassYear classYear, Integer expectedGraduationYearFrom, Integer expectedGraduationYearTo, Double minCumulativeGpa, Double maxCumulativeGpa, Double minTermGpa, Double maxTermGpa, Term term, Integer year, int skip, int max) { logServiceCall(); // classYear is mutually exclusive with (expectedGraduationYearFrom, // expectedGraduationYearTo). // TODO This argument processing is part of the business logic, and not // resource-specific; it should be transferred to the service layer. validate( (classYear == null && expectedGraduationYearFrom == null && expectedGraduationYearTo == null) || (classYear != null && expectedGraduationYearFrom == null && expectedGraduationYearTo == null) || (classYear == null && (expectedGraduationYearFrom != null || expectedGraduationYearTo != null)), Response.Status.BAD_REQUEST, ARGUMENT_CONFLICT ); // make sure cumulative gpa and term gpa queries are mutually exclusive boolean gpaTermOrYearAreProvided = term != null || year != null; boolean gpaTermAndYearAreProvided = term != null && year != null; boolean isCumulativeGpaQuery = (minCumulativeGpa != null || maxCumulativeGpa!= null); boolean isTermGpaQuery = (minTermGpa != null || maxTermGpa != null); if (isCumulativeGpaQuery || isTermGpaQuery) { // Validate we have admin privileges validatePrivilegedUserCredentials(); // Validate we don't have both, they are mutually exclusive. validate( (isCumulativeGpaQuery != isTermGpaQuery), Response.Status.BAD_REQUEST, ARGUMENT_CONFLICT ); // Validate we don't have parameters from the two types of GPA queries mixing with each other validate(!(isCumulativeGpaQuery && gpaTermOrYearAreProvided), Response.Status.BAD_REQUEST, ARGUMENT_CONFLICT); // Validate we don't have incomplete term gpa query validate(!(isTermGpaQuery && !gpaTermAndYearAreProvided), Response.Status.BAD_REQUEST, ARGUMENT_CONFLICT); } else { // Validate we don't only have gpaTerm or gpaYear for querying term gpa validate(!gpaTermOrYearAreProvided, Response.Status.BAD_REQUEST, ARGUMENT_CONFLICT); } // At least one of query, classYear, expectedGraduationYearFrom, // expectedGraduationYearTo, or either of the GPA queries must be set. validate(query != null || classYear != null || expectedGraduationYearFrom != null || expectedGraduationYearTo != null || transferStudent != null || isCumulativeGpaQuery || isTermGpaQuery, Response.Status.BAD_REQUEST, QUERY_REQUIRED); // The classYear parameter produces an expected graduation year. if (classYear != null) { expectedGraduationYearFrom = expectedGraduationYearTo = classYear.getExpectedGraduationYear(new DateTime()); } return studentService.getStudents( query != null ? preprocessQuery(query, skip, max, 0, 100) : null, active, transferStudent, expectedGraduationYearFrom, expectedGraduationYearTo, minCumulativeGpa, maxCumulativeGpa, minTermGpa, maxTermGpa, term, year, skip, max ); } /** * Creates a student for which the server will generate the id. * * @param student the student object to create. The student must have a null id. * @return A response with HTTP 201 on success, or a response with HTTP 400 and message * <code>student.overspecified</code> if the student's id is not null. */ @Override public Response createStudent(Student student) { logServiceCall(); validate(student.getId() == null, Response.Status.BAD_REQUEST, STUDENT_OVERSPECIFIED); // Dao problems will filter up as exceptions. studentService.createStudent(student); return Response.created(URI.create(Long.toString(student.getId()))).build(); } /** * Supposed to save the representation of the student with the given id. * Inconsistent data should result in HTTP 400, while a successful PUT * should return Response.noContent. * * @param id the id of the student to save. * @return A response with HTTP 204 no content on success, or a response * with HTTP 400 and message <code>student.inconsistent</code> if * checked data does not have the save id as requested in the URL. */ @Override public Response createOrUpdateStudent(Long id, Student student) { logServiceCall(); // The student IDs should match. validate(id.equals(student.getId()), Response.Status.BAD_REQUEST, STUDENT_INCONSISTENT); // Dao problems will filter up as exceptions. studentService.createOrUpdateStudent(id, student); return Response.noContent().build(); } /** * Returns the student with the given id. * * @param id the id of the requested student. * @return the student with the given id. * @throws ServiceException if there is no student with the given id, causing the framework * to generate an HTTP 404. */ @Override public Student getStudentById(Long id) { logServiceCall(); Student student = studentService.getStudentById(id); validate(student != null, Response.Status.NOT_FOUND, STUDENT_NOT_FOUND); return student; } /** * Returns the events attended by the student with the given id. * * @param id the id of the requested student. * @return the events attended by the student with the given id. * @throws ServiceException if there is no student with the given id, causing the framework * to generate an HTTP 404. */ @Override public List<Event> getStudentAttendanceById(Long id) { logServiceCall(); List<Event> events = studentService.getStudentAttendanceById(id); validate(events != null, Response.Status.NOT_FOUND, STUDENT_NOT_FOUND); return events; } /** * Returns the student record for the student with the given id. */ @Override public StudentRecord getStudentRecordById(Long id) { return getStudentById(id).getRecord(); } /** * Updates the record for the student with the given id. */ @Override public Response updateStudentRecord(Long id, StudentRecord studentRecord) { // Dao problems will filter up as exceptions. studentService.updateStudentRecord(id, studentRecord); return Response.noContent().build(); } }