app/controllers/HostInfoController.scala (80 lines of code) (raw):

package controllers import java.time.ZonedDateTime import akka.actor.ActorSystem import akka.stream.{ActorMaterializer, Materializer} import com.sksamuel.elastic4s.http.ElasticDsl.update import helpers.{ESClientManager, ZonedDateTimeEncoder} import javax.inject.{Inject, Singleton} import play.api.{Configuration, Logger} import play.api.mvc.{AbstractController, ControllerComponents, PlayBodyParsers} import models.{HostInfo, RecentLogin} import responses.{GenericErrorResponse, ObjectListResponse, ErrorListResponse} import io.circe.generic.auto._ import io.circe.syntax._ import play.api.http.{DefaultHttpErrorHandler, HttpErrorHandler, ParserConfiguration} import play.api.libs.Files.TemporaryFileCreator import play.api.libs.circe.Circe import play.api.libs.json.Json import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits._ import scala.concurrent.Future @Singleton class HostInfoController @Inject()(playConfig:Configuration,cc:ControllerComponents, defaultTempFileCreator:TemporaryFileCreator, esClientMgr:ESClientManager)(implicit system:ActorSystem) extends AbstractController(cc) with PlayBodyParsers with ZonedDateTimeEncoder with Circe { import com.sksamuel.elastic4s.http.ElasticDsl._ import com.sksamuel.elastic4s.circe._ private val logger = Logger(getClass) implicit def materializer:Materializer = ActorMaterializer() protected val indexName = playConfig.get[String]("elasticsearch.indexName") protected val loginsIndex = playConfig.get[String]("elasticsearch.loginsIndexName") override def config:ParserConfiguration = { ParserConfiguration(2048*1024,10*1024*1024) } override def errorHandler:HttpErrorHandler = DefaultHttpErrorHandler override def temporaryFileCreator:TemporaryFileCreator = defaultTempFileCreator def addRecord = Action.async(parse.xml) { request=> val client = esClientMgr.getClient() HostInfo.fromXml(request.body, ZonedDateTime.now()) match { case Right(entry)=> val idToUse = s"${entry.hostName}" client.execute { update(idToUse).in(s"$indexName/entry").docAsUpsert(entry) }.map({ case Left(failure) => InternalServerError(GenericErrorResponse("elasticsearch_error", failure.error.toString).asJson) case Right(success) => Ok(success.result.id) }).recoverWith({ case ex:Throwable=> logger.error("Could not create host entry", ex) Future(InternalServerError(GenericErrorResponse("elasticsearch_error", ex.getLocalizedMessage).asJson)) }) case Left(err)=> Future(BadRequest(ErrorListResponse("bad_data", err).asJson)) } } def simpleStringSearch(q:Option[String],start:Option[Int],length:Option[Int]) = Action.async { val cli = esClientMgr.getClient() val actualStart=start.getOrElse(0) val actualLength=length.getOrElse(50) q match { case Some(searchTerms) => val searchString = s"*$searchTerms*" val responseFuture = cli.execute { search(indexName) query searchString from actualStart size actualLength } responseFuture.map({ case Left(failure) => InternalServerError(Json.obj("status" -> "error", "detail" -> failure.toString)) case Right(results) => val resultList = results.result.to[HostInfo] //using the HostInfoHitReader trait Ok(ObjectListResponse[IndexedSeq[HostInfo]]("ok","entry",resultList,results.result.totalHits.toInt).asJson) }).recover({ case ex:Throwable=> logger.error("Could not process result from elastic: ", ex) InternalServerError(GenericErrorResponse("error",ex.toString).asJson) }) case None => Future(BadRequest(GenericErrorResponse("error", "you must specify a query string with ?q={string}").asJson)) } } }