diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/Constants.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/Constants.java index a2f31f123..aab75d804 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/Constants.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/Constants.java @@ -168,6 +168,7 @@ public enum IsaacServerLogType implements LogType { UPDATE_QUIZ_FEEDBACK_MODE, VIEW_ASSIGNMENT_PROGRESS, VIEW_CONCEPT, + VIEW_BOOK_PAGE, VIEW_MY_BOARDS_PAGE, VIEW_PAGE, VIEW_PAGE_FRAGMENT, diff --git a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java index e408ff6bf..ab9fc2c14 100644 --- a/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java +++ b/src/main/java/uk/ac/cam/cl/dtg/isaac/api/PagesFacade.java @@ -32,6 +32,7 @@ import uk.ac.cam.cl.dtg.isaac.dos.QuestionValidationResponse; import uk.ac.cam.cl.dtg.isaac.dos.content.Content; import uk.ac.cam.cl.dtg.isaac.dto.GameboardDTO; +import uk.ac.cam.cl.dtg.isaac.dto.IsaacBookPageDTO; import uk.ac.cam.cl.dtg.isaac.dto.IsaacConceptPageDTO; import uk.ac.cam.cl.dtg.isaac.dto.IsaacPageFragmentDTO; import uk.ac.cam.cl.dtg.isaac.dto.IsaacQuestionPageDTO; @@ -850,6 +851,64 @@ public final Response getPageFragment(@Context final Request request, } } + /** + * Endpoint that gets a single book index page from the given id. + * + * @param request + * - so that we can deal with caching. + * @param httpServletRequest + * - so that we can extract user information. + * @param bookId + * as a string + * @return A Response object containing a page fragment object or containing a SegueErrorResponse. + */ + @GET + @Path("/books/{book_id}") + @Produces(MediaType.APPLICATION_JSON) + @GZIP + @Operation(summary = "Get a book index page by ID.") + public final Response getBookPage(@Context final Request request, + @Context final HttpServletRequest httpServletRequest, + @PathParam("book_id") final String bookId) { + + // Calculate the ETag on current live version of the content + EntityTag etag = new EntityTag(String.valueOf(this.contentManager.getCurrentContentSHA().hashCode() + bookId.hashCode())); + Response cachedResponse = generateCachedResponse(request, etag); + if (cachedResponse != null) { + return cachedResponse; + } + + try { + ContentDTO contentDTO = contentManager.getContentById(bookId, true); + if (contentDTO instanceof IsaacBookPageDTO) { + // Unlikely we want to augment with related content here! + + // Log the page view: + getLogManager().logEvent(userManager.getCurrentUser(httpServletRequest), httpServletRequest, + IsaacServerLogType.VIEW_BOOK_PAGE, ImmutableMap.of( + PAGE_ID_LOG_FIELDNAME, bookId, + CONTENT_VERSION_FIELDNAME, this.contentManager.getCurrentContentSHA() + )); + + return Response.ok(contentDTO) + .cacheControl(getCacheControl(NUMBER_SECONDS_IN_ONE_HOUR, true)) + .tag(etag) + .build(); + } else { + log.warn("Unable to locate a book page with the id '{}'!", bookId); + return SegueErrorResponse.getResourceNotFoundResponse("Unable to locate a book page with the id specified!"); + } + } catch (SegueDatabaseException e) { + SegueErrorResponse error = new SegueErrorResponse(Status.INTERNAL_SERVER_ERROR, "Database error while processing request."); + log.error(error.getErrorMessage(), e); + return error.toResponse(); + } catch (ContentManagerException e) { + SegueErrorResponse error = new SegueErrorResponse(Status.INTERNAL_SERVER_ERROR, "Error locating the content requested"); + log.error(error.getErrorMessage(), e); + return error.toResponse(); + } + } + /** * Rest endpoint retrieving the first MAX_PODS_TO_RETURN pods by ID. *