initial typed actor impl

This commit is contained in:
Evgenii Alekseev 2020-12-04 03:22:17 +03:00
parent 2e16a8c1fa
commit d42e90608f
77 changed files with 842 additions and 699 deletions

View File

@ -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"

View File

@ -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 {

View File

@ -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))
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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, _))
}

View File

@ -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, _))
}

View File

@ -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)
}

View File

@ -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, _))
}

View File

@ -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")

View File

@ -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],

View File

@ -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, _))
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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)),

View File

@ -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))
}

View File

@ -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

View File

@ -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"))
}

View File

@ -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("")

View File

@ -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"),

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -12,6 +12,7 @@ import scalatags.Text
import scalatags.Text.all._
object SearchLineView {
def template: Text.TypedTag[String] =
div(
input(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,9 @@
package me.arcanis.ffxivbis.messages
import akka.actor.typed.Behavior
trait Message
object Message {
type Handler = PartialFunction[Message, Behavior[Message]]
}

View File

@ -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

View File

@ -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))
}

View File

@ -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

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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]],

View File

@ -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 {

View File

@ -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

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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")

View File

@ -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()
}

View File

@ -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 = {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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 =

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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"