app/controllers/LoginHistoryController.scala (75 lines of code) (raw):

package controllers import akka.actor.ActorSystem import akka.stream.{ActorMaterializer, Materializer} import helpers.{ESClientManager, ZonedDateTimeEncoder} import javax.inject.{Inject, Singleton} import models.RecentLogin import play.api.{Configuration, Logger} import play.api.http.{DefaultHttpErrorHandler, HttpErrorHandler, ParserConfiguration} import play.api.libs.Files.TemporaryFileCreator import play.api.libs.circe.Circe import play.api.mvc.{AbstractController, ControllerComponents, PlayBodyParsers} import responses.{GenericErrorResponse, ObjectListResponse} import io.circe.syntax._ import io.circe.generic.auto._ import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global @Singleton class LoginHistoryController @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 updateLogins = Action.async(parse.xml) { request=> val client = esClientMgr.getClient() val maybeLoginsList = (request.body \ "recentLogins").map(RecentLogin.fromXml(_)) val errors = maybeLoginsList.collect({ case Left(err)=>err }) if(errors.nonEmpty){ Future(BadRequest(GenericErrorResponse("bad_data", errors.mkString(";")).asJson)) } else { val loginsList = maybeLoginsList.collect({ case Right(log)=>log }) val resulFutures = Future.traverse(loginsList) { loginEntry=> client.execute { update(loginEntry.idForElastic).in(s"$loginsIndex/login").docAsUpsert(loginEntry) } } resulFutures.map(results=>{ val outErrors = results.collect({case Left(err)=>err}) if(outErrors.nonEmpty){ InternalServerError(GenericErrorResponse("index_error", outErrors.mkString(";")).asJson) } else { Ok(GenericErrorResponse("ok", s"${results.length} records stored").asJson) } }) } } def loginsFor(hostName:String, limit:Option[Int]) = Action.async { val client = esClientMgr.getClient() val actualLimit = limit match { case Some(lim)=>lim case None=>10 } val resultFuture = client.execute { search(s"$loginsIndex/login") query { termQuery("hostname", hostName) } sortByFieldDesc "loginTime" limit actualLimit } resultFuture.map({ case Left(err)=>InternalServerError(GenericErrorResponse("search_error", err.toString).asJson) case Right(results)=>Ok(ObjectListResponse("ok","login_history", results.result.to[RecentLogin], results.result.totalHits.toInt).asJson) }) } }