backend/app/controllers/api/Resource.scala (77 lines of code) (raw):

package controllers.api import org.apache.pekko.stream.scaladsl.StreamConverters import org.apache.pekko.util.ByteString import commands._ import model.frontend.{Chips, HighlightableText, ResourcesForExtractionFailureRequest} import model.index.{FrontendPage, FrontendPageResult, Page, PageHighlight, PageResult} import model.user.UserPermission.CanPerformAdminOperations import model.{English, Language, Uri} import org.apache.pdfbox.pdmodel.PDDocument import play.api.http.HttpEntity import play.api.libs.json._ import play.api.mvc.{ResponseHeader, Result} import play.utils.UriEncoding import services.{ObjectStorage, PreviewConfig} import services.annotations.Annotations import services.index.{Index, Pages} import services.manifest.Manifest import services.previewing.PreviewService import utils.PDFUtil import utils.attempt.{Attempt, IllegalStateFailure} import utils.controller.{AuthApiController, AuthControllerComponents} import java.io.{ByteArrayOutputStream, InputStream} import java.nio.charset.StandardCharsets case class FlagData(flagValue: String) object FlagData { implicit val flagDataFormat = Json.format[FlagData] } class Resource(val controllerComponents: AuthControllerComponents, manifest: Manifest, index: Index, pages: Pages, annotations: Annotations, previewStorage: ObjectStorage) extends AuthApiController { def getResource(uri: Uri, basic: Boolean, q: Option[String]) = ApiAction.attempt { implicit req => val parsedChips = q.map(Chips.parseQueryString) val resourceFetchMode = (basic, parsedChips) match { case (true, _) => ResourceFetchMode.Basic case (false, pc) => ResourceFetchMode.WithData(pc.map(_.query)) } val decodedUri = Uri(UriEncoding.decodePath(uri.value, StandardCharsets.UTF_8)) val command = GetResource(decodedUri, resourceFetchMode, req.user.username, manifest, index, annotations, controllerComponents.users) command.process().map { file => Ok(Json.toJson(file)) } } // TODO MRB: filter by collection and ingestion by writing failures as events to Elasticsearch def getExtractionFailures = ApiAction.attempt { req => checkPermission(CanPerformAdminOperations, req) { Attempt.fromEither { manifest.getFailedExtractions.map(failures => Ok(Json.toJson(failures))) } } } // This isn't conceptually a POST but we couldn't put the stack trace as a query parameter without it breaking the // default maximum size for them in Play. It would be better to attach an ID to each extraction failure but that // would be a breaking schema change def getResourcesForExtractionFailure(page: Int, pageSize: Int) = ApiAction.attempt(parse.json) { req => checkPermission(CanPerformAdminOperations, req) { Attempt.fromEither { val request = req.body.as[ResourcesForExtractionFailureRequest] val skip = (page - 1) * pageSize val resources = manifest.getResourcesForExtractionFailure(request.extractorName, request.stackTrace, page, skip, pageSize) resources.map(r => Ok(Json.toJson(r))) } } } def getTextPages(uri: Uri, top: Double, bottom: Double, q: Option[String], language: Option[Language]) = ApiAction.attempt { req => val parsedChips = q.map(Chips.parseQueryString) for { // Check we have permission to see this file _ <- GetResource(uri, ResourceFetchMode.Basic, req.user.username, manifest, index, annotations, controllerComponents.users).process() response <- new GetPages(uri, top, bottom, parsedChips.map(_.query), language, pages, previewStorage).process() } yield { Ok(Json.toJson(response)) } } def getPagePreview(uri: Uri, language: Language, pageNumber: Int) = ApiAction.attempt { req => for { // Check we have permission to see this file _ <- GetResource(uri, ResourceFetchMode.Basic, req.user.username, manifest, index, annotations, controllerComponents.users).process() response <- new GetPagePreview(uri, language, pageNumber, previewStorage).process() } yield { Result(ResponseHeader(200, Map.empty), response) } } }