diff --git a/libraries.sbt b/libraries.sbt index dbe25f3..97b066e 100644 --- a/libraries.sbt +++ b/libraries.sbt @@ -1,19 +1,20 @@ libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3" libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2" -libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.1.10" -libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % "10.1.10" -libraryDependencies += "com.typesafe.akka" %% "akka-stream" % "2.5.23" -libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.0.4" +libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.2.1" +libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % "10.2.1" +libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.6.10" +libraryDependencies += "com.typesafe.akka" %% "akka-stream" % "2.6.10" +libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.3.0" libraryDependencies += "javax.ws.rs" % "javax.ws.rs-api" % "2.1.1" -libraryDependencies += "io.spray" %% "spray-json" % "1.3.5" -libraryDependencies += "com.lihaoyi" %% "scalatags" % "0.7.0" +libraryDependencies += "io.spray" %% "spray-json" % "1.3.6" +libraryDependencies += "com.lihaoyi" %% "scalatags" % "0.9.2" -libraryDependencies += "com.typesafe.slick" %% "slick" % "3.3.2" -libraryDependencies += "com.typesafe.slick" %% "slick-hikaricp" % "3.3.2" +libraryDependencies += "com.typesafe.slick" %% "slick" % "3.3.3" +libraryDependencies += "com.typesafe.slick" %% "slick-hikaricp" % "3.3.3" libraryDependencies += "org.flywaydb" % "flyway-core" % "6.0.6" -libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.28.0" -libraryDependencies += "org.postgresql" % "postgresql" % "9.3-1104-jdbc4" +libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.32.3.2" +libraryDependencies += "org.postgresql" % "postgresql" % "42.2.18" libraryDependencies += "org.mindrot" % "jbcrypt" % "0.3m" diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 29e36fc..2bf4453 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -54,12 +54,6 @@ me.arcanis.ffxivbis { port = 8000 # hostname to use in docs, if not set host:port will be used #hostname = "127.0.0.1:8000" - - # rate limits - limits { - intetval = 1m - max-count = 60 - } } default-dispatcher { diff --git a/src/main/scala/me/arcanis/ffxivbis/Application.scala b/src/main/scala/me/arcanis/ffxivbis/Application.scala index dbbb7d1..3cd35d1 100644 --- a/src/main/scala/me/arcanis/ffxivbis/Application.scala +++ b/src/main/scala/me/arcanis/ffxivbis/Application.scala @@ -8,46 +8,53 @@ */ package me.arcanis.ffxivbis -import akka.actor.{Actor, Props} +import akka.actor.typed.{Behavior, PostStop, Signal} +import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors} import akka.http.scaladsl.Http -import akka.stream.ActorMaterializer +import akka.stream.Materializer import com.typesafe.scalalogging.StrictLogging import me.arcanis.ffxivbis.http.RootEndpoint import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.impl.DatabaseImpl -import me.arcanis.ffxivbis.service.PartyService +import me.arcanis.ffxivbis.service.{Database, PartyService} import me.arcanis.ffxivbis.storage.Migration -import scala.concurrent.duration.Duration -import scala.concurrent.{Await, ExecutionContext} -import scala.util.{Failure, Success} +import scala.concurrent.ExecutionContext -class Application extends Actor with StrictLogging { - implicit private val executionContext: ExecutionContext = context.system.dispatcher - implicit private val materializer: ActorMaterializer = ActorMaterializer() +class Application(context: ActorContext[Nothing]) + extends AbstractBehavior[Nothing](context) with StrictLogging { - private val config = context.system.settings.config - private val host = config.getString("me.arcanis.ffxivbis.web.host") - private val port = config.getInt("me.arcanis.ffxivbis.web.port") + logger.info("root supervisor started") + startApplication() - override def receive: Receive = Actor.emptyBehavior + override def onMessage(msg: Nothing): Behavior[Nothing] = Behaviors.unhandled - Migration(config).onComplete { - case Success(_) => - val bisProvider = context.system.actorOf(BisProvider.props, "bis-provider") - val storage = context.system.actorOf(DatabaseImpl.props, "storage") - val party = context.system.actorOf(PartyService.props(storage), "party") - val http = new RootEndpoint(context.system, party, bisProvider) + override def onSignal: PartialFunction[Signal, Behavior[Nothing]] = { + case PostStop => + logger.info("root supervisor stopped") + Behaviors.same + } - logger.info(s"start server at $host:$port") - val bind = Http()(context.system).bindAndHandle(http.route, host, port) - Await.result(context.system.whenTerminated, Duration.Inf) - bind.foreach(_.unbind()) + private def startApplication(): Unit = { + val config = context.system.settings.config + val host = config.getString("me.arcanis.ffxivbis.web.host") + val port = config.getInt("me.arcanis.ffxivbis.web.port") - case Failure(exception) => throw exception + implicit val executionContext: ExecutionContext = context.system.executionContext + implicit val materializer: Materializer = Materializer(context) + + Migration(config) + + val bisProvider = context.spawn(BisProvider(), "bis-provider") + val storage = context.spawn(Database(), "storage") + val party = context.spawn(PartyService(storage), "party") + val http = new RootEndpoint(context.system, party, bisProvider) + + Http()(context.system).newServerAt(host, port).bindFlow(http.route) } } object Application { - def props: Props = Props(new Application) + + def apply(): Behavior[Nothing] = + Behaviors.setup[Nothing](context => new Application(context)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/ffxivbis.scala b/src/main/scala/me/arcanis/ffxivbis/ffxivbis.scala index 31f6ca0..6ad9732 100644 --- a/src/main/scala/me/arcanis/ffxivbis/ffxivbis.scala +++ b/src/main/scala/me/arcanis/ffxivbis/ffxivbis.scala @@ -8,13 +8,13 @@ */ package me.arcanis.ffxivbis -import akka.actor.ActorSystem +import akka.actor.typed.ActorSystem import com.typesafe.config.ConfigFactory object ffxivbis { + def main(args: Array[String]): Unit = { val config = ConfigFactory.load() - val actorSystem = ActorSystem("ffxivbis", config) - actorSystem.actorOf(Application.props, "ffxivbis") + ActorSystem[Nothing](Application(), "ffxivbis", config) } } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala b/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala index b62d9e8..97946cc 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala @@ -8,22 +8,22 @@ */ package me.arcanis.ffxivbis.http -import akka.actor.ActorRef +import akka.actor.typed.scaladsl.AskPattern.Askable +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.server.AuthenticationFailedRejection._ import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ -import akka.pattern.ask import akka.util.Timeout -import me.arcanis.ffxivbis.models.{Permission, User} -import me.arcanis.ffxivbis.service.impl.DatabaseUserHandler +import me.arcanis.ffxivbis.messages.{GetUser, Message} +import me.arcanis.ffxivbis.models.Permission import scala.concurrent.{ExecutionContext, Future} // idea comes from https://synkre.com/bcrypt-for-akka-http-password-encryption/ trait Authorization { - def storage: ActorRef + def storage: ActorRef[Message] def authenticateBasicBCrypt[T](realm: String, authenticate: (String, String) => Future[Option[T]]): Directive1[T] = { @@ -40,21 +40,21 @@ trait Authorization { } def authenticator(scope: Permission.Value, partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[String]] = - (storage ? DatabaseUserHandler.GetUser(partyId, username)).mapTo[Option[User]].map { + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = + storage.ask(GetUser(partyId, username, _)).map { case Some(user) if user.verify(password) && user.verityScope(scope) => Some(username) case _ => None } def authAdmin(partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[String]] = + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = authenticator(Permission.admin, partyId)(username, password) def authGet(partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[String]] = + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = authenticator(Permission.get, partyId)(username, password) def authPost(partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[String]] = + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = authenticator(Permission.post, partyId)(username, password) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala index 25b71b1..2ee6a26 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala @@ -8,37 +8,37 @@ */ package me.arcanis.ffxivbis.http -import akka.actor.ActorRef -import akka.pattern.ask +import akka.actor.typed.scaladsl.AskPattern.Askable +import akka.actor.typed.{ActorRef, Scheduler} import akka.util.Timeout import me.arcanis.ffxivbis.http.api.v1.json.ApiAction +import me.arcanis.ffxivbis.messages.{AddPieceToBis, GetBiS, Message, RemovePieceFromBiS, RemovePiecesFromBiS} import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId} -import me.arcanis.ffxivbis.service.impl.DatabaseBiSHandler import scala.concurrent.{ExecutionContext, Future} trait BiSHelper extends BisProviderHelper { - def storage: ActorRef + def storage: ActorRef[Message] def addPieceBiS(playerId: PlayerId, piece: Piece) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabaseBiSHandler.AddPieceToBis(playerId, piece.withJob(playerId.job))).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(AddPieceToBis(playerId, piece.withJob(playerId.job), _)) def bis(partyId: String, playerId: Option[PlayerId]) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] = - (storage ? DatabaseBiSHandler.GetBiS(partyId, playerId)).mapTo[Seq[Player]] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[Player]] = + storage.ask(GetBiS(partyId, playerId, _)) def doModifyBiS(action: ApiAction.Value, playerId: PlayerId, piece: Piece) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = action match { case ApiAction.add => addPieceBiS(playerId, piece) case ApiAction.remove => removePieceBiS(playerId, piece) } def putBiS(playerId: PlayerId, link: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { - (storage ? DatabaseBiSHandler.RemovePiecesFromBiS(playerId)).flatMap { _ => + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = { + storage.ask(RemovePiecesFromBiS(playerId, _)).flatMap { _ => downloadBiS(link, playerId.job).flatMap { bis => Future.traverse(bis.pieces)(addPieceBiS(playerId, _)) }.map(_ => ()) @@ -46,7 +46,7 @@ trait BiSHelper extends BisProviderHelper { } def removePieceBiS(playerId: PlayerId, piece: Piece) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabaseBiSHandler.RemovePieceFromBiS(playerId, piece)).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(RemovePieceFromBiS(playerId, piece, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala index 0237a18..6f81ef7 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala @@ -8,19 +8,19 @@ */ package me.arcanis.ffxivbis.http -import akka.actor.ActorRef -import akka.pattern.ask +import akka.actor.typed.{ActorRef, Scheduler} +import akka.actor.typed.scaladsl.AskPattern.Askable import akka.util.Timeout +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, DownloadBiS} import me.arcanis.ffxivbis.models.{BiS, Job} -import me.arcanis.ffxivbis.service.bis.BisProvider -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future trait BisProviderHelper { - def ariyala: ActorRef + def provider: ActorRef[BiSProviderMessage] def downloadBiS(link: String, job: Job.Job) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[BiS] = - (ariyala ? BisProvider.GetBiS(link, job)).mapTo[BiS] + (implicit timeout: Timeout, scheduler: Scheduler): Future[BiS] = + provider.ask(DownloadBiS(link, job, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala index 2ff2604..a556bd0 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala @@ -8,26 +8,26 @@ */ package me.arcanis.ffxivbis.http -import akka.actor.ActorRef -import akka.pattern.ask +import akka.actor.typed.scaladsl.AskPattern.Askable +import akka.actor.typed.{ActorRef, Scheduler} import akka.util.Timeout import me.arcanis.ffxivbis.http.api.v1.json.ApiAction +import me.arcanis.ffxivbis.messages.{AddPieceTo, GetLoot, Message, RemovePieceFrom, SuggestLoot} import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId, PlayerIdWithCounters} -import me.arcanis.ffxivbis.service.LootSelector.LootSelectorResult -import me.arcanis.ffxivbis.service.impl.DatabaseLootHandler import scala.concurrent.{ExecutionContext, Future} trait LootHelper { - def storage: ActorRef + def storage: ActorRef[Message] def addPieceLoot(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabaseLootHandler.AddPieceTo(playerId, piece, isFreeLoot)).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask( + AddPieceTo(playerId, piece, isFreeLoot, _)) def doModifyLoot(action: ApiAction.Value, playerId: PlayerId, piece: Piece, maybeFree: Option[Boolean]) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = (action, maybeFree) match { case (ApiAction.add, Some(isFreeLoot)) => addPieceLoot(playerId, piece, isFreeLoot) case (ApiAction.remove, _) => removePieceLoot(playerId, piece) @@ -35,14 +35,14 @@ trait LootHelper { } def loot(partyId: String, playerId: Option[PlayerId]) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] = - (storage ? DatabaseLootHandler.GetLoot(partyId, playerId)).mapTo[Seq[Player]] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[Player]] = + storage.ask(GetLoot(partyId, playerId, _)) def removePieceLoot(playerId: PlayerId, piece: Piece) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabaseLootHandler.RemovePieceFrom(playerId, piece)).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(RemovePieceFrom(playerId, piece, _)) def suggestPiece(partyId: String, piece: Piece) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[PlayerIdWithCounters]] = - (storage ? DatabaseLootHandler.SuggestLoot(partyId, piece)).mapTo[LootSelectorResult].map(_.result) + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Seq[PlayerIdWithCounters]] = + storage.ask(SuggestLoot(partyId, piece, _)).map(_.result) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala index 8a74be2..c035bdc 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala @@ -8,56 +8,56 @@ */ package me.arcanis.ffxivbis.http -import akka.actor.ActorRef -import akka.pattern.ask +import akka.actor.typed.{ActorRef, Scheduler} +import akka.actor.typed.scaladsl.AskPattern.Askable import akka.util.Timeout import me.arcanis.ffxivbis.http.api.v1.json.ApiAction -import me.arcanis.ffxivbis.models.{Party, PartyDescription, Player, PlayerId} -import me.arcanis.ffxivbis.service.impl.{DatabaseBiSHandler, DatabasePartyHandler} +import me.arcanis.ffxivbis.messages.{AddPieceToBis, AddPlayer, GetParty, GetPartyDescription, GetPlayer, Message, RemovePlayer, UpdateParty} +import me.arcanis.ffxivbis.models.{PartyDescription, Player, PlayerId} import scala.concurrent.{ExecutionContext, Future} trait PlayerHelper extends BisProviderHelper { - def storage: ActorRef + def storage: ActorRef[Message] def addPlayer(player: Player) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabasePartyHandler.AddPlayer(player)).mapTo[Int].map { res => + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(ref => AddPlayer(player, ref)).map { res => player.link match { case Some(link) => downloadBiS(link, player.job).map { bis => - bis.pieces.map(storage ? DatabaseBiSHandler.AddPieceToBis(player.playerId, _)) + bis.pieces.map(piece => storage.ask(AddPieceToBis(player.playerId, piece, _))) }.map(_ => res) case None => Future.successful(res) } }.flatten def doModifyPlayer(action: ApiAction.Value, player: Player) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = action match { case ApiAction.add => addPlayer(player) case ApiAction.remove => removePlayer(player.playerId) } def getPartyDescription(partyId: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[PartyDescription] = - (storage ? DatabasePartyHandler.GetPartyDescription(partyId)).mapTo[PartyDescription] + (implicit timeout: Timeout, scheduler: Scheduler): Future[PartyDescription] = + storage.ask(GetPartyDescription(partyId, _)) def getPlayers(partyId: String, maybePlayerId: Option[PlayerId]) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] = + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Seq[Player]] = maybePlayerId match { case Some(playerId) => - (storage ? DatabasePartyHandler.GetPlayer(playerId)).mapTo[Option[Player]].map(_.toSeq) + storage.ask(GetPlayer(playerId, _)).map(_.toSeq) case None => - (storage ? DatabasePartyHandler.GetParty(partyId)).mapTo[Party].map(_.players.values.toSeq) + storage.ask(GetParty(partyId, _)).map(_.players.values.toSeq) } def removePlayer(playerId: PlayerId) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabasePartyHandler.RemovePlayer(playerId)).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(RemovePlayer(playerId, _)) def updateDescription(partyDescription: PartyDescription) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabasePartyHandler.UpdateParty(partyDescription)).mapTo[Int] + (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(UpdateParty(partyDescription, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala index a9fbe6c..eb3117b 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala @@ -10,25 +10,29 @@ package me.arcanis.ffxivbis.http import java.time.Instant -import akka.actor.{ActorRef, ActorSystem} +import akka.actor.typed.{ActorRef, ActorSystem, Scheduler} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import akka.util.Timeout import com.typesafe.scalalogging.{Logger, StrictLogging} import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint import me.arcanis.ffxivbis.http.view.RootView +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} -class RootEndpoint(system: ActorSystem, storage: ActorRef, ariyala: ActorRef) +class RootEndpoint(system: ActorSystem[Nothing], + storage: ActorRef[Message], + provider: ActorRef[BiSProviderMessage]) extends StrictLogging { import me.arcanis.ffxivbis.utils.Implicits._ private val config = system.settings.config + implicit val scheduler: Scheduler = system.scheduler implicit val timeout: Timeout = config.getDuration("me.arcanis.ffxivbis.settings.request-timeout") - private val rootApiV1Endpoint: RootApiV1Endpoint = new RootApiV1Endpoint(storage, ariyala, config) - private val rootView: RootView = new RootView(storage, ariyala) + private val rootApiV1Endpoint: RootApiV1Endpoint = new RootApiV1Endpoint(storage, provider, config) + private val rootView: RootView = new RootView(storage, provider) private val swagger: Swagger = new Swagger(config) private val httpLogger = Logger("http") diff --git a/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala b/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala index f3ed15d..698ffa8 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala @@ -16,6 +16,7 @@ import io.swagger.v3.oas.models.security.SecurityScheme import scala.io.Source class Swagger(config: Config) extends SwaggerHttpService { + override val apiClasses: Set[Class[_]] = Set( classOf[api.v1.BiSEndpoint], classOf[api.v1.LootEndpoint], classOf[api.v1.PartyEndpoint], classOf[api.v1.PlayerEndpoint], diff --git a/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala index 98aa939..3086e3a 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala @@ -8,35 +8,34 @@ */ package me.arcanis.ffxivbis.http -import akka.actor.ActorRef -import akka.pattern.ask +import akka.actor.typed.scaladsl.AskPattern.Askable +import akka.actor.typed.{ActorRef, Scheduler} import akka.util.Timeout +import me.arcanis.ffxivbis.messages.{AddUser, DeleteUser, GetNewPartyId, GetUser, GetUsers, Message} import me.arcanis.ffxivbis.models.User -import me.arcanis.ffxivbis.service.PartyService -import me.arcanis.ffxivbis.service.impl.DatabaseUserHandler -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future trait UserHelper { - def storage: ActorRef + def storage: ActorRef[Message] def addUser(user: User, isHashedPassword: Boolean) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabaseUserHandler.AddUser(user, isHashedPassword)).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(AddUser(user, isHashedPassword, _)) - def newPartyId(implicit executionContext: ExecutionContext, timeout: Timeout): Future[String] = - (storage ? PartyService.GetNewPartyId).mapTo[String] + def newPartyId(implicit timeout: Timeout, scheduler: Scheduler): Future[String] = + storage.ask(GetNewPartyId) def user(partyId: String, username: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[User]] = - (storage ? DatabaseUserHandler.GetUser(partyId, username)).mapTo[Option[User]] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Option[User]] = + storage.ask(GetUser(partyId, username, _)) def users(partyId: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[User]] = - (storage ? DatabaseUserHandler.GetUsers(partyId)).mapTo[Seq[User]] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[User]] = + storage.ask(GetUsers(partyId, _)) def removeUser(partyId: String, username: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] = - (storage ? DatabaseUserHandler.DeleteUser(partyId, username)).mapTo[Int] + (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage.ask(DeleteUser(partyId, username, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala index a95cd9d..2d7bf4d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala @@ -8,7 +8,7 @@ */ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.{HttpEntity, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ @@ -22,12 +22,15 @@ import io.swagger.v3.oas.annotations.{Operation, Parameter} import javax.ws.rs._ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.http.{Authorization, BiSHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import me.arcanis.ffxivbis.models.PlayerId import scala.util.{Failure, Success} @Path("api/v1") -class BiSEndpoint(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class BiSEndpoint(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends BiSHelper with Authorization with JsonSupport { def route: Route = createBiS ~ getBiS ~ modifyBiS diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/HttpHandler.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/HttpHandler.scala index a34140a..af7bced 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/HttpHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/HttpHandler.scala @@ -31,7 +31,7 @@ trait HttpHandler extends StrictLogging { this: JsonSupport => .mapRejectionResponse { case response @ HttpResponse(_, _, entity: HttpEntity.Strict, _) => val message = ErrorResponse(entity.data.utf8String).toJson - response.copy(entity = HttpEntity(ContentTypes.`application/json`, message.compactPrint)) + response.withEntity(HttpEntity(ContentTypes.`application/json`, message.compactPrint)) case other => other } } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala index 252f4e7..92215c6 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala @@ -8,7 +8,7 @@ */ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.{HttpEntity, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ @@ -22,12 +22,14 @@ import io.swagger.v3.oas.annotations.{Operation, Parameter} import javax.ws.rs._ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.http.{Authorization, LootHelper} +import me.arcanis.ffxivbis.messages.Message import me.arcanis.ffxivbis.models.PlayerId import scala.util.{Failure, Success} @Path("api/v1") -class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout) +class LootEndpoint(override val storage: ActorRef[Message]) + (implicit timeout: Timeout, scheduler: Scheduler) extends LootHelper with Authorization with JsonSupport with HttpHandler { def route: Route = getLoot ~ modifyLoot diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala index 1fd348f..c6a8a64 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala @@ -8,7 +8,7 @@ */ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.{HttpEntity, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ @@ -22,11 +22,14 @@ import io.swagger.v3.oas.annotations.{Operation, Parameter} import javax.ws.rs._ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.http.{Authorization, PlayerHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import scala.util.{Failure, Success} @Path("api/v1") -class PartyEndpoint(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class PartyEndpoint(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends PlayerHelper with Authorization with JsonSupport with HttpHandler { def route: Route = getPartyDescription ~ modifyPartyDescription diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala index 198af96..823a70f 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala @@ -8,7 +8,7 @@ */ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.{HttpEntity, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ @@ -22,12 +22,15 @@ import io.swagger.v3.oas.annotations.{Operation, Parameter} import javax.ws.rs._ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.http.{Authorization, PlayerHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import me.arcanis.ffxivbis.models.PlayerId import scala.util.{Failure, Success} @Path("api/v1") -class PlayerEndpoint(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class PlayerEndpoint(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends PlayerHelper with Authorization with JsonSupport with HttpHandler { def route: Route = getParty ~ modifyParty diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala index 26cd0d5..e4c8e36 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala @@ -8,21 +8,23 @@ */ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout import com.typesafe.config.Config import me.arcanis.ffxivbis.http.api.v1.json.JsonSupport +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} -class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef, config: Config) - (implicit timeout: Timeout) +class RootApiV1Endpoint(storage: ActorRef[Message], + provider: ActorRef[BiSProviderMessage], + config: Config)(implicit timeout: Timeout, scheduler: Scheduler) extends JsonSupport with HttpHandler { - private val biSEndpoint = new BiSEndpoint(storage, ariyala) + private val biSEndpoint = new BiSEndpoint(storage, provider) private val lootEndpoint = new LootEndpoint(storage) - private val partyEndpoint = new PartyEndpoint(storage, ariyala) - private val playerEndpoint = new PlayerEndpoint(storage, ariyala) + private val partyEndpoint = new PartyEndpoint(storage, provider) + private val playerEndpoint = new PlayerEndpoint(storage, provider) private val typesEndpoint = new TypesEndpoint(config) private val userEndpoint = new UserEndpoint(storage) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala index 9b33c30..55e8a33 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala @@ -8,7 +8,7 @@ */ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.{HttpEntity, StatusCodes} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ @@ -22,12 +22,14 @@ import io.swagger.v3.oas.annotations.{Operation, Parameter} import javax.ws.rs._ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.http.{Authorization, UserHelper} +import me.arcanis.ffxivbis.messages.Message import me.arcanis.ffxivbis.models.Permission import scala.util.{Failure, Success} @Path("api/v1") -class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout) +class UserEndpoint(override val storage: ActorRef[Message]) + (implicit timeout: Timeout, scheduler: Scheduler) extends UserHelper with Authorization with JsonSupport { def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala index 2a465f3..d07121e 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala @@ -9,10 +9,12 @@ case class LootResponse( @Schema(description = "looted piece", required = true) piece: PieceResponse, @Schema(description = "loot timestamp", required = true) timestamp: Instant, @Schema(description = "is loot free for all", required = true) isFreeLoot: Boolean) { + def toLoot: Loot = Loot(-1, piece.toPiece, timestamp, isFreeLoot) } object LootResponse { + def fromLoot(loot: Loot): LootResponse = LootResponse(PieceResponse.fromPiece(loot.piece), loot.timestamp, loot.isFreeLoot) } \ No newline at end of file diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala index ba585c1..fd5135b 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala @@ -14,10 +14,12 @@ import me.arcanis.ffxivbis.models.PartyDescription case class PartyDescriptionResponse( @Schema(description = "party id", required = true) partyId: String, @Schema(description = "party name") partyAlias: Option[String]) { + def toDescription: PartyDescription = PartyDescription(partyId, partyAlias) } object PartyDescriptionResponse { + def fromDescription(description: PartyDescription): PartyDescriptionResponse = PartyDescriptionResponse(description.partyId, description.partyAlias) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala index e2aefb6..56a05ee 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala @@ -15,10 +15,12 @@ case class PieceResponse( @Schema(description = "piece type", required = true) pieceType: String, @Schema(description = "job name to which piece belong or AnyJob", required = true, example = "DNC") job: String, @Schema(description = "piece name", required = true, example = "body") piece: String) { + def toPiece: Piece = Piece(piece, PieceType.withName(pieceType), Job.withName(job)) } object PieceResponse { + def fromPiece(piece: Piece): PieceResponse = PieceResponse(piece.pieceType.toString, piece.job.toString, piece.piece) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala index b5284c6..726398b 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala @@ -15,11 +15,13 @@ case class PlayerIdResponse( @Schema(description = "unique party ID. Required in responses", example = "abcdefgh") partyId: Option[String], @Schema(description = "job name", required = true, example = "DNC") job: String, @Schema(description = "player nick name", required = true, example = "Siuan Sanche") nick: String) { + def withPartyId(partyId: String): PlayerId = PlayerId(partyId, Job.withName(job), nick) } object PlayerIdResponse { + def fromPlayerId(playerId: PlayerId): PlayerIdResponse = PlayerIdResponse(Some(playerId.partyId), playerId.job.toString, playerId.nick) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala index 094df3f..84ac265 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala @@ -23,6 +23,7 @@ case class PlayerIdWithCountersResponse( @Schema(description = "total count of looted pieces", required = true) lootCountTotal: Int) object PlayerIdWithCountersResponse { + def fromPlayerId(playerIdWithCounters: PlayerIdWithCounters): PlayerIdWithCountersResponse = PlayerIdWithCountersResponse( playerIdWithCounters.partyId, diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala index 2225f09..b645c0b 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala @@ -19,6 +19,7 @@ case class PlayerResponse( @Schema(description = "looted pieces") loot: Option[Seq[LootResponse]], @Schema(description = "link to best in slot", example = "https://ffxiv.ariyala.com/19V5R") link: Option[String], @Schema(description = "player loot priority", `type` = "number") priority: Option[Int]) { + def toPlayer: Player = Player(-1, partyId, Job.withName(job), nick, BiS(bis.getOrElse(Seq.empty).map(_.toPiece)), @@ -27,6 +28,7 @@ case class PlayerResponse( } object PlayerResponse { + def fromPlayer(player: Player): PlayerResponse = PlayerResponse(player.partyId, player.job.toString, player.nick, Some(player.bis.pieces.map(PieceResponse.fromPiece)), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala index 05e37dd..b1c1abf 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala @@ -16,11 +16,13 @@ case class UserResponse( @Schema(description = "username to login to party", required = true, example = "siuan") username: String, @Schema(description = "password to login to party", required = true, example = "pa55w0rd") password: String, @Schema(description = "user permission", defaultValue = "get", allowableValues = Array("get", "post", "admin")) permission: Option[Permission.Value] = None) { + def toUser: User = User(partyId, username, password, permission.getOrElse(Permission.get)) } object UserResponse { + def fromUser(user: User): UserResponse = UserResponse(user.partyId, user.username, "", Some(user.permission)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala index 810e2aa..f6c9a79 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala @@ -8,16 +8,19 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import akka.util.Timeout import me.arcanis.ffxivbis.http.{Authorization, PlayerHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import scala.util.{Failure, Success} -class BasePartyView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class BasePartyView(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends PlayerHelper with Authorization { def route: Route = getIndex diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala index 87f740f..52d119c 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala @@ -8,18 +8,21 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import akka.util.Timeout import me.arcanis.ffxivbis.http.{Authorization, BiSHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import me.arcanis.ffxivbis.models.{Piece, PieceType, Player, PlayerId} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -class BiSView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class BiSView(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends BiSHelper with Authorization { def route: Route = getBiS ~ modifyBiS @@ -64,7 +67,7 @@ class BiSView(override val storage: ActorRef, override val ariyala: ActorRef)(im def getPiece(playerId: PlayerId, piece: String, pieceType: String) = Try(Piece(piece, PieceType.withName(pieceType), playerId.job)).toOption - def bisAction(playerId: PlayerId, piece: String, pieceType: String)(fn: Piece => Future[Int]) = + def bisAction(playerId: PlayerId, piece: String, pieceType: String)(fn: Piece => Future[Unit]) = getPiece(playerId, piece, pieceType) match { case Some(item) => fn(item).map(_ => ()) case _ => Future.failed(new Error(s"Could not construct piece from `$piece ($pieceType)`")) @@ -73,9 +76,9 @@ class BiSView(override val storage: ActorRef, override val ariyala: ActorRef)(im PlayerId(partyId, player) match { case Some(playerId) => (maybePiece, maybePieceType, action, maybeLink) match { case (Some(piece), Some(pieceType), "add", _) => - bisAction(playerId, piece, pieceType) { item => addPieceBiS(playerId, item) } + bisAction(playerId, piece, pieceType)(addPieceBiS(playerId, _)) case (Some(piece), Some(pieceType), "remove", _) => - bisAction(playerId, piece, pieceType) { item => removePieceBiS(playerId, item) } + bisAction(playerId, piece, pieceType)(removePieceBiS(playerId, _)) case (_, _, "create", Some(link)) => putBiS(playerId, link).map(_ => ()) case _ => Future.failed(new Error(s"Could not perform $action")) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala index 2057155..5ce1ff7 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala @@ -12,6 +12,7 @@ import scalatags.Text import scalatags.Text.all._ object ErrorView { + def template(error: Option[String]): Text.TypedTag[String] = error match { case Some(text) => p(id:="error", s"Error occurs: $text") case None => p("") diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala index 4a5393d..3edc9ee 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala @@ -12,6 +12,7 @@ import scalatags.Text import scalatags.Text.all._ object ExportToCSVView { + def template: Text.TypedTag[String] = div( button(onclick:="exportTableToCsv('result.csv')")("Export to CSV"), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala index 4997cef..8da73a0 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala @@ -8,18 +8,21 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server._ import akka.util.Timeout import me.arcanis.ffxivbis.http.{PlayerHelper, UserHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import me.arcanis.ffxivbis.models.{PartyDescription, Permission, User} import scala.concurrent.Future import scala.util.{Failure, Success} -class IndexView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class IndexView(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends PlayerHelper with UserHelper { def route: Route = createParty ~ getIndex diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala index 2b02da6..24359d1 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala @@ -8,18 +8,20 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout import me.arcanis.ffxivbis.http.{Authorization, LootHelper} +import me.arcanis.ffxivbis.messages.Message import me.arcanis.ffxivbis.models.{Job, Piece, PieceType, PlayerIdWithCounters} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success, Try} -class LootSuggestView(override val storage: ActorRef)(implicit timeout: Timeout) +class LootSuggestView(override val storage: ActorRef[Message]) + (implicit timeout: Timeout, scheduler: Scheduler) extends LootHelper with Authorization { def route: Route = getIndex ~ suggestLoot diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala index 24f7202..1fb5f5f 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala @@ -8,18 +8,20 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout import me.arcanis.ffxivbis.http.{Authorization, LootHelper} +import me.arcanis.ffxivbis.messages.Message import me.arcanis.ffxivbis.models.{Piece, PieceType, Player, PlayerId} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -class LootView (override val storage: ActorRef)(implicit timeout: Timeout) +class LootView(override val storage: ActorRef[Message]) + (implicit timeout: Timeout, scheduler: Scheduler) extends LootHelper with Authorization { def route: Route = getLoot ~ modifyLoot diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala index 932b582..4530f6c 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala @@ -8,17 +8,20 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout import me.arcanis.ffxivbis.http.{Authorization, PlayerHelper} +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import me.arcanis.ffxivbis.models._ import scala.concurrent.{ExecutionContext, Future} -class PlayerView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout) +class PlayerView(override val storage: ActorRef[Message], + override val provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) extends PlayerHelper with Authorization { def route: Route = getParty ~ modifyParty diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala index a631f38..dabaa0e 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala @@ -8,21 +8,24 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.{ContentTypes, HttpEntity} import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} -class RootView(storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout) { +class RootView(storage: ActorRef[Message], + provider: ActorRef[BiSProviderMessage]) + (implicit timeout: Timeout, scheduler: Scheduler) { - private val basePartyView = new BasePartyView(storage, ariyala) - private val indexView = new IndexView(storage, ariyala) + private val basePartyView = new BasePartyView(storage, provider) + private val indexView = new IndexView(storage, provider) - private val biSView = new BiSView(storage, ariyala) + private val biSView = new BiSView(storage, provider) private val lootView = new LootView(storage) private val lootSuggestView = new LootSuggestView(storage) - private val playerView = new PlayerView(storage, ariyala) + private val playerView = new PlayerView(storage, provider) private val userView = new UserView(storage) def route: Route = @@ -31,6 +34,7 @@ class RootView(storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout) } object RootView { + def toHtml(template: String): HttpEntity.Strict = HttpEntity(ContentTypes.`text/html(UTF-8)`, template) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala index efb3c91..9adffd0 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala @@ -12,6 +12,7 @@ import scalatags.Text import scalatags.Text.all._ object SearchLineView { + def template: Text.TypedTag[String] = div( input( diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala index 98bdbea..a9de452 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala @@ -8,18 +8,20 @@ */ package me.arcanis.ffxivbis.http.view -import akka.actor.ActorRef +import akka.actor.typed.{ActorRef, Scheduler} import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.server.Directives._ import akka.http.scaladsl.server.Route import akka.util.Timeout import me.arcanis.ffxivbis.http.{Authorization, UserHelper} +import me.arcanis.ffxivbis.messages.Message import me.arcanis.ffxivbis.models.{Permission, User} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -class UserView(override val storage: ActorRef)(implicit timeout: Timeout) +class UserView(override val storage: ActorRef[Message]) + (implicit timeout: Timeout, scheduler: Scheduler) extends UserHelper with Authorization { def route: Route = getUsers ~ modifyUsers diff --git a/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala b/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala new file mode 100644 index 0000000..36768cb --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala @@ -0,0 +1,8 @@ +package me.arcanis.ffxivbis.messages + +import akka.actor.typed.ActorRef +import me.arcanis.ffxivbis.models.{BiS, Job} + +sealed trait BiSProviderMessage + +case class DownloadBiS(link: String, job: Job.Job, replyTo: ActorRef[BiS]) extends BiSProviderMessage diff --git a/src/main/scala/me/arcanis/ffxivbis/messages/ContolMessage.scala b/src/main/scala/me/arcanis/ffxivbis/messages/ContolMessage.scala new file mode 100644 index 0000000..409d399 --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/messages/ContolMessage.scala @@ -0,0 +1,10 @@ +package me.arcanis.ffxivbis.messages + +import akka.actor.typed.ActorRef +import me.arcanis.ffxivbis.models.Party + +case class ForgetParty(partyId: String) extends Message + +case class GetNewPartyId(replyTo: ActorRef[String]) extends Message + +case class StoreParty(partyId: String, party: Party) extends Message diff --git a/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala b/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala new file mode 100644 index 0000000..3d4575b --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala @@ -0,0 +1,77 @@ +package me.arcanis.ffxivbis.messages + +import akka.actor.typed.{ActorRef, Behavior} +import me.arcanis.ffxivbis.models.{Party, PartyDescription, Piece, Player, PlayerId, User} +import me.arcanis.ffxivbis.service.LootSelector + +sealed trait DatabaseMessage extends Message { + + def partyId: String +} + +object DatabaseMessage { + + type Handler = PartialFunction[DatabaseMessage, Behavior[DatabaseMessage]] +} + +// bis handler +case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]]) extends DatabaseMessage + +case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +// loot handler +case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]]) extends DatabaseMessage + +case class RemovePieceFrom(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult]) extends DatabaseMessage + +// party handler +case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = player.partyId +} + +case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends DatabaseMessage + +case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends DatabaseMessage + +case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = playerId.partyId +} + +case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = partyDescription.partyId +} + +// user handler +case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends DatabaseMessage { + override def partyId: String = user.partyId +} + +case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends DatabaseMessage + +case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends DatabaseMessage + +case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends DatabaseMessage + +case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends DatabaseMessage \ No newline at end of file diff --git a/src/main/scala/me/arcanis/ffxivbis/messages/Message.scala b/src/main/scala/me/arcanis/ffxivbis/messages/Message.scala new file mode 100644 index 0000000..def973d --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/messages/Message.scala @@ -0,0 +1,9 @@ +package me.arcanis.ffxivbis.messages + +import akka.actor.typed.Behavior + +trait Message + +object Message { + type Handler = PartialFunction[Message, Behavior[Message]] +} diff --git a/src/main/scala/me/arcanis/ffxivbis/models/Job.scala b/src/main/scala/me/arcanis/ffxivbis/models/Job.scala index 415eb6f..f73f392 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/Job.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/Job.scala @@ -26,14 +26,15 @@ object Job { object BodyTanks extends LeftSide object BodyRanges extends LeftSide - sealed trait Job { + sealed trait Job extends Equals { def leftSide: LeftSide def rightSide: RightSide // conversion to string to avoid recursion + override def canEqual(that: Any): Boolean = that.isInstanceOf[Job] + override def equals(obj: Any): Boolean = { - def canEqual(obj: Any): Boolean = obj.isInstanceOf[Job] def equality(objRepr: String): Boolean = objRepr match { case _ if objRepr == AnyJob.toString => true case _ if this.toString == AnyJob.toString => true diff --git a/src/main/scala/me/arcanis/ffxivbis/service/Database.scala b/src/main/scala/me/arcanis/ffxivbis/service/Database.scala index 356cdab..354e2a4 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/Database.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/Database.scala @@ -8,21 +8,22 @@ */ package me.arcanis.ffxivbis.service -import akka.actor.Actor +import akka.actor.typed.Behavior +import akka.actor.typed.scaladsl.Behaviors +import com.typesafe.config.Config import com.typesafe.scalalogging.StrictLogging +import me.arcanis.ffxivbis.messages.{DatabaseMessage} import me.arcanis.ffxivbis.models.{Party, Player, PlayerId} +import me.arcanis.ffxivbis.service.impl.DatabaseImpl import me.arcanis.ffxivbis.storage.DatabaseProfile import scala.concurrent.{ExecutionContext, Future} -trait Database extends Actor with StrictLogging { - implicit def executionContext: ExecutionContext - def profile: DatabaseProfile +trait Database extends StrictLogging { - override def postStop(): Unit = { - profile.db.close() - super.postStop() - } + implicit def executionContext: ExecutionContext + def config: Config + def profile: DatabaseProfile def filterParty(party: Party, maybePlayerId: Option[PlayerId]): Seq[Player] = maybePlayerId match { @@ -36,11 +37,11 @@ trait Database extends Actor with StrictLogging { players <- profile.getParty(partyId) bis <- if (withBiS) profile.getPiecesBiS(partyId) else Future(Seq.empty) loot <- if (withLoot) profile.getPieces(partyId) else Future(Seq.empty) - } yield Party(partyDescription, context.system.settings.config, players, bis, loot) + } yield Party(partyDescription, config, players, bis, loot) } object Database { - trait DatabaseRequest { - def partyId: String - } + + def apply(): Behavior[DatabaseMessage] = + Behaviors.setup[DatabaseMessage](context => new DatabaseImpl(context)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/LootSelector.scala b/src/main/scala/me/arcanis/ffxivbis/service/LootSelector.scala index 4ed46f1..e77be60 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/LootSelector.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/LootSelector.scala @@ -21,6 +21,7 @@ class LootSelector(players: Seq[Player], piece: Piece, orderBy: Seq[String]) { } object LootSelector { + def apply(players: Seq[Player], piece: Piece, orderBy: Seq[String]): LootSelectorResult = new LootSelector(players, piece, orderBy).suggest diff --git a/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala b/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala index 0cfa41e..58e8bb0 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala @@ -8,57 +8,65 @@ */ package me.arcanis.ffxivbis.service -import akka.actor.{Actor, ActorRef, Props} -import akka.pattern.{ask, pipe} +import akka.actor.typed.scaladsl.AskPattern.Askable +import akka.actor.typed.{ActorRef, Behavior, DispatcherSelector, Scheduler} +import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors} import akka.util.Timeout import com.typesafe.scalalogging.StrictLogging +import me.arcanis.ffxivbis.messages.{DatabaseMessage, Exists, ForgetParty, GetNewPartyId, GetParty, Message, StoreParty} import me.arcanis.ffxivbis.models.Party import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ExecutionContext, Future} -class PartyService(storage: ActorRef) extends Actor with StrictLogging { - import PartyService._ +class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMessage]) + extends AbstractBehavior[Message](context) with StrictLogging { import me.arcanis.ffxivbis.utils.Implicits._ private val cacheTimeout: FiniteDuration = context.system.settings.config.getDuration("me.arcanis.ffxivbis.settings.cache-timeout") - implicit private val executionContext: ExecutionContext = - context.system.dispatchers.lookup("me.arcanis.ffxivbis.default-dispatcher") + implicit private val executionContext: ExecutionContext = { + val selector = DispatcherSelector.fromConfig("me.arcanis.ffxivbis.default-dispatcher") + context.system.dispatchers.lookup(selector) + } implicit private val timeout: Timeout = context.system.settings.config.getDuration("me.arcanis.ffxivbis.settings.request-timeout") + implicit private val scheduler: Scheduler = context.system.scheduler - override def receive: Receive = handle(Map.empty) + override def onMessage(msg: Message): Behavior[Message] = handle(Map.empty)(msg) - private def handle(cache: Map[String, Party]): Receive = { + private def handle(cache: Map[String, Party]): Message.Handler = { case ForgetParty(partyId) => - context become handle(cache - partyId) + Behaviors.receiveMessage(handle(cache - partyId)) - case GetNewPartyId => - val client = sender() - getPartyId.pipeTo(client) + case GetNewPartyId(client) => + getPartyId.foreach(client ! _) + Behaviors.same - case req @ impl.DatabasePartyHandler.GetParty(partyId) => - val client = sender() + case StoreParty(partyId, party) => + Behaviors.receiveMessage(handle(cache.updated(partyId, party))) + + case GetParty(partyId, client) => val party = cache.get(partyId) match { case Some(party) => Future.successful(party) case None => - (storage ? req).mapTo[Party].map { party => - context become handle(cache + (partyId -> party)) - context.system.scheduler.scheduleOnce(cacheTimeout, self, ForgetParty(partyId)) + storage.ask(ref => GetParty(partyId, ref)).map { party => + context.self ! StoreParty(partyId, party) + context.system.scheduler.scheduleOnce(cacheTimeout, () => context.self ! ForgetParty(partyId)) party } } - party.pipeTo(client) + party.foreach(client ! _) + Behaviors.same - case req: Database.DatabaseRequest => - self ! ForgetParty(req.partyId) - storage.forward(req) + case req: DatabaseMessage => + storage ! req + Behaviors.receiveMessage(handle(cache - req.partyId)) } private def getPartyId: Future[String] = { val partyId = Party.randomPartyId - (storage ? impl.DatabaseUserHandler.Exists(partyId)).mapTo[Boolean].flatMap { + storage.ask(ref => Exists(partyId, ref)).flatMap { case true => getPartyId case false => Future.successful(partyId) } @@ -66,8 +74,7 @@ class PartyService(storage: ActorRef) extends Actor with StrictLogging { } object PartyService { - def props(storage: ActorRef): Props = Props(new PartyService(storage)) - case class ForgetParty(partyId: String) - case object GetNewPartyId + def apply(storage: ActorRef[DatabaseMessage]): Behavior[Message] = + Behaviors.setup[Message](context => new PartyService(context, storage)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/RateLimiter.scala b/src/main/scala/me/arcanis/ffxivbis/service/RateLimiter.scala deleted file mode 100644 index 48525ee..0000000 --- a/src/main/scala/me/arcanis/ffxivbis/service/RateLimiter.scala +++ /dev/null @@ -1,47 +0,0 @@ -package me.arcanis.ffxivbis.service - -import java.time.Instant - -import akka.actor.Actor - -import scala.concurrent.ExecutionContext -import scala.concurrent.duration.FiniteDuration - -class RateLimiter extends Actor { - import RateLimiter._ - import me.arcanis.ffxivbis.utils.Implicits._ - - implicit private val executionContext: ExecutionContext = context.system.dispatcher - - private val maxRequestCount: Int = context.system.settings.config.getInt("me.arcanis.ffxivbis.web.limits.max-count") - private val requestInterval: FiniteDuration = context.system.settings.config.getDuration("me.arcanis.ffxivbis.web.limits.interval") - - override def receive: Receive = handle(Map.empty) - - private def handle(cache: Map[String, Usage]): Receive = { - case username: String => - val client = sender() - val usage = if (cache.contains(username)) { - cache(username) - } else { - context.system.scheduler.scheduleOnce(requestInterval, self, Reset(username)) - Usage() - } - context become handle(cache + (username -> usage.increment)) - - val response = if (usage.count > maxRequestCount) Some(usage.left) else None - client ! response - - case Reset(username) => - context become handle(cache - username) - } -} - -object RateLimiter { - private case class Usage(count: Int = 0, since: Instant = Instant.now) { - def increment: Usage = copy(count = count + 1) - def left: Long = (Instant.now.toEpochMilli - since.toEpochMilli) / 1000 - } - - case class Reset(username: String) -} \ No newline at end of file diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala index 4d23319..0a0fdaf 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala @@ -10,26 +10,33 @@ package me.arcanis.ffxivbis.service.bis import java.nio.file.Paths -import akka.actor.{Actor, Props} +import akka.actor.ClassicActorSystemProvider +import akka.actor.typed.{Behavior, PostStop, Signal} +import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors} import akka.http.scaladsl.model._ -import akka.pattern.pipe import com.typesafe.scalalogging.StrictLogging +import me.arcanis.ffxivbis.messages.{BiSProviderMessage, DownloadBiS} import me.arcanis.ffxivbis.models.{BiS, Job, Piece, PieceType} import spray.json._ import scala.concurrent.{ExecutionContext, Future} -class BisProvider extends Actor with XivApi with StrictLogging { +class BisProvider(context: ActorContext[BiSProviderMessage]) + extends AbstractBehavior[BiSProviderMessage](context) with XivApi with StrictLogging { - override def receive: Receive = { - case BisProvider.GetBiS(link, job) => - val client = sender() - get(link, job).map(BiS(_)).pipeTo(client) - } + override def system: ClassicActorSystemProvider = context.system - override def postStop(): Unit = { - shutdown() - super.postStop() + override def onMessage(msg: BiSProviderMessage): Behavior[BiSProviderMessage] = + msg match { + case DownloadBiS(link, job, client) => + get(link, job).map(BiS(_)).foreach(client ! _) + Behaviors.same + } + + override def onSignal: PartialFunction[Signal, Behavior[BiSProviderMessage]] = { + case PostStop => + shutdown() + Behaviors.same } private def get(link: String, job: Job.Job): Future[Seq[Piece]] = { @@ -49,9 +56,8 @@ class BisProvider extends Actor with XivApi with StrictLogging { object BisProvider { - def props: Props = Props(new BisProvider) - - case class GetBiS(link: String, job: Job.Job) + def apply(): Behavior[BiSProviderMessage] = + Behaviors.setup[BiSProviderMessage](context => new BisProvider(context)) private def parseBisJsonToPieces(job: Job.Job, idParser: (Job.Job, JsObject) => Future[Map[String, Long]], diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala index ccf0cc5..eb6be3c 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala @@ -8,11 +8,11 @@ */ package me.arcanis.ffxivbis.service.bis -import akka.actor.ActorContext +import akka.actor.ClassicActorSystemProvider import akka.http.scaladsl.Http import akka.http.scaladsl.model.headers.Location import akka.http.scaladsl.model.{HttpRequest, HttpResponse, Uri} -import akka.stream.ActorMaterializer +import akka.stream.Materializer import akka.stream.scaladsl.{Keep, Sink} import akka.util.ByteString import spray.json._ @@ -21,12 +21,12 @@ import scala.concurrent.{ExecutionContext, Future} trait RequestExecutor { - def context: ActorContext + def system: ClassicActorSystemProvider - private val http = Http()(context.system) - implicit val materializer: ActorMaterializer = ActorMaterializer()(context) + private val http = Http()(system) + implicit val materializer: Materializer = Materializer.createMaterializer(system) implicit val executionContext: ExecutionContext = - context.system.dispatchers.lookup("me.arcanis.ffxivbis.default-dispatcher") + system.classicSystem.dispatchers.lookup("me.arcanis.ffxivbis.default-dispatcher") def sendRequest[T](uri: Uri, parser: JsObject => Future[T]): Future[T] = http.singleRequest(HttpRequest(uri = uri)).map { diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala index 4a1332a..ce17342 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala @@ -17,7 +17,7 @@ import scala.util.Try trait XivApi extends RequestExecutor { - private val config = context.system.settings.config + private val config = system.classicSystem.settings.config private val xivapiUrl = config.getString("me.arcanis.ffxivbis.bis-provider.xivapi-url") private val xivapiKey = Try(config.getString("me.arcanis.ffxivbis.bis-provider.xivapi-key")).toOption diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala index 50a6e36..d134f21 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala @@ -8,43 +8,30 @@ */ package me.arcanis.ffxivbis.service.impl -import akka.pattern.pipe -import me.arcanis.ffxivbis.models.{Piece, PlayerId} +import akka.actor.typed.scaladsl.Behaviors +import me.arcanis.ffxivbis.messages.{AddPieceToBis, DatabaseMessage, GetBiS, RemovePieceFromBiS, RemovePiecesFromBiS} import me.arcanis.ffxivbis.service.Database trait DatabaseBiSHandler { this: Database => - import DatabaseBiSHandler._ - def bisHandler: Receive = { - case AddPieceToBis(playerId, piece) => - val client = sender() - profile.insertPieceBiS(playerId, piece).pipeTo(client) + def bisHandler: DatabaseMessage.Handler = { + case AddPieceToBis(playerId, piece, client) => + profile.insertPieceBiS(playerId, piece).foreach(_ => client ! ()) + Behaviors.same - case GetBiS(partyId, maybePlayerId) => - val client = sender() + case GetBiS(partyId, maybePlayerId, client) => getParty(partyId, withBiS = true, withLoot = false) .map(filterParty(_, maybePlayerId)) - .pipeTo(client) + .foreach(client ! _) + Behaviors.same - case RemovePieceFromBiS(playerId, piece) => - val client = sender() - profile.deletePieceBiS(playerId, piece).pipeTo(client) + case RemovePieceFromBiS(playerId, piece, client) => + profile.deletePieceBiS(playerId, piece).foreach(_ => client ! ()) + Behaviors.same - case RemovePiecesFromBiS(playerId) => - val client = sender() - profile.deletePiecesBiS(playerId).pipeTo(client) + case RemovePiecesFromBiS(playerId, client) => + profile.deletePiecesBiS(playerId).foreach(_ => client ! ()) + Behaviors.same } } -object DatabaseBiSHandler { - case class AddPieceToBis(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } - case class GetBiS(partyId: String, playerId: Option[PlayerId]) extends Database.DatabaseRequest - case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } - case class RemovePiecesFromBiS(playerId: PlayerId) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } -} diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala index 74e4e55..f7999b8 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala @@ -8,24 +8,29 @@ */ package me.arcanis.ffxivbis.service.impl -import akka.actor.Props +import akka.actor.typed.{Behavior, DispatcherSelector} +import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext} +import com.typesafe.config.Config +import me.arcanis.ffxivbis.messages.DatabaseMessage import me.arcanis.ffxivbis.service.Database import me.arcanis.ffxivbis.storage.DatabaseProfile import scala.concurrent.ExecutionContext -class DatabaseImpl extends Database +class DatabaseImpl(context: ActorContext[DatabaseMessage]) + extends AbstractBehavior[DatabaseMessage](context) with Database with DatabaseBiSHandler with DatabaseLootHandler with DatabasePartyHandler with DatabaseUserHandler { - implicit val executionContext: ExecutionContext = - context.system.dispatchers.lookup("me.arcanis.ffxivbis.default-dispatcher") - val profile = new DatabaseProfile(executionContext, context.system.settings.config) + override implicit val executionContext: ExecutionContext = { + val selector = DispatcherSelector.fromConfig("me.arcanis.ffxivbis.default-dispatcher") + context.system.dispatchers.lookup(selector) + } + override val config: Config = context.system.settings.config + override val profile: DatabaseProfile = new DatabaseProfile(executionContext, config) - override def receive: Receive = + override def onMessage(msg: DatabaseMessage): Behavior[DatabaseMessage] = handle(msg) + + private def handle: DatabaseMessage.Handler = bisHandler orElse lootHandler orElse partyHandler orElse userHandler } - -object DatabaseImpl { - def props: Props = Props(new DatabaseImpl) -} diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala index 8251375..c90e0f4 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala @@ -10,42 +10,33 @@ package me.arcanis.ffxivbis.service.impl import java.time.Instant -import akka.pattern.pipe -import me.arcanis.ffxivbis.models.{Loot, Piece, PlayerId} +import akka.actor.typed.scaladsl.Behaviors +import me.arcanis.ffxivbis.messages.{AddPieceTo, DatabaseMessage, GetLoot, RemovePieceFrom, SuggestLoot} +import me.arcanis.ffxivbis.models.Loot import me.arcanis.ffxivbis.service.Database trait DatabaseLootHandler { this: Database => - import DatabaseLootHandler._ - def lootHandler: Receive = { - case AddPieceTo(playerId, piece, isFreeLoot) => - val client = sender() + def lootHandler: DatabaseMessage.Handler = { + case AddPieceTo(playerId, piece, isFreeLoot, client) => val loot = Loot(-1, piece, Instant.now, isFreeLoot) - profile.insertPiece(playerId, loot).pipeTo(client) + profile.insertPiece(playerId, loot).foreach(_ => client ! ()) + Behaviors.same - case GetLoot(partyId, maybePlayerId) => - val client = sender() + case GetLoot(partyId, maybePlayerId, client) => getParty(partyId, withBiS = false, withLoot = true) .map(filterParty(_, maybePlayerId)) - .pipeTo(client) + .foreach(client ! _) + Behaviors.same - case RemovePieceFrom(playerId, piece) => - val client = sender() - profile.deletePiece(playerId, piece).pipeTo(client) + case RemovePieceFrom(playerId, piece, client) => + profile.deletePiece(playerId, piece).foreach(_ => client ! ()) + Behaviors.same - case SuggestLoot(partyId, piece) => - val client = sender() - getParty(partyId, withBiS = true, withLoot = true).map(_.suggestLoot(piece)).pipeTo(client) + case SuggestLoot(partyId, piece, client) => + getParty(partyId, withBiS = true, withLoot = true) + .map(_.suggestLoot(piece)) + .foreach(client ! _) + Behaviors.same } } - -object DatabaseLootHandler { - case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } - case class GetLoot(partyId: String, playerId: Option[PlayerId]) extends Database.DatabaseRequest - case class RemovePieceFrom(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } - case class SuggestLoot(partyId: String, piece: Piece) extends Database.DatabaseRequest -} diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala index bc13fd6..afd47d6 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala @@ -8,30 +8,29 @@ */ package me.arcanis.ffxivbis.service.impl -import akka.pattern.pipe -import me.arcanis.ffxivbis.models.{BiS, PartyDescription, Player, PlayerId} +import akka.actor.typed.scaladsl.Behaviors +import me.arcanis.ffxivbis.messages.{AddPlayer, DatabaseMessage, GetParty, GetPartyDescription, GetPlayer, RemovePlayer, UpdateParty} +import me.arcanis.ffxivbis.models.{BiS, Player} import me.arcanis.ffxivbis.service.Database import scala.concurrent.Future trait DatabasePartyHandler { this: Database => - import DatabasePartyHandler._ - def partyHandler: Receive = { - case AddPlayer(player) => - val client = sender() - profile.insertPlayer(player).pipeTo(client) + def partyHandler: DatabaseMessage.Handler = { + case AddPlayer(player, client) => + profile.insertPlayer(player).foreach(_ => client ! _) + Behaviors.same - case GetParty(partyId) => - val client = sender() - getParty(partyId, withBiS = true, withLoot = true).pipeTo(client) + case GetParty(partyId, client) => + getParty(partyId, withBiS = true, withLoot = true).foreach(client ! _) + Behaviors.same - case GetPartyDescription(partyId) => - val client = sender() - profile.getPartyDescription(partyId).pipeTo(client) + case GetPartyDescription(partyId, client) => + profile.getPartyDescription(partyId).foreach(client ! _) + Behaviors.same - case GetPlayer(playerId) => - val client = sender() + case GetPlayer(playerId, client) => val player = profile.getPlayerFull(playerId).flatMap { maybePlayerData => Future.traverse(maybePlayerData.toSeq) { playerData => for { @@ -42,31 +41,15 @@ trait DatabasePartyHandler { this: Database => playerData.link, playerData.priority) } }.map(_.headOption) - player.pipeTo(client) + player.foreach(client ! _) + Behaviors.same - case RemovePlayer(playerId) => - val client = sender() - profile.deletePlayer(playerId).pipeTo(client) + case RemovePlayer(playerId, client) => + profile.deletePlayer(playerId).foreach(_ => client ! ()) + Behaviors.same - case UpdateParty(description) => - val client = sender() - profile.insertPartyDescription(description).pipeTo(client) - } -} - -object DatabasePartyHandler { - case class AddPlayer(player: Player) extends Database.DatabaseRequest { - override def partyId: String = player.partyId - } - case class GetParty(partyId: String) extends Database.DatabaseRequest - case class GetPartyDescription(partyId: String) extends Database.DatabaseRequest - case class GetPlayer(playerId: PlayerId) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } - case class RemovePlayer(playerId: PlayerId) extends Database.DatabaseRequest { - override def partyId: String = playerId.partyId - } - case class UpdateParty(partyDescription: PartyDescription) extends Database.DatabaseRequest { - override def partyId: String = partyDescription.partyId + case UpdateParty(description, client) => + profile.insertPartyDescription(description).foreach(_ => client ! ()) + Behaviors.same } } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala index ddb62b0..d186225 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala @@ -8,43 +8,32 @@ */ package me.arcanis.ffxivbis.service.impl -import akka.pattern.pipe -import me.arcanis.ffxivbis.models.User +import akka.actor.typed.scaladsl.Behaviors +import me.arcanis.ffxivbis.messages.{AddUser, DatabaseMessage, DeleteUser, Exists, GetUser, GetUsers} import me.arcanis.ffxivbis.service.Database trait DatabaseUserHandler { this: Database => - import DatabaseUserHandler._ - def userHandler: Receive = { - case AddUser(user, isHashedPassword) => - val client = sender() + def userHandler: DatabaseMessage.Handler = { + case AddUser(user, isHashedPassword, client) => val toInsert = if (isHashedPassword) user else user.withHashedPassword - profile.insertUser(toInsert).pipeTo(client) + profile.insertUser(toInsert).foreach(_ => client ! ()) + Behaviors.same - case DeleteUser(partyId, username) => - val client = sender() - profile.deleteUser(partyId, username).pipeTo(client) + case DeleteUser(partyId, username, client) => + profile.deleteUser(partyId, username).foreach(_ => client ! ()) + Behaviors.same - case Exists(partyId) => - val client = sender() - profile.exists(partyId).pipeTo(client) + case Exists(partyId, client) => + profile.exists(partyId).foreach(client ! _) + Behaviors.same - case GetUser(partyId, username) => - val client = sender() - profile.getUser(partyId, username).pipeTo(client) + case GetUser(partyId, username, client) => + profile.getUser(partyId, username).foreach(client ! _) + Behaviors.same - case GetUsers(partyId) => - val client = sender() - profile.getUsers(partyId).pipeTo(client) + case GetUsers(partyId, client) => + profile.getUsers(partyId).foreach(client ! _) + Behaviors.same } } - -object DatabaseUserHandler { - case class AddUser(user: User, isHashedPassword: Boolean) extends Database.DatabaseRequest { - override def partyId: String = user.partyId - } - case class DeleteUser(partyId: String, username: String) extends Database.DatabaseRequest - case class Exists(partyId: String) extends Database.DatabaseRequest - case class GetUser(partyId: String, username: String) extends Database.DatabaseRequest - case class GetUsers(partyId: String) extends Database.DatabaseRequest -} diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala index 45658ef..5b6a6a0 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala @@ -68,6 +68,7 @@ class DatabaseProfile(context: ExecutionContext, config: Config) } object DatabaseProfile { + def now: Long = Instant.now.toEpochMilli def getSection(config: Config): Config = { val section = config.getString("me.arcanis.ffxivbis.database.mode") diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala b/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala index 25af3d9..eb8a9c3 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala @@ -15,6 +15,7 @@ import org.flywaydb.core.api.configuration.ClassicConfiguration import scala.concurrent.Future class Migration(config: Config) { + def performMigration(): Future[Int] = { val section = DatabaseProfile.getSection(config) @@ -37,5 +38,6 @@ class Migration(config: Config) { } object Migration { + def apply(config: Config): Future[Int] = new Migration(config).performMigration() } diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala index e63a678..05b2960 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala @@ -1,52 +1,55 @@ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable import akka.http.scaladsl.model.{StatusCodes, Uri} import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials} import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} -import akka.http.scaladsl.server._ -import akka.pattern.ask import akka.testkit.TestKit import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ -import me.arcanis.ffxivbis.models.{BiS, Body, Job, PieceType} +import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser} +import me.arcanis.ffxivbis.models.{BiS, Job} import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.{PartyService, impl} +import me.arcanis.ffxivbis.service.{Database, PartyService} import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class BiSEndpointTest extends WordSpec - with Matchers with ScalatestRouteTest with JsonSupport { +class BiSEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport { - private val auth: Authorization = + private val testKit = ActorTestKit(Settings.withRandomDatabase) + override val testConfig: Config = testKit.system.settings.config + + private val auth = Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword)) - private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}/bis") + private val endpoint = Uri(s"/party/${Fixtures.partyId}/bis") private val playerId = PlayerIdResponse.fromPlayerId(Fixtures.playerEmpty.playerId) - private val timeout: FiniteDuration = 60 seconds - implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout) + private val askTimeout = 60 seconds + implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(askTimeout) - private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props) - private val provider: ActorRef = system.actorOf(BisProvider.props) - private val party: ActorRef = system.actorOf(PartyService.props(storage)) - private val route: Route = new BiSEndpoint(party, provider)(timeout).route - - override def testConfig: Config = Settings.withRandomDatabase + private val storage = testKit.spawn(Database()) + private val provider = testKit.spawn(BisProvider()) + private val party = testKit.spawn(PartyService(storage)) + private val route = new BiSEndpoint(party, provider)(askTimeout, testKit.scheduler).route override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) - Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout) - Await.result((storage ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout) + Await.result(Migration(testConfig), askTimeout) + Await.result(storage.ask(AddUser(Fixtures.userAdmin, isHashedPassword = true, _))(askTimeout, testKit.scheduler), askTimeout) + Await.result(storage.ask(AddPlayer(Fixtures.playerEmpty, _))(askTimeout, testKit.scheduler), askTimeout) } override def afterAll: Unit = { + super.afterAll() + Settings.clearDatabase(testConfig) TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + testKit.shutdownTestKit() } private def compareBiSResponse(actual: PlayerResponse, expected: PlayerResponse): Unit = { diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala index 861b949..891e82c 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala @@ -2,49 +2,52 @@ package me.arcanis.ffxivbis.http.api.v1 import java.time.Instant -import akka.actor.ActorRef +import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable import akka.http.scaladsl.model.{StatusCodes, Uri} import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials} import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} -import akka.http.scaladsl.server._ -import akka.pattern.ask import akka.testkit.TestKit import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ -import me.arcanis.ffxivbis.service.{PartyService, impl} +import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser} +import me.arcanis.ffxivbis.service.{Database, PartyService} import me.arcanis.ffxivbis.storage.Migration -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class LootEndpointTest extends WordSpec - with Matchers with ScalatestRouteTest with JsonSupport { +class LootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport { - private val auth: Authorization = + private val testKit = ActorTestKit(Settings.withRandomDatabase) + override val testConfig: Config = testKit.system.settings.config + + private val auth = Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword)) - private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}/loot") + private val endpoint = Uri(s"/party/${Fixtures.partyId}/loot") private val playerId = PlayerIdResponse.fromPlayerId(Fixtures.playerEmpty.playerId) - private val timeout: FiniteDuration = 60 seconds - implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout) + private val askTimeout = 60 seconds + implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(askTimeout) - private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props) - private val party: ActorRef = system.actorOf(PartyService.props(storage)) - private val route: Route = new LootEndpoint(party)(timeout).route - - override def testConfig: Config = Settings.withRandomDatabase + private val storage = testKit.spawn(Database()) + private val party = testKit.spawn(PartyService(storage)) + private val route = new LootEndpoint(party)(askTimeout, testKit.scheduler).route override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) - Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout) - Await.result((storage ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout) + Await.result(Migration(testConfig), askTimeout) + Await.result(storage.ask(AddUser(Fixtures.userAdmin, isHashedPassword = true, _))(askTimeout, testKit.scheduler), askTimeout) + Await.result(storage.ask(AddPlayer(Fixtures.playerEmpty, _))(askTimeout, testKit.scheduler), askTimeout) } override def afterAll: Unit = { + super.afterAll() + Settings.clearDatabase(testConfig) TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + testKit.shutdownTestKit() } "api v1 loot endpoint" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala index 74b5e17..c08a11f 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala @@ -1,48 +1,52 @@ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable import akka.http.scaladsl.model.{StatusCodes, Uri} import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials} import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} -import akka.http.scaladsl.server._ import akka.testkit.TestKit -import akka.pattern.ask import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ +import me.arcanis.ffxivbis.messages.AddUser import me.arcanis.ffxivbis.models.PartyDescription import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.impl +import me.arcanis.ffxivbis.service.{Database, PartyService} import me.arcanis.ffxivbis.storage.Migration -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class PartyEndpointTest extends WordSpec - with Matchers with ScalatestRouteTest with JsonSupport { +class PartyEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport { - private val auth: Authorization = + private val testKit = ActorTestKit(Settings.withRandomDatabase) + override val testConfig: Config = testKit.system.settings.config + + private val auth = Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword)) - private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}/description") - private val timeout: FiniteDuration = 60 seconds - implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout) + private val endpoint = Uri(s"/party/${Fixtures.partyId}/description") + private val askTimeout = 60 seconds + implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(askTimeout) - private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props) - private val provider: ActorRef = system.actorOf(BisProvider.props) - private val route: Route = new PartyEndpoint(storage, provider)(timeout).route - - override def testConfig: Config = Settings.withRandomDatabase + private val storage = testKit.spawn(Database()) + private val provider = testKit.spawn(BisProvider()) + private val party = testKit.spawn(PartyService(storage)) + private val route = new PartyEndpoint(party, provider)(askTimeout, testKit.scheduler).route override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) - Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout) + Await.result(Migration(testConfig), askTimeout) + Await.result(storage.ask(AddUser(Fixtures.userAdmin, isHashedPassword = true, _))(askTimeout, testKit.scheduler), askTimeout) } override def afterAll: Unit = { + super.afterAll() + Settings.clearDatabase(testConfig) TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + testKit.shutdownTestKit() } "api v1 party endpoint" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala index 6d61339..5e998e9 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala @@ -1,56 +1,57 @@ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable import akka.http.scaladsl.model.{StatusCodes, Uri} import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials} import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} -import akka.http.scaladsl.server._ -import akka.pattern.ask import akka.testkit.TestKit import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ +import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser} import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.{PartyService, impl} +import me.arcanis.ffxivbis.service.{Database, PartyService} import me.arcanis.ffxivbis.storage.Migration -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class PlayerEndpointTest extends WordSpec - with Matchers with ScalatestRouteTest with JsonSupport { +class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport { - private val auth: Authorization = + private val testKit = ActorTestKit(Settings.withRandomDatabase) + override val testConfig: Config = testKit.system.settings.config + + private val auth = Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword)) - private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}") - private val playerId = PlayerIdResponse.fromPlayerId(Fixtures.playerEmpty.playerId) - private val timeout: FiniteDuration = 60 seconds - implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout) + private val endpoint = Uri(s"/party/${Fixtures.partyId}") + private val askTimeout = 60 seconds + implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(askTimeout) - private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props) - private val provider: ActorRef = system.actorOf(BisProvider.props) - private val party: ActorRef = system.actorOf(PartyService.props(storage)) - private val route: Route = new PlayerEndpoint(party, provider)(timeout).route - - override def testConfig: Config = Settings.withRandomDatabase + private val storage = testKit.spawn(Database()) + private val provider = testKit.spawn(BisProvider()) + private val party = testKit.spawn(PartyService(storage)) + private val route = new PlayerEndpoint(party, provider)(askTimeout, testKit.scheduler).route override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) - Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout) - Await.result((storage ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout) + Await.result(Migration(testConfig), askTimeout) + Await.result(storage.ask(AddUser(Fixtures.userAdmin, isHashedPassword = true, _))(askTimeout, testKit.scheduler), askTimeout) + Await.result(storage.ask(AddPlayer(Fixtures.playerEmpty, _))(askTimeout, testKit.scheduler), askTimeout) } override def afterAll: Unit = { + super.afterAll() + Settings.clearDatabase(testConfig) TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + testKit.shutdownTestKit() } "api v1 player endpoint" must { "get users" in { - val uri = endpoint.withQuery(Uri.Query(Map("nick" -> playerId.nick, "job" -> playerId.job))) val response = Seq(PlayerResponse.fromPlayer(Fixtures.playerEmpty)) Get(endpoint).withHeaders(auth) ~> route ~> check { diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpointTest.scala index 8af3ce1..05a1720 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpointTest.scala @@ -2,21 +2,21 @@ package me.arcanis.ffxivbis.http.api.v1 import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.testkit.ScalatestRouteTest -import akka.http.scaladsl.server._ import com.typesafe.config.Config import me.arcanis.ffxivbis.Settings import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.models.{Job, Party, Permission, Piece, PieceType} -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.language.postfixOps -class TypesEndpointTest extends WordSpec +class TypesEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport { - private val route: Route = new TypesEndpoint(testConfig).route + override val testConfig: Config = Settings.withRandomDatabase - override def testConfig: Config = Settings.withRandomDatabase + private val route = new TypesEndpoint(testConfig).route "api v1 types endpoint" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala index 3b99653..fc0c1f9 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala @@ -1,45 +1,47 @@ package me.arcanis.ffxivbis.http.api.v1 -import akka.actor.ActorRef +import akka.actor.testkit.typed.scaladsl.ActorTestKit import akka.http.scaladsl.model.{StatusCodes, Uri} import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials} import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest} -import akka.http.scaladsl.server._ import akka.testkit.TestKit import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ -import me.arcanis.ffxivbis.service.{PartyService, impl} +import me.arcanis.ffxivbis.service.{Database, PartyService} import me.arcanis.ffxivbis.storage.Migration -import org.scalatest.{Matchers, WordSpec} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class UserEndpointTest extends WordSpec - with Matchers with ScalatestRouteTest with JsonSupport { +class UserEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport { - private val auth: Authorization = + private val testKit = ActorTestKit(Settings.withRandomDatabase) + override val testConfig: Config = testKit.system.settings.config + + private val auth = Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword)) - private def endpoint: Uri = Uri(s"/party/$partyId/users") - private val timeout: FiniteDuration = 60 seconds - implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout) + private def endpoint = Uri(s"/party/$partyId/users") + private val askTimeout = 60 seconds + implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(askTimeout) - private var partyId: String = Fixtures.partyId - private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props) - private val party: ActorRef = system.actorOf(PartyService.props(storage)) - private val route: Route = new UserEndpoint(party)(timeout).route - - override def testConfig: Config = Settings.withRandomDatabase + private var partyId = Fixtures.partyId + private val storage = testKit.spawn(Database()) + private val party = testKit.spawn(PartyService(storage)) + private val route = new UserEndpoint(party)(askTimeout, testKit.scheduler).route override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) + Await.result(Migration(testConfig), askTimeout) } override def afterAll: Unit = { + super.afterAll() + Settings.clearDatabase(testConfig) TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + testKit.shutdownTestKit() } "api v1 users endpoint" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/models/BiSTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/BiSTest.scala index ef03846..864bc90 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/BiSTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/BiSTest.scala @@ -2,9 +2,10 @@ package me.arcanis.ffxivbis.models import me.arcanis.ffxivbis.Fixtures import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class BiSTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class BiSTest extends AnyWordSpecLike with Matchers { "bis model" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/models/JobTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/JobTest.scala index da5c2ef..87d5baa 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/JobTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/JobTest.scala @@ -1,8 +1,9 @@ package me.arcanis.ffxivbis.models -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class JobTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class JobTest extends AnyWordSpecLike with Matchers { "job model" must { @@ -22,7 +23,7 @@ class JobTest extends WordSpecLike with Matchers with BeforeAndAfterAll { "equal AnyJob to others" in { Job.available.foreach { job => - Job.AnyJob shouldBe job + Job.AnyJob shouldEqual job } } diff --git a/src/test/scala/me/arcanis/ffxivbis/models/PartyTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/PartyTest.scala index 88f6d5a..e50a5e8 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/PartyTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/PartyTest.scala @@ -2,9 +2,10 @@ package me.arcanis.ffxivbis.models import me.arcanis.ffxivbis.Fixtures import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class PartyTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class PartyTest extends AnyWordSpecLike with Matchers { private val partyDescription = PartyDescription.empty(Fixtures.partyId) private val party = diff --git a/src/test/scala/me/arcanis/ffxivbis/models/PieceTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/PieceTest.scala index 70b4535..0cae8e2 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/PieceTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/PieceTest.scala @@ -1,9 +1,10 @@ package me.arcanis.ffxivbis.models import me.arcanis.ffxivbis.Fixtures -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class PieceTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class PieceTest extends AnyWordSpecLike with Matchers { "piece model" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/models/PieceTypeTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/PieceTypeTest.scala index dc8efef..595aeee 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/PieceTypeTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/PieceTypeTest.scala @@ -1,8 +1,9 @@ package me.arcanis.ffxivbis.models -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class PieceTypeTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class PieceTypeTest extends AnyWordSpecLike with Matchers { "piece type model" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/models/PlayerIdTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/PlayerIdTest.scala index ba9eff7..d8c6f6f 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/PlayerIdTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/PlayerIdTest.scala @@ -1,9 +1,10 @@ package me.arcanis.ffxivbis.models import me.arcanis.ffxivbis.Fixtures -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class PlayerIdTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class PlayerIdTest extends AnyWordSpecLike with Matchers { "player id model" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/models/PlayerTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/PlayerTest.scala index 5e483c1..05e3d17 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/PlayerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/PlayerTest.scala @@ -1,9 +1,10 @@ package me.arcanis.ffxivbis.models import me.arcanis.ffxivbis.Fixtures -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class PlayerTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class PlayerTest extends AnyWordSpecLike with Matchers { "player model" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/models/UserTest.scala b/src/test/scala/me/arcanis/ffxivbis/models/UserTest.scala index b188446..1969388 100644 --- a/src/test/scala/me/arcanis/ffxivbis/models/UserTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/models/UserTest.scala @@ -1,9 +1,10 @@ package me.arcanis.ffxivbis.models import me.arcanis.ffxivbis.Fixtures -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike -class UserTest extends WordSpecLike with Matchers with BeforeAndAfterAll { +class UserTest extends AnyWordSpecLike with Matchers { "user model" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala index c849169..cd7837b 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala @@ -1,79 +1,85 @@ package me.arcanis.ffxivbis.service -import akka.actor.ActorSystem -import akka.pattern.ask -import akka.testkit.{ImplicitSender, TestKit} +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable +import me.arcanis.ffxivbis.messages.{AddPieceToBis, AddPlayer, GetBiS, RemovePieceFromBiS} import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class DatabaseBiSHandlerTest - extends TestKit(ActorSystem("database-bis-handler", Settings.withRandomDatabase)) - with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { +class DatabaseBiSHandlerTest extends ScalaTestWithActorTestKit(Settings.withRandomDatabase) + with AnyWordSpecLike { - private val database = system.actorOf(impl.DatabaseImpl.props) - private val timeout: FiniteDuration = 60 seconds + private val database = testKit.spawn(Database()) + private val askTimeout: FiniteDuration = 60 seconds override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) - Await.result((database ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout) + Await.result(Migration(testKit.system.settings.config), askTimeout) + Await.result(database.ask(AddPlayer(Fixtures.playerEmpty, _))(askTimeout, testKit.scheduler), askTimeout) } override def afterAll: Unit = { - TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + super.afterAll() + Settings.clearDatabase(testKit.system.settings.config) } "database bis handler" must { "add pieces to bis" in { - database ! impl.DatabaseBiSHandler.AddPieceToBis(Fixtures.playerEmpty.playerId, Fixtures.lootBody) - expectMsg(timeout, 1) + val probe = testKit.createTestProbe[Unit]() + database ! AddPieceToBis(Fixtures.playerEmpty.playerId, Fixtures.lootBody, probe.ref) + probe.expectMessage(askTimeout, ()) - database ! impl.DatabaseBiSHandler.AddPieceToBis(Fixtures.playerEmpty.playerId, Fixtures.lootHands) - expectMsg(timeout, 1) + database ! AddPieceToBis(Fixtures.playerEmpty.playerId, Fixtures.lootHands, probe.ref) + probe.expectMessage(askTimeout, ()) } "get party bis set" in { - database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, None) - expectMsgPF(timeout) { - case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootBody, Fixtures.lootHands)) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetBiS(Fixtures.playerEmpty.partyId, None, probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyBiSCompare(party, Seq(Fixtures.lootBody, Fixtures.lootHands)) shouldEqual true } "get bis set" in { - database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, Some(Fixtures.playerEmpty.playerId)) - expectMsgPF(timeout) { - case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootBody, Fixtures.lootHands)) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetBiS(Fixtures.playerEmpty.partyId, Some(Fixtures.playerEmpty.playerId), probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyBiSCompare(party, Seq(Fixtures.lootBody, Fixtures.lootHands)) shouldEqual true } "remove piece from bis set" in { - database ! impl.DatabaseBiSHandler.RemovePieceFromBiS(Fixtures.playerEmpty.playerId, Fixtures.lootBody) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! RemovePieceFromBiS(Fixtures.playerEmpty.playerId, Fixtures.lootBody, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, None) - expectMsgPF(timeout) { - case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootHands)) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetBiS(Fixtures.playerEmpty.partyId, None, probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyBiSCompare(party, Seq(Fixtures.lootHands)) shouldEqual true } "update piece in bis set" in { + val updateProbe = testKit.createTestProbe[Unit]() val newPiece = Hands(pieceType = PieceType.Savage, Job.DNC) - database ! impl.DatabaseBiSHandler.AddPieceToBis(Fixtures.playerEmpty.playerId, newPiece) - expectMsg(timeout, 1) + database ! AddPieceToBis(Fixtures.playerEmpty.playerId, newPiece, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, None) - expectMsgPF(timeout) { - case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootHands, newPiece)) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetBiS(Fixtures.playerEmpty.partyId, None, probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyBiSCompare(party, Seq(Fixtures.lootHands, newPiece)) shouldEqual true } } diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala index 2c4ead2..38422e3 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala @@ -1,83 +1,89 @@ package me.arcanis.ffxivbis.service -import akka.actor.ActorSystem -import akka.pattern.ask -import akka.testkit.{ImplicitSender, TestKit} +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable +import me.arcanis.ffxivbis.messages.{AddPieceTo, AddPlayer, GetLoot, RemovePieceFrom} import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class DatabaseLootHandlerTest - extends TestKit(ActorSystem("database-loot-handler", Settings.withRandomDatabase)) - with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { +class DatabaseLootHandlerTest extends ScalaTestWithActorTestKit(Settings.withRandomDatabase) + with AnyWordSpecLike { - private val database = system.actorOf(impl.DatabaseImpl.props) - private val timeout: FiniteDuration = 60 seconds + private val database = testKit.spawn(Database()) + private val askTimeout = 60 seconds override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) - Await.result((database ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout) + Await.result(Migration(testKit.system.settings.config), askTimeout) + Await.result(database.ask(AddPlayer(Fixtures.playerEmpty, _))(askTimeout, testKit.scheduler), askTimeout) } override def afterAll: Unit = { - TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + super.afterAll() + Settings.clearDatabase(testKit.system.settings.config) } "database loot handler actor" must { "add loot" in { + val probe = testKit.createTestProbe[Unit]() Fixtures.loot.foreach { piece => - database ! impl.DatabaseLootHandler.AddPieceTo(Fixtures.playerEmpty.playerId, piece, isFreeLoot = false) - expectMsg(timeout, 1) + database ! AddPieceTo(Fixtures.playerEmpty.playerId, piece, isFreeLoot = false, probe.ref) + probe.expectMessage(askTimeout, ()) } } "get party loot" in { - database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, None) - expectMsgPF(timeout) { - case party: Seq[_] if partyLootCompare(party, Fixtures.loot) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetLoot(Fixtures.playerEmpty.partyId, None, probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyLootCompare(party, Fixtures.loot) shouldEqual true } "get loot" in { - database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, Some(Fixtures.playerEmpty.playerId)) - expectMsgPF(timeout) { - case party: Seq[_] if partyLootCompare(party, Fixtures.loot) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetLoot(Fixtures.playerEmpty.partyId, Some(Fixtures.playerEmpty.playerId), probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyLootCompare(party, Fixtures.loot) shouldEqual true } "remove loot" in { - database ! impl.DatabaseLootHandler.RemovePieceFrom(Fixtures.playerEmpty.playerId, Fixtures.lootBody) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! RemovePieceFrom(Fixtures.playerEmpty.playerId, Fixtures.lootBody, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) val newLoot = Fixtures.loot.filterNot(_ == Fixtures.lootBody) - database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, None) - expectMsgPF(timeout) { - case party: Seq[_] if partyLootCompare(party, newLoot) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetLoot(Fixtures.playerEmpty.partyId, None, probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyLootCompare(party, newLoot) shouldEqual true } "add same loot" in { - database ! impl.DatabaseLootHandler.AddPieceTo(Fixtures.playerEmpty.playerId, Fixtures.lootBody, isFreeLoot = false) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! AddPieceTo(Fixtures.playerEmpty.playerId, Fixtures.lootBody, isFreeLoot = false, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) Fixtures.loot.foreach { piece => - database ! impl.DatabaseLootHandler.AddPieceTo(Fixtures.playerEmpty.playerId, piece, isFreeLoot = false) - expectMsg(timeout, 1) + database ! AddPieceTo(Fixtures.playerEmpty.playerId, piece, isFreeLoot = false, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) } - database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, None) - expectMsgPF(timeout) { - case party: Seq[_] if partyLootCompare(party, Fixtures.loot ++ Fixtures.loot) => () - } + val probe = testKit.createTestProbe[Seq[Player]]() + database ! GetLoot(Fixtures.playerEmpty.partyId, None, probe.ref) + + val party = probe.expectMessageType[Seq[Player]](askTimeout) + partyLootCompare(party, Fixtures.loot ++ Fixtures.loot) shouldEqual true } } diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala index 59caee8..daec91e 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala @@ -1,73 +1,80 @@ package me.arcanis.ffxivbis.service -import akka.actor.ActorSystem -import akka.testkit.{ImplicitSender, TestKit} +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import me.arcanis.ffxivbis.messages.{AddPlayer, GetParty, GetPlayer, RemovePlayer} import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class DatabasePartyHandlerTest - extends TestKit(ActorSystem("database-party-handler", Settings.withRandomDatabase)) - with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { +class DatabasePartyHandlerTest extends ScalaTestWithActorTestKit(Settings.withRandomDatabase) + with AnyWordSpecLike { - private val database = system.actorOf(impl.DatabaseImpl.props) - private val timeout: FiniteDuration = 60 seconds + private val database = testKit.spawn(Database()) + private val askTimeout = 60 seconds override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) + Await.result(Migration(testKit.system.settings.config), askTimeout) } override def afterAll: Unit = { - TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + super.afterAll() + Settings.clearDatabase(testKit.system.settings.config) } "database party handler actor" must { "add player" in { - database ! impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty) - expectMsg(timeout, 1) + val probe = testKit.createTestProbe[Unit]() + database ! AddPlayer(Fixtures.playerEmpty, probe.ref) + probe.expectMessage(askTimeout, ()) } "get party" in { - database ! impl.DatabasePartyHandler.GetParty(Fixtures.partyId) - expectMsgPF(timeout) { - case p: Party if Compare.seqEquals(p.getPlayers, Seq(Fixtures.playerEmpty)) => () - } + val probe = testKit.createTestProbe[Party]() + database ! GetParty(Fixtures.partyId, probe.ref) + + val party = probe.expectMessageType[Party](askTimeout) + Compare.seqEquals(party.getPlayers, Seq(Fixtures.playerEmpty)) shouldEqual true } "get player" in { - database ! impl.DatabasePartyHandler.GetPlayer(Fixtures.playerEmpty.playerId) - expectMsg(timeout, Some(Fixtures.playerEmpty)) + val probe = testKit.createTestProbe[Option[Player]]() + database ! GetPlayer(Fixtures.playerEmpty.playerId, probe.ref) + probe.expectMessage(askTimeout, Some(Fixtures.playerEmpty)) } "update player" in { + val updateProbe = testKit.createTestProbe[Unit]() val newPlayer = Fixtures.playerEmpty.copy(priority = 2) - database ! impl.DatabasePartyHandler.AddPlayer(newPlayer) - expectMsg(timeout, 1) + database ! AddPlayer(newPlayer, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabasePartyHandler.GetPlayer(newPlayer.playerId) - expectMsg(timeout, Some(newPlayer)) + val probe = testKit.createTestProbe[Option[Player]]() + database ! GetPlayer(newPlayer.playerId, probe.ref) + probe.expectMessage(askTimeout, Some(newPlayer)) - database ! impl.DatabasePartyHandler.GetParty(Fixtures.partyId) - expectMsgPF(timeout) { - case p: Party if Compare.seqEquals(p.getPlayers, Seq(newPlayer)) => () - } + val partyProbe = testKit.createTestProbe[Party]() + database ! GetParty(Fixtures.partyId, partyProbe.ref) + + val party = partyProbe.expectMessageType[Party](askTimeout) + Compare.seqEquals(party.getPlayers, Seq(newPlayer)) shouldEqual true } "remove player" in { - database ! impl.DatabasePartyHandler.RemovePlayer(Fixtures.playerEmpty.playerId) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! RemovePlayer(Fixtures.playerEmpty.playerId, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabasePartyHandler.GetPlayer(Fixtures.playerEmpty.playerId) - expectMsg(timeout, None) + val probe = testKit.createTestProbe[Option[Player]]() + database ! GetPlayer(Fixtures.playerEmpty.playerId, probe.ref) + probe.expectMessage(askTimeout, None) } } diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala index 3a871a0..8a6a862 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala @@ -1,76 +1,85 @@ package me.arcanis.ffxivbis.service -import akka.actor.ActorSystem -import akka.testkit.{ImplicitSender, TestKit} +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import me.arcanis.ffxivbis.messages.{AddUser, DeleteUser, GetUser, GetUsers} +import me.arcanis.ffxivbis.models.User import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class DatabaseUserHandlerTest - extends TestKit(ActorSystem("database-user-handler", Settings.withRandomDatabase)) - with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { +class DatabaseUserHandlerTest extends ScalaTestWithActorTestKit(Settings.withRandomDatabase) + with AnyWordSpecLike { - private val database = system.actorOf(impl.DatabaseImpl.props) - private val timeout: FiniteDuration = 60 seconds + private val database = testKit.spawn(Database()) + private val askTimeout = 60 seconds override def beforeAll: Unit = { - Await.result(Migration(system.settings.config), timeout) + Await.result(Migration(testKit.system.settings.config), askTimeout) } override def afterAll: Unit = { - TestKit.shutdownActorSystem(system) - Settings.clearDatabase(system.settings.config) + super.afterAll() + Settings.clearDatabase(testKit.system.settings.config) } "database user handler actor" must { "add user" in { - database ! impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true) - expectMsg(timeout, 1) + val probe = testKit.createTestProbe[Unit]() + database ! AddUser(Fixtures.userAdmin, isHashedPassword = true, probe.ref) + probe.expectMessage(askTimeout, ()) } "get user" in { - database ! impl.DatabaseUserHandler.GetUser(Fixtures.partyId, Fixtures.userAdmin.username) - expectMsg(timeout, Some(Fixtures.userAdmin)) + val probe = testKit.createTestProbe[Option[User]]() + database ! GetUser(Fixtures.partyId, Fixtures.userAdmin.username, probe.ref) + probe.expectMessage(askTimeout, Some(Fixtures.userAdmin)) } "get users" in { - database ! impl.DatabaseUserHandler.AddUser(Fixtures.userGet, isHashedPassword = true) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! AddUser(Fixtures.userGet, isHashedPassword = true, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabaseUserHandler.GetUsers(Fixtures.partyId) - expectMsgPF(timeout) { - case u: Seq[_] if Compare.seqEquals(u, Fixtures.users) => () - } + val probe = testKit.createTestProbe[Seq[User]]() + database ! GetUsers(Fixtures.partyId, probe.ref) + + val users = probe.expectMessageType[Seq[User]] + Compare.seqEquals(users, Fixtures.users) shouldEqual true } "update user" in { val newUser= Fixtures.userGet.copy(password = Fixtures.userPassword2).withHashedPassword val newUserSet = Seq(newUser, Fixtures.userAdmin) - database ! impl.DatabaseUserHandler.AddUser(newUser, isHashedPassword = true) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! AddUser(newUser, isHashedPassword = true, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabaseUserHandler.GetUser(Fixtures.partyId, newUser.username) - expectMsg(timeout, Some(newUser)) + val probe = testKit.createTestProbe[Option[User]]() + database ! GetUser(Fixtures.partyId, newUser.username, probe.ref) + probe.expectMessage(askTimeout, Some(newUser)) - database ! impl.DatabaseUserHandler.GetUsers(Fixtures.partyId) - expectMsgPF(timeout) { - case u: Seq[_] if Compare.seqEquals(u, newUserSet) => () - } + val partyProbe = testKit.createTestProbe[Seq[User]]() + database ! GetUsers(Fixtures.partyId, partyProbe.ref) + + val users = partyProbe.expectMessageType[Seq[User]] + Compare.seqEquals(users, newUserSet) shouldEqual true } "remove user" in { - database ! impl.DatabaseUserHandler.DeleteUser(Fixtures.partyId, Fixtures.userGet.username) - expectMsg(timeout, 1) + val updateProbe = testKit.createTestProbe[Unit]() + database ! DeleteUser(Fixtures.partyId, Fixtures.userGet.username, updateProbe.ref) + updateProbe.expectMessage(askTimeout, ()) - database ! impl.DatabaseUserHandler.GetUser(Fixtures.partyId, Fixtures.userGet.username) - expectMsg(timeout, None) + val probe = testKit.createTestProbe[Option[User]]() + database ! GetUser(Fixtures.partyId, Fixtures.userGet.username, probe.ref) + probe.expectMessage(askTimeout, None) } } } diff --git a/src/test/scala/me/arcanis/ffxivbis/service/LootSelectorTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/LootSelectorTest.scala index a914076..3f34e6b 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/LootSelectorTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/LootSelectorTest.scala @@ -1,19 +1,20 @@ package me.arcanis.ffxivbis.service -import akka.actor.ActorSystem -import akka.pattern.ask -import akka.testkit.{ImplicitSender, TestKit} +import akka.actor.testkit.typed.scaladsl.ActorTestKit +import akka.actor.typed.scaladsl.AskPattern.Askable +import me.arcanis.ffxivbis.messages.DownloadBiS import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.service.bis.BisProvider -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.BeforeAndAfterAll +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps -class LootSelectorTest extends TestKit(ActorSystem("lootselector")) - with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { +class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfterAll { import me.arcanis.ffxivbis.utils.Converters._ @@ -23,16 +24,19 @@ class LootSelectorTest extends TestKit(ActorSystem("lootselector")) private val timeout: FiniteDuration = 60 seconds override def beforeAll(): Unit = { - val provider = system.actorOf(BisProvider.props) + val testKit = ActorTestKit(Settings.withRandomDatabase) + val provider = testKit.spawn(BisProvider()) - val dncSet = Await.result((provider ? BisProvider.GetBiS(Fixtures.link, Job.DNC) )(timeout).mapTo[BiS], timeout) + val dncSet = Await.result(provider.ask(DownloadBiS(Fixtures.link, Job.DNC, _) )(timeout, testKit.scheduler), timeout) dnc = dnc.withBiS(Some(dncSet)) - val drgSet = Await.result((provider ? BisProvider.GetBiS(Fixtures.link2, Job.DRG) )(timeout).mapTo[BiS], timeout) + val drgSet = Await.result(provider.ask(DownloadBiS(Fixtures.link2, Job.DRG, _) )(timeout, testKit.scheduler), timeout) drg = drg.withBiS(Some(drgSet)) default = default.withPlayer(dnc).withPlayer(drg) - system.stop(provider) + + testKit.stop(provider) + testKit.shutdownTestKit() } "loot selector" must { diff --git a/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala index 5c70a28..3d20613 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala @@ -1,33 +1,32 @@ package me.arcanis.ffxivbis.service.bis -import akka.actor.ActorSystem -import akka.testkit.{ImplicitSender, TestKit} -import me.arcanis.ffxivbis.Fixtures +import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit +import me.arcanis.ffxivbis.messages.DownloadBiS +import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ -import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike} +import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.duration._ import scala.language.postfixOps -class BisProviderTest extends TestKit(ActorSystem("bis-provider")) - with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { +class BisProviderTest extends ScalaTestWithActorTestKit(Settings.withRandomDatabase) + with AnyWordSpecLike { - private val timeout: FiniteDuration = 60 seconds - - override def afterAll: Unit = TestKit.shutdownActorSystem(system) + private val provider = testKit.spawn(BisProvider()) + private val askTimeout = 60 seconds "ariyala actor" must { "get best in slot set (ariyala)" in { - val provider = system.actorOf(BisProvider.props) - provider ! BisProvider.GetBiS(Fixtures.link, Job.DNC) - expectMsg(timeout, Fixtures.bis) + val probe = testKit.createTestProbe[BiS]() + provider ! DownloadBiS(Fixtures.link, Job.DNC, probe.ref) + probe.expectMessage(askTimeout, Fixtures.bis) } "get best in slot set (etro)" in { - val provider = system.actorOf(BisProvider.props) - provider ! BisProvider.GetBiS(Fixtures.link3, Job.DNC) - expectMsg(timeout, Fixtures.bis) + val probe = testKit.createTestProbe[BiS]() + provider ! DownloadBiS(Fixtures.link3, Job.DNC, probe.ref) + probe.expectMessage(askTimeout, Fixtures.bis) } } diff --git a/test.sbt b/test.sbt index 352ddd2..7175ad3 100644 --- a/test.sbt +++ b/test.sbt @@ -1,6 +1,7 @@ -libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.8" % "test" -libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test" +libraryDependencies += "org.scalactic" %% "scalactic" % "3.1.4" % "test" +libraryDependencies += "org.scalatest" %% "scalatest" % "3.1.4" % "test" -libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.5.26" % "test" -libraryDependencies += "com.typesafe.akka" %% "akka-stream-testkit" % "2.5.26" % "test" -libraryDependencies += "com.typesafe.akka" %% "akka-http-testkit" % "10.1.10" % "test" +libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.6.10" % "test" +libraryDependencies += "com.typesafe.akka" %% "akka-actor-testkit-typed" % "2.6.10" % "test" +libraryDependencies += "com.typesafe.akka" %% "akka-stream-testkit" % "2.6.10" % "test" +libraryDependencies += "com.typesafe.akka" %% "akka-http-testkit" % "10.2.1" % "test"