mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-25 01:37:17 +00:00
styling
This commit is contained in:
parent
88617eccdf
commit
ed3cdd62bd
@ -10,8 +10,7 @@ package me.arcanis.ffxivbis
|
|||||||
|
|
||||||
import akka.actor.typed.ActorSystem
|
import akka.actor.typed.ActorSystem
|
||||||
|
|
||||||
object ffxivbis {
|
object ffxivbis extends App {
|
||||||
|
|
||||||
def main(args: Array[String]): Unit =
|
ActorSystem[Nothing](Application(), "ffxivbis", Configuration.load())
|
||||||
ActorSystem[Nothing](Application(), "ffxivbis", Configuration.load())
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@ import akka.actor.typed.{ActorRef, Scheduler}
|
|||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.messages.{GetUser, Message}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage.GetUser
|
||||||
|
import me.arcanis.ffxivbis.messages.Message
|
||||||
import me.arcanis.ffxivbis.models.{Permission, User}
|
import me.arcanis.ffxivbis.models.{Permission, User}
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -16,13 +16,15 @@ import com.typesafe.scalalogging.StrictLogging
|
|||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import spray.json._
|
import spray.json._
|
||||||
|
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
trait HttpHandler extends StrictLogging { this: JsonSupport =>
|
trait HttpHandler extends StrictLogging { this: JsonSupport =>
|
||||||
|
|
||||||
def exceptionHandler: ExceptionHandler = ExceptionHandler {
|
def exceptionHandler: ExceptionHandler = ExceptionHandler {
|
||||||
case ex: IllegalArgumentException =>
|
case exception: IllegalArgumentException =>
|
||||||
complete(StatusCodes.BadRequest, ErrorModel(ex.getMessage))
|
complete(StatusCodes.BadRequest, ErrorModel(exception.getMessage))
|
||||||
|
|
||||||
case other: Exception =>
|
case NonFatal(other) =>
|
||||||
logger.error("exception during request completion", other)
|
logger.error("exception during request completion", other)
|
||||||
complete(StatusCodes.InternalServerError, ErrorModel("unknown server error"))
|
complete(StatusCodes.InternalServerError, ErrorModel("unknown server error"))
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,6 @@
|
|||||||
package me.arcanis.ffxivbis.http.api.v1.json
|
package me.arcanis.ffxivbis.http.api.v1.json
|
||||||
|
|
||||||
object ApiAction extends Enumeration {
|
object ApiAction extends Enumeration {
|
||||||
|
|
||||||
val add, remove = Value
|
val add, remove = Value
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ import akka.actor.typed.scaladsl.AskPattern.Askable
|
|||||||
import akka.actor.typed.{ActorRef, Scheduler}
|
import akka.actor.typed.{ActorRef, Scheduler}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
|
import me.arcanis.ffxivbis.messages.Message
|
||||||
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId}
|
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
@ -11,7 +11,8 @@ package me.arcanis.ffxivbis.http.helpers
|
|||||||
import akka.actor.typed.scaladsl.AskPattern.Askable
|
import akka.actor.typed.scaladsl.AskPattern.Askable
|
||||||
import akka.actor.typed.{ActorRef, Scheduler}
|
import akka.actor.typed.{ActorRef, Scheduler}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.messages.{BiSProviderMessage, DownloadBiS}
|
import me.arcanis.ffxivbis.messages.BiSProviderMessage
|
||||||
|
import me.arcanis.ffxivbis.messages.BiSProviderMessage._
|
||||||
import me.arcanis.ffxivbis.models.{BiS, Job}
|
import me.arcanis.ffxivbis.models.{BiS, Job}
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
@ -20,6 +21,6 @@ trait BisProviderHelper {
|
|||||||
|
|
||||||
def provider: ActorRef[BiSProviderMessage]
|
def provider: ActorRef[BiSProviderMessage]
|
||||||
|
|
||||||
def downloadBiS(link: String, job: Job.Job)(implicit timeout: Timeout, scheduler: Scheduler): Future[BiS] =
|
def downloadBiS(link: String, job: Job)(implicit timeout: Timeout, scheduler: Scheduler): Future[BiS] =
|
||||||
provider.ask(DownloadBiS(link, job, _))
|
provider.ask(DownloadBiS(link, job, _))
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ import akka.actor.typed.scaladsl.AskPattern.Askable
|
|||||||
import akka.actor.typed.{ActorRef, Scheduler}
|
import akka.actor.typed.{ActorRef, Scheduler}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
|
import me.arcanis.ffxivbis.messages.Message
|
||||||
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId, PlayerIdWithCounters}
|
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId, PlayerIdWithCounters}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
@ -12,7 +12,8 @@ import akka.actor.typed.scaladsl.AskPattern.Askable
|
|||||||
import akka.actor.typed.{ActorRef, Scheduler}
|
import akka.actor.typed.{ActorRef, Scheduler}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
|
import me.arcanis.ffxivbis.messages.Message
|
||||||
import me.arcanis.ffxivbis.models.{PartyDescription, Player, PlayerId}
|
import me.arcanis.ffxivbis.models.{PartyDescription, Player, PlayerId}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
@ -11,7 +11,9 @@ package me.arcanis.ffxivbis.http.helpers
|
|||||||
import akka.actor.typed.scaladsl.AskPattern.Askable
|
import akka.actor.typed.scaladsl.AskPattern.Askable
|
||||||
import akka.actor.typed.{ActorRef, Scheduler}
|
import akka.actor.typed.{ActorRef, Scheduler}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.ControlMessage.GetNewPartyId
|
||||||
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
|
import me.arcanis.ffxivbis.messages.Message
|
||||||
import me.arcanis.ffxivbis.models.User
|
import me.arcanis.ffxivbis.models.User
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
@ -13,7 +13,10 @@ import me.arcanis.ffxivbis.models.{BiS, Job}
|
|||||||
|
|
||||||
sealed trait BiSProviderMessage
|
sealed trait BiSProviderMessage
|
||||||
|
|
||||||
case class DownloadBiS(link: String, job: Job.Job, replyTo: ActorRef[BiS]) extends BiSProviderMessage {
|
object BiSProviderMessage {
|
||||||
|
|
||||||
require(link.nonEmpty && link.trim == link, "Link must be not empty and contain no spaces")
|
case class DownloadBiS(link: String, job: Job, replyTo: ActorRef[BiS]) extends BiSProviderMessage {
|
||||||
|
|
||||||
|
require(link.nonEmpty && link.trim == link, "Link must be not empty and contain no spaces")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,13 @@ package me.arcanis.ffxivbis.messages
|
|||||||
import akka.actor.typed.ActorRef
|
import akka.actor.typed.ActorRef
|
||||||
import me.arcanis.ffxivbis.models.Party
|
import me.arcanis.ffxivbis.models.Party
|
||||||
|
|
||||||
case class ForgetParty(partyId: String) extends Message
|
sealed trait ControlMessage extends Message
|
||||||
|
|
||||||
case class GetNewPartyId(replyTo: ActorRef[String]) extends Message
|
object ControlMessage {
|
||||||
|
|
||||||
case class StoreParty(partyId: String, party: Party) extends Message
|
case class ForgetParty(partyId: String) extends ControlMessage
|
||||||
|
|
||||||
|
case class GetNewPartyId(replyTo: ActorRef[String]) extends ControlMessage
|
||||||
|
|
||||||
|
case class StoreParty(partyId: String, party: Party) extends ControlMessage
|
||||||
|
}
|
@ -19,105 +19,108 @@ sealed trait DatabaseMessage extends Message {
|
|||||||
def isReadOnly: Boolean
|
def isReadOnly: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// bis handler
|
object DatabaseMessage {
|
||||||
trait BisDatabaseMessage extends DatabaseMessage
|
|
||||||
|
|
||||||
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
// bis handler
|
||||||
override val partyId: String = playerId.partyId
|
trait BisDatabaseMessage extends DatabaseMessage
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
override val isReadOnly: Boolean = false
|
||||||
extends BisDatabaseMessage {
|
}
|
||||||
override val isReadOnly: Boolean = true
|
|
||||||
}
|
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||||
|
extends BisDatabaseMessage {
|
||||||
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val partyId: String = playerId.partyId
|
}
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
override val isReadOnly: Boolean = false
|
||||||
override val partyId: String = playerId.partyId
|
}
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
// loot handler
|
override val isReadOnly: Boolean = false
|
||||||
trait LootDatabaseMessage extends DatabaseMessage
|
}
|
||||||
|
|
||||||
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
// loot handler
|
||||||
extends LootDatabaseMessage {
|
trait LootDatabaseMessage extends DatabaseMessage
|
||||||
override val partyId: String = playerId.partyId
|
|
||||||
override val isReadOnly: Boolean = false
|
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||||
}
|
extends LootDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
override val isReadOnly: Boolean = false
|
||||||
extends LootDatabaseMessage {
|
}
|
||||||
override val isReadOnly: Boolean = true
|
|
||||||
}
|
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||||
|
extends LootDatabaseMessage {
|
||||||
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
override val isReadOnly: Boolean = true
|
||||||
extends LootDatabaseMessage {
|
}
|
||||||
override val partyId: String = playerId.partyId
|
|
||||||
override val isReadOnly: Boolean = false
|
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||||
}
|
extends LootDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
override val isReadOnly: Boolean = false
|
||||||
extends LootDatabaseMessage {
|
}
|
||||||
override val isReadOnly: Boolean = true
|
|
||||||
}
|
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
||||||
|
extends LootDatabaseMessage {
|
||||||
// party handler
|
override val isReadOnly: Boolean = true
|
||||||
trait PartyDatabaseMessage extends DatabaseMessage
|
}
|
||||||
|
|
||||||
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
// party handler
|
||||||
override val partyId: String = player.partyId
|
trait PartyDatabaseMessage extends DatabaseMessage
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
|
override val partyId: String = player.partyId
|
||||||
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage {
|
override val isReadOnly: Boolean = false
|
||||||
override val isReadOnly: Boolean = true
|
}
|
||||||
}
|
|
||||||
|
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage {
|
||||||
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val isReadOnly: Boolean = true
|
}
|
||||||
}
|
|
||||||
|
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage {
|
||||||
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val partyId: String = playerId.partyId
|
}
|
||||||
override val isReadOnly: Boolean = true
|
|
||||||
}
|
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val partyId: String = playerId.partyId
|
}
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
|
override val partyId: String = playerId.partyId
|
||||||
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
override val isReadOnly: Boolean = false
|
||||||
override val partyId: String = partyDescription.partyId
|
}
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
|
override val partyId: String = partyDescription.partyId
|
||||||
// user handler
|
override val isReadOnly: Boolean = false
|
||||||
trait UserDatabaseMessage extends DatabaseMessage
|
}
|
||||||
|
|
||||||
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
// user handler
|
||||||
override val partyId: String = user.partyId
|
trait UserDatabaseMessage extends DatabaseMessage
|
||||||
override val isReadOnly: Boolean = false
|
|
||||||
}
|
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||||
|
override val partyId: String = user.partyId
|
||||||
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
override val isReadOnly: Boolean = false
|
||||||
override val isReadOnly: Boolean = true
|
}
|
||||||
}
|
|
||||||
|
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||||
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val isReadOnly: Boolean = true
|
}
|
||||||
}
|
|
||||||
|
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage {
|
||||||
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val isReadOnly: Boolean = true
|
}
|
||||||
}
|
|
||||||
|
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage {
|
||||||
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage {
|
override val isReadOnly: Boolean = true
|
||||||
override val isReadOnly: Boolean = true
|
}
|
||||||
|
|
||||||
|
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,14 +11,14 @@ package me.arcanis.ffxivbis.models
|
|||||||
case class BiS(pieces: Seq[Piece]) {
|
case class BiS(pieces: Seq[Piece]) {
|
||||||
|
|
||||||
def hasPiece(piece: Piece): Boolean = piece match {
|
def hasPiece(piece: Piece): Boolean = piece match {
|
||||||
case upgrade: PieceUpgrade => upgrades.contains(upgrade)
|
case upgrade: Piece.PieceUpgrade => upgrades.contains(upgrade)
|
||||||
case _ => pieces.contains(piece)
|
case _ => pieces.contains(piece)
|
||||||
}
|
}
|
||||||
|
|
||||||
def upgrades: Map[PieceUpgrade, Int] =
|
def upgrades: Map[Piece.PieceUpgrade, Int] =
|
||||||
pieces
|
pieces
|
||||||
.groupBy(_.upgrade)
|
.groupBy(_.upgrade)
|
||||||
.foldLeft(Map.empty[PieceUpgrade, Int]) {
|
.foldLeft(Map.empty[Piece.PieceUpgrade, Int]) {
|
||||||
case (acc, (Some(k), v)) => acc + (k -> v.size)
|
case (acc, (Some(k), v)) => acc + (k -> v.size)
|
||||||
case (acc, _) => acc
|
case (acc, _) => acc
|
||||||
}
|
}
|
||||||
@ -43,5 +43,5 @@ case class BiS(pieces: Seq[Piece]) {
|
|||||||
|
|
||||||
object BiS {
|
object BiS {
|
||||||
|
|
||||||
def empty: BiS = BiS(Seq.empty)
|
val empty: BiS = BiS(Seq.empty)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,26 @@
|
|||||||
*/
|
*/
|
||||||
package me.arcanis.ffxivbis.models
|
package me.arcanis.ffxivbis.models
|
||||||
|
|
||||||
|
sealed trait Job extends Equals {
|
||||||
|
|
||||||
|
def leftSide: Job.LeftSide
|
||||||
|
|
||||||
|
def rightSide: Job.RightSide
|
||||||
|
|
||||||
|
// conversion to string to avoid recursion
|
||||||
|
override def canEqual(that: Any): Boolean = that.isInstanceOf[Job]
|
||||||
|
|
||||||
|
override def equals(obj: Any): Boolean = {
|
||||||
|
def equality(objRepr: String): Boolean = objRepr match {
|
||||||
|
case _ if objRepr == Job.AnyJob.toString => true
|
||||||
|
case _ if this.toString == Job.AnyJob.toString => true
|
||||||
|
case _ => this.toString == objRepr
|
||||||
|
}
|
||||||
|
|
||||||
|
canEqual(obj) && equality(obj.toString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object Job {
|
object Job {
|
||||||
|
|
||||||
sealed trait RightSide
|
sealed trait RightSide
|
||||||
@ -26,54 +46,38 @@ object Job {
|
|||||||
object BodyTanks extends LeftSide
|
object BodyTanks extends LeftSide
|
||||||
object BodyRanges extends LeftSide
|
object BodyRanges extends LeftSide
|
||||||
|
|
||||||
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 equality(objRepr: String): Boolean = objRepr match {
|
|
||||||
case _ if objRepr == AnyJob.toString => true
|
|
||||||
case _ if this.toString == AnyJob.toString => true
|
|
||||||
case _ => this.toString == objRepr
|
|
||||||
}
|
|
||||||
|
|
||||||
canEqual(obj) && equality(obj.toString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case object AnyJob extends Job {
|
case object AnyJob extends Job {
|
||||||
val leftSide: LeftSide = null
|
override val leftSide: LeftSide = null
|
||||||
val rightSide: RightSide = null
|
override val rightSide: RightSide = null
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Casters extends Job {
|
trait Casters extends Job {
|
||||||
val leftSide: LeftSide = BodyCasters
|
override val leftSide: LeftSide = BodyCasters
|
||||||
val rightSide: RightSide = AccessoriesInt
|
override val rightSide: RightSide = AccessoriesInt
|
||||||
}
|
}
|
||||||
trait Healers extends Job {
|
trait Healers extends Job {
|
||||||
val leftSide: LeftSide = BodyHealers
|
override val leftSide: LeftSide = BodyHealers
|
||||||
val rightSide: RightSide = AccessoriesMnd
|
override val rightSide: RightSide = AccessoriesMnd
|
||||||
}
|
}
|
||||||
trait Mnks extends Job {
|
trait Mnks extends Job {
|
||||||
val leftSide: LeftSide = BodyMnks
|
override val leftSide: LeftSide = BodyMnks
|
||||||
val rightSide: RightSide = AccessoriesStr
|
override val rightSide: RightSide = AccessoriesStr
|
||||||
}
|
}
|
||||||
trait Drgs extends Job {
|
trait Drgs extends Job {
|
||||||
val leftSide: LeftSide = BodyDrgs
|
override val leftSide: LeftSide = BodyDrgs
|
||||||
val rightSide: RightSide = AccessoriesStr
|
override val rightSide: RightSide = AccessoriesStr
|
||||||
|
}
|
||||||
|
trait Nins extends Job {
|
||||||
|
override val leftSide: LeftSide = BodyNins
|
||||||
|
override val rightSide: RightSide = AccessoriesDex
|
||||||
}
|
}
|
||||||
trait Tanks extends Job {
|
trait Tanks extends Job {
|
||||||
val leftSide: LeftSide = BodyTanks
|
override val leftSide: LeftSide = BodyTanks
|
||||||
val rightSide: RightSide = AccessoriesVit
|
override val rightSide: RightSide = AccessoriesVit
|
||||||
}
|
}
|
||||||
trait Ranges extends Job {
|
trait Ranges extends Job {
|
||||||
val leftSide: LeftSide = BodyRanges
|
override val leftSide: LeftSide = BodyRanges
|
||||||
val rightSide: RightSide = AccessoriesDex
|
override val rightSide: RightSide = AccessoriesDex
|
||||||
}
|
}
|
||||||
|
|
||||||
case object PLD extends Tanks
|
case object PLD extends Tanks
|
||||||
@ -89,10 +93,7 @@ object Job {
|
|||||||
case object MNK extends Mnks
|
case object MNK extends Mnks
|
||||||
case object DRG extends Drgs
|
case object DRG extends Drgs
|
||||||
case object RPR extends Drgs
|
case object RPR extends Drgs
|
||||||
case object NIN extends Job {
|
case object NIN extends Nins
|
||||||
val leftSide: LeftSide = BodyNins
|
|
||||||
val rightSide: RightSide = AccessoriesDex
|
|
||||||
}
|
|
||||||
case object SAM extends Mnks
|
case object SAM extends Mnks
|
||||||
|
|
||||||
case object BRD extends Ranges
|
case object BRD extends Ranges
|
||||||
@ -103,11 +104,11 @@ object Job {
|
|||||||
case object SMN extends Casters
|
case object SMN extends Casters
|
||||||
case object RDM extends Casters
|
case object RDM extends Casters
|
||||||
|
|
||||||
lazy val available: Seq[Job] =
|
val available: Seq[Job] =
|
||||||
Seq(PLD, WAR, DRK, GNB, WHM, SCH, AST, SGE, MNK, DRG, RPR, NIN, SAM, BRD, MCH, DNC, BLM, SMN, RDM)
|
Seq(PLD, WAR, DRK, GNB, WHM, SCH, AST, SGE, MNK, DRG, RPR, NIN, SAM, BRD, MCH, DNC, BLM, SMN, RDM)
|
||||||
lazy val availableWithAnyJob: Seq[Job] = available.prepended(AnyJob)
|
val availableWithAnyJob: Seq[Job] = available.prepended(AnyJob)
|
||||||
|
|
||||||
def withName(job: String): Job.Job =
|
def withName(job: String): Job =
|
||||||
availableWithAnyJob.find(_.toString.equalsIgnoreCase(job)) match {
|
availableWithAnyJob.find(_.toString.equalsIgnoreCase(job)) match {
|
||||||
case Some(value) => value
|
case Some(value) => value
|
||||||
case None if job.isEmpty => AnyJob
|
case None if job.isEmpty => AnyJob
|
||||||
|
@ -12,7 +12,5 @@ import java.time.Instant
|
|||||||
|
|
||||||
case class Loot(playerId: Long, piece: Piece, timestamp: Instant, isFreeLoot: Boolean) {
|
case class Loot(playerId: Long, piece: Piece, timestamp: Instant, isFreeLoot: Boolean) {
|
||||||
|
|
||||||
def isFreeLootToString: String = if (isFreeLoot) "yes" else "no"
|
lazy val isFreeLootToInt: Int = if (isFreeLoot) 1 else 0
|
||||||
|
|
||||||
def isFreeLootToInt: Int = if (isFreeLoot) 1 else 0
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import me.arcanis.ffxivbis.service.LootSelector
|
|||||||
|
|
||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
case class Party(partyDescription: PartyDescription, rules: Seq[String], players: Map[PlayerId, Player])
|
case class Party(partyDescription: PartyDescription, rules: Seq[String], players: Map[PlayerId, Player])
|
||||||
extends StrictLogging {
|
extends StrictLogging {
|
||||||
@ -29,7 +30,7 @@ case class Party(partyDescription: PartyDescription, rules: Seq[String], players
|
|||||||
require(player.partyId == partyDescription.partyId, "player must belong to this party")
|
require(player.partyId == partyDescription.partyId, "player must belong to this party")
|
||||||
copy(players = players + (player.playerId -> player))
|
copy(players = players + (player.playerId -> player))
|
||||||
} catch {
|
} catch {
|
||||||
case exception: Exception =>
|
case NonFatal(exception) =>
|
||||||
logger.error("cannot add player", exception)
|
logger.error("cannot add player", exception)
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package me.arcanis.ffxivbis.models
|
package me.arcanis.ffxivbis.models
|
||||||
|
|
||||||
case class PartyDescription(partyId: String, partyAlias: Option[String]) {
|
case class PartyDescription(partyId: String, partyAlias: Option[String])
|
||||||
|
|
||||||
def alias: String = partyAlias.getOrElse(partyId)
|
|
||||||
}
|
|
||||||
|
|
||||||
object PartyDescription {
|
object PartyDescription {
|
||||||
|
|
||||||
|
@ -10,20 +10,20 @@ package me.arcanis.ffxivbis.models
|
|||||||
|
|
||||||
sealed trait Piece extends Equals {
|
sealed trait Piece extends Equals {
|
||||||
|
|
||||||
def pieceType: PieceType.PieceType
|
def pieceType: PieceType
|
||||||
|
|
||||||
def job: Job.Job
|
def job: Job
|
||||||
|
|
||||||
def piece: String
|
def piece: String
|
||||||
|
|
||||||
def withJob(other: Job.Job): Piece
|
def withJob(other: Job): Piece
|
||||||
|
|
||||||
def upgrade: Option[PieceUpgrade] = {
|
def upgrade: Option[Piece.PieceUpgrade] = {
|
||||||
val isTome = pieceType == PieceType.Tome
|
val isTome = pieceType == PieceType.Tome
|
||||||
Some(this).collect {
|
Some(this).collect {
|
||||||
case _: PieceAccessory if isTome => AccessoryUpgrade
|
case _: Piece.PieceAccessory if isTome => Piece.AccessoryUpgrade
|
||||||
case _: PieceBody if isTome => BodyUpgrade
|
case _: Piece.PieceBody if isTome => Piece.BodyUpgrade
|
||||||
case _: PieceWeapon if isTome => WeaponUpgrade
|
case _: Piece.PieceWeapon if isTome => Piece.WeaponUpgrade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,83 +31,84 @@ sealed trait Piece extends Equals {
|
|||||||
def strictEqual(obj: Any): Boolean = equals(obj)
|
def strictEqual(obj: Any): Boolean = equals(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PieceAccessory extends Piece
|
|
||||||
trait PieceBody extends Piece
|
|
||||||
trait PieceUpgrade extends Piece {
|
|
||||||
val pieceType: PieceType.PieceType = PieceType.Tome
|
|
||||||
val job: Job.Job = Job.AnyJob
|
|
||||||
def withJob(other: Job.Job): Piece = this
|
|
||||||
}
|
|
||||||
trait PieceWeapon extends Piece
|
|
||||||
|
|
||||||
case class Weapon(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceWeapon {
|
|
||||||
val piece: String = "weapon"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
|
|
||||||
case class Head(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceBody {
|
|
||||||
val piece: String = "head"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Body(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceBody {
|
|
||||||
val piece: String = "body"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Hands(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceBody {
|
|
||||||
val piece: String = "hands"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Legs(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceBody {
|
|
||||||
val piece: String = "legs"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Feet(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceBody {
|
|
||||||
val piece: String = "feet"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
|
|
||||||
case class Ears(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceAccessory {
|
|
||||||
val piece: String = "ears"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Neck(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceAccessory {
|
|
||||||
val piece: String = "neck"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Wrist(override val pieceType: PieceType.PieceType, override val job: Job.Job) extends PieceAccessory {
|
|
||||||
val piece: String = "wrist"
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
}
|
|
||||||
case class Ring(
|
|
||||||
override val pieceType: PieceType.PieceType,
|
|
||||||
override val job: Job.Job,
|
|
||||||
override val piece: String = "ring"
|
|
||||||
) extends PieceAccessory {
|
|
||||||
def withJob(other: Job.Job): Piece = copy(job = other)
|
|
||||||
|
|
||||||
override def equals(obj: Any): Boolean = obj match {
|
|
||||||
case Ring(thatPieceType, thatJob, _) => (thatPieceType == pieceType) && (thatJob == job)
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
|
|
||||||
override def strictEqual(obj: Any): Boolean = obj match {
|
|
||||||
case ring: Ring => equals(obj) && (ring.piece == this.piece)
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case object AccessoryUpgrade extends PieceUpgrade {
|
|
||||||
val piece: String = "accessory upgrade"
|
|
||||||
}
|
|
||||||
case object BodyUpgrade extends PieceUpgrade {
|
|
||||||
val piece: String = "body upgrade"
|
|
||||||
}
|
|
||||||
case object WeaponUpgrade extends PieceUpgrade {
|
|
||||||
val piece: String = "weapon upgrade"
|
|
||||||
}
|
|
||||||
|
|
||||||
object Piece {
|
object Piece {
|
||||||
def apply(piece: String, pieceType: PieceType.PieceType, job: Job.Job = Job.AnyJob): Piece =
|
|
||||||
|
trait PieceAccessory extends Piece
|
||||||
|
trait PieceBody extends Piece
|
||||||
|
trait PieceUpgrade extends Piece {
|
||||||
|
override val pieceType: PieceType = PieceType.Tome
|
||||||
|
override val job: Job = Job.AnyJob
|
||||||
|
override def withJob(other: Job): Piece = this
|
||||||
|
}
|
||||||
|
trait PieceWeapon extends Piece
|
||||||
|
|
||||||
|
case class Weapon(override val pieceType: PieceType, override val job: Job) extends PieceWeapon {
|
||||||
|
override val piece: String = "weapon"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Head(override val pieceType: PieceType, override val job: Job) extends PieceBody {
|
||||||
|
override val piece: String = "head"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Body(override val pieceType: PieceType, override val job: Job) extends PieceBody {
|
||||||
|
override val piece: String = "body"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Hands(override val pieceType: PieceType, override val job: Job) extends PieceBody {
|
||||||
|
override val piece: String = "hands"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Legs(override val pieceType: PieceType, override val job: Job) extends PieceBody {
|
||||||
|
override val piece: String = "legs"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Feet(override val pieceType: PieceType, override val job: Job) extends PieceBody {
|
||||||
|
override val piece: String = "feet"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Ears(override val pieceType: PieceType, override val job: Job) extends PieceAccessory {
|
||||||
|
override val piece: String = "ears"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Neck(override val pieceType: PieceType, override val job: Job) extends PieceAccessory {
|
||||||
|
override val piece: String = "neck"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Wrist(override val pieceType: PieceType, override val job: Job) extends PieceAccessory {
|
||||||
|
override val piece: String = "wrist"
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
}
|
||||||
|
case class Ring(
|
||||||
|
override val pieceType: PieceType,
|
||||||
|
override val job: Job,
|
||||||
|
override val piece: String = "ring"
|
||||||
|
) extends PieceAccessory {
|
||||||
|
override def withJob(other: Job): Piece = copy(job = other)
|
||||||
|
|
||||||
|
override def equals(obj: Any): Boolean = obj match {
|
||||||
|
case Ring(thatPieceType, thatJob, _) => (thatPieceType == pieceType) && (thatJob == job)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
|
||||||
|
override def strictEqual(obj: Any): Boolean = obj match {
|
||||||
|
case ring: Ring => equals(obj) && (ring.piece == this.piece)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case object AccessoryUpgrade extends PieceUpgrade {
|
||||||
|
override val piece: String = "accessory upgrade"
|
||||||
|
}
|
||||||
|
case object BodyUpgrade extends PieceUpgrade {
|
||||||
|
override val piece: String = "body upgrade"
|
||||||
|
}
|
||||||
|
case object WeaponUpgrade extends PieceUpgrade {
|
||||||
|
override val piece: String = "weapon upgrade"
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(piece: String, pieceType: PieceType, job: Job = Job.AnyJob): Piece =
|
||||||
piece.toLowerCase match {
|
piece.toLowerCase match {
|
||||||
case "weapon" => Weapon(pieceType, job)
|
case "weapon" => Weapon(pieceType, job)
|
||||||
case "head" => Head(pieceType, job)
|
case "head" => Head(pieceType, job)
|
||||||
@ -125,7 +126,7 @@ object Piece {
|
|||||||
case other => throw new Error(s"Unknown item type $other")
|
case other => throw new Error(s"Unknown item type $other")
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val available: Seq[String] = Seq(
|
val available: Seq[String] = Seq(
|
||||||
"weapon",
|
"weapon",
|
||||||
"head",
|
"head",
|
||||||
"body",
|
"body",
|
||||||
|
@ -8,17 +8,16 @@
|
|||||||
*/
|
*/
|
||||||
package me.arcanis.ffxivbis.models
|
package me.arcanis.ffxivbis.models
|
||||||
|
|
||||||
|
sealed trait PieceType
|
||||||
|
|
||||||
object PieceType {
|
object PieceType {
|
||||||
|
|
||||||
sealed trait PieceType
|
|
||||||
|
|
||||||
case object Crafted extends PieceType
|
|
||||||
case object Tome extends PieceType
|
|
||||||
case object Savage extends PieceType
|
case object Savage extends PieceType
|
||||||
|
case object Tome extends PieceType
|
||||||
|
case object Crafted extends PieceType
|
||||||
case object Artifact extends PieceType
|
case object Artifact extends PieceType
|
||||||
|
|
||||||
lazy val available: Seq[PieceType] =
|
val available: Seq[PieceType] = Seq(Savage, Tome, Crafted, Artifact)
|
||||||
Seq(Crafted, Tome, Savage, Artifact)
|
|
||||||
|
|
||||||
def withName(pieceType: String): PieceType =
|
def withName(pieceType: String): PieceType =
|
||||||
available.find(_.toString.equalsIgnoreCase(pieceType)) match {
|
available.find(_.toString.equalsIgnoreCase(pieceType)) match {
|
||||||
|
@ -11,7 +11,7 @@ package me.arcanis.ffxivbis.models
|
|||||||
case class Player(
|
case class Player(
|
||||||
id: Long,
|
id: Long,
|
||||||
partyId: String,
|
partyId: String,
|
||||||
job: Job.Job,
|
job: Job,
|
||||||
nick: String,
|
nick: String,
|
||||||
bis: BiS,
|
bis: BiS,
|
||||||
loot: Seq[Loot],
|
loot: Seq[Loot],
|
||||||
@ -51,7 +51,7 @@ case class Player(
|
|||||||
piece match {
|
piece match {
|
||||||
case None => false
|
case None => false
|
||||||
case Some(p) if !bis.hasPiece(p) => false
|
case Some(p) if !bis.hasPiece(p) => false
|
||||||
case Some(p: PieceUpgrade) => bis.upgrades(p) > lootCount(piece)
|
case Some(p: Piece.PieceUpgrade) => bis.upgrades(p) > lootCount(piece)
|
||||||
case Some(_) => lootCount(piece) == 0
|
case Some(_) => lootCount(piece) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,14 +13,14 @@ import scala.util.matching.Regex
|
|||||||
|
|
||||||
trait PlayerIdBase {
|
trait PlayerIdBase {
|
||||||
|
|
||||||
def job: Job.Job
|
def job: Job
|
||||||
|
|
||||||
def nick: String
|
def nick: String
|
||||||
|
|
||||||
override def toString: String = s"$nick ($job)"
|
override def toString: String = s"$nick ($job)"
|
||||||
}
|
}
|
||||||
|
|
||||||
case class PlayerId(partyId: String, job: Job.Job, nick: String) extends PlayerIdBase
|
case class PlayerId(partyId: String, job: Job, nick: String) extends PlayerIdBase
|
||||||
|
|
||||||
object PlayerId {
|
object PlayerId {
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ package me.arcanis.ffxivbis.models
|
|||||||
|
|
||||||
case class PlayerIdWithCounters(
|
case class PlayerIdWithCounters(
|
||||||
partyId: String,
|
partyId: String,
|
||||||
job: Job.Job,
|
job: Job,
|
||||||
nick: String,
|
nick: String,
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
priority: Int,
|
priority: Int,
|
||||||
@ -24,8 +24,6 @@ case class PlayerIdWithCounters(
|
|||||||
def gt(that: PlayerIdWithCounters, orderBy: Seq[String]): Boolean =
|
def gt(that: PlayerIdWithCounters, orderBy: Seq[String]): Boolean =
|
||||||
withCounters(orderBy) > that.withCounters(orderBy)
|
withCounters(orderBy) > that.withCounters(orderBy)
|
||||||
|
|
||||||
def isRequiredToString: String = if (isRequired) "yes" else "no"
|
|
||||||
|
|
||||||
def playerId: PlayerId = PlayerId(partyId, job, nick)
|
def playerId: PlayerId = PlayerId(partyId, job, nick)
|
||||||
|
|
||||||
private val counters: Map[String, Int] = Map(
|
private val counters: Map[String, Int] = Map(
|
||||||
@ -47,13 +45,13 @@ object PlayerIdWithCounters {
|
|||||||
|
|
||||||
def >(that: PlayerCountersComparator): Boolean = {
|
def >(that: PlayerCountersComparator): Boolean = {
|
||||||
@scala.annotation.tailrec
|
@scala.annotation.tailrec
|
||||||
def compareLists(left: List[Int], right: List[Int]): Boolean =
|
def compare(left: Seq[Int], right: Seq[Int]): Boolean =
|
||||||
(left, right) match {
|
(left, right) match {
|
||||||
case (hl :: tl, hr :: tr) => if (hl == hr) compareLists(tl, tr) else hl > hr
|
case (hl :: tl, hr :: tr) => if (hl == hr) compare(tl, tr) else hl > hr
|
||||||
case (_ :: _, Nil) => true
|
case (_ :: _, Nil) => true
|
||||||
case (_, _) => false
|
case (_, _) => false
|
||||||
}
|
}
|
||||||
compareLists(values.toList, that.values.toList)
|
compare(values, that.values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ package me.arcanis.ffxivbis.models
|
|||||||
import org.mindrot.jbcrypt.BCrypt
|
import org.mindrot.jbcrypt.BCrypt
|
||||||
|
|
||||||
object Permission extends Enumeration {
|
object Permission extends Enumeration {
|
||||||
|
|
||||||
val get, post, admin = Value
|
val get, post, admin = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,23 +36,24 @@ class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMes
|
|||||||
override def onMessage(msg: Message): Behavior[Message] = handle(Map.empty)(msg)
|
override def onMessage(msg: Message): Behavior[Message] = handle(Map.empty)(msg)
|
||||||
|
|
||||||
private def handle(cache: Map[String, Party]): Message.Handler = {
|
private def handle(cache: Map[String, Party]): Message.Handler = {
|
||||||
case ForgetParty(partyId) =>
|
case ControlMessage.ForgetParty(partyId) =>
|
||||||
Behaviors.receiveMessage(handle(cache - partyId))
|
Behaviors.receiveMessage(handle(cache - partyId))
|
||||||
|
|
||||||
case GetNewPartyId(client) =>
|
case ControlMessage.GetNewPartyId(client) =>
|
||||||
getPartyId.foreach(client ! _)
|
getPartyId.foreach(client ! _)
|
||||||
Behaviors.same
|
Behaviors.same
|
||||||
|
|
||||||
case StoreParty(partyId, party) =>
|
case ControlMessage.StoreParty(partyId, party) =>
|
||||||
Behaviors.receiveMessage(handle(cache.updated(partyId, party)))
|
Behaviors.receiveMessage(handle(cache.updated(partyId, party)))
|
||||||
|
|
||||||
case GetParty(partyId, client) =>
|
case DatabaseMessage.GetParty(partyId, client) =>
|
||||||
val party = cache.get(partyId) match {
|
val party = cache.get(partyId) match {
|
||||||
case Some(party) => Future.successful(party)
|
case Some(party) => Future.successful(party)
|
||||||
case None =>
|
case None =>
|
||||||
storage.ask(ref => GetParty(partyId, ref)).map { party =>
|
storage.ask(ref => DatabaseMessage.GetParty(partyId, ref)).map { party =>
|
||||||
context.self ! StoreParty(partyId, party)
|
context.self ! ControlMessage.StoreParty(partyId, party)
|
||||||
context.system.scheduler.scheduleOnce(cacheTimeout, () => context.self ! ForgetParty(partyId))
|
context.system.scheduler
|
||||||
|
.scheduleOnce(cacheTimeout, () => context.self ! ControlMessage.ForgetParty(partyId))
|
||||||
party
|
party
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +68,7 @@ class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMes
|
|||||||
|
|
||||||
private def getPartyId: Future[String] = {
|
private def getPartyId: Future[String] = {
|
||||||
val partyId = Party.randomPartyId
|
val partyId = Party.randomPartyId
|
||||||
storage.ask(ref => Exists(partyId, ref)).flatMap {
|
storage.ask(ref => DatabaseMessage.Exists(partyId, ref)).flatMap {
|
||||||
case true => getPartyId
|
case true => getPartyId
|
||||||
case false => Future.successful(partyId)
|
case false => Future.successful(partyId)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
|
|||||||
import akka.actor.typed.{Behavior, PostStop, Signal}
|
import akka.actor.typed.{Behavior, PostStop, Signal}
|
||||||
import akka.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
import com.typesafe.scalalogging.StrictLogging
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
import me.arcanis.ffxivbis.messages.{BiSProviderMessage, DownloadBiS}
|
import me.arcanis.ffxivbis.messages.BiSProviderMessage
|
||||||
import me.arcanis.ffxivbis.models.{BiS, Job, Piece, PieceType}
|
import me.arcanis.ffxivbis.models.{BiS, Job, Piece, PieceType}
|
||||||
import me.arcanis.ffxivbis.service.bis.parser.Parser
|
import me.arcanis.ffxivbis.service.bis.parser.Parser
|
||||||
import me.arcanis.ffxivbis.service.bis.parser.impl.{Ariyala, Etro}
|
import me.arcanis.ffxivbis.service.bis.parser.impl.{Ariyala, Etro}
|
||||||
@ -21,6 +21,7 @@ import spray.json._
|
|||||||
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import scala.util.control.NonFatal
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
class BisProvider(context: ActorContext[BiSProviderMessage])
|
class BisProvider(context: ActorContext[BiSProviderMessage])
|
||||||
@ -32,7 +33,7 @@ class BisProvider(context: ActorContext[BiSProviderMessage])
|
|||||||
|
|
||||||
override def onMessage(msg: BiSProviderMessage): Behavior[BiSProviderMessage] =
|
override def onMessage(msg: BiSProviderMessage): Behavior[BiSProviderMessage] =
|
||||||
msg match {
|
msg match {
|
||||||
case DownloadBiS(link, job, client) =>
|
case BiSProviderMessage.DownloadBiS(link, job, client) =>
|
||||||
get(link, job).onComplete {
|
get(link, job).onComplete {
|
||||||
case Success(items) => client ! BiS(items)
|
case Success(items) => client ! BiS(items)
|
||||||
case Failure(exception) =>
|
case Failure(exception) =>
|
||||||
@ -46,7 +47,7 @@ class BisProvider(context: ActorContext[BiSProviderMessage])
|
|||||||
Behaviors.same
|
Behaviors.same
|
||||||
}
|
}
|
||||||
|
|
||||||
private def get(link: String, job: Job.Job): Future[Seq[Piece]] =
|
private def get(link: String, job: Job): Future[Seq[Piece]] =
|
||||||
try {
|
try {
|
||||||
val url = Uri(link)
|
val url = Uri(link)
|
||||||
val id = Paths.get(link).normalize.getFileName.toString
|
val id = Paths.get(link).normalize.getFileName.toString
|
||||||
@ -55,7 +56,7 @@ class BisProvider(context: ActorContext[BiSProviderMessage])
|
|||||||
val uri = parser.uri(url, id)
|
val uri = parser.uri(url, id)
|
||||||
sendRequest(uri, BisProvider.parseBisJsonToPieces(job, parser, getPieceType))
|
sendRequest(uri, BisProvider.parseBisJsonToPieces(job, parser, getPieceType))
|
||||||
} catch {
|
} catch {
|
||||||
case exception: Exception => Future.failed(exception)
|
case NonFatal(exception) => Future.failed(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,9 +66,9 @@ object BisProvider {
|
|||||||
Behaviors.setup[BiSProviderMessage](context => new BisProvider(context))
|
Behaviors.setup[BiSProviderMessage](context => new BisProvider(context))
|
||||||
|
|
||||||
private def parseBisJsonToPieces(
|
private def parseBisJsonToPieces(
|
||||||
job: Job.Job,
|
job: Job,
|
||||||
idParser: Parser,
|
idParser: Parser,
|
||||||
pieceTypes: Seq[Long] => Future[Map[Long, PieceType.PieceType]]
|
pieceTypes: Seq[Long] => Future[Map[Long, PieceType]]
|
||||||
)(js: JsObject)(implicit executionContext: ExecutionContext): Future[Seq[Piece]] =
|
)(js: JsObject)(implicit executionContext: ExecutionContext): Future[Seq[Piece]] =
|
||||||
idParser.parse(job, js).flatMap { pieces =>
|
idParser.parse(job, js).flatMap { pieces =>
|
||||||
pieceTypes(pieces.values.toSeq).map { types =>
|
pieceTypes(pieces.values.toSeq).map { types =>
|
||||||
|
@ -22,7 +22,7 @@ trait XivApi extends RequestExecutor {
|
|||||||
private val xivapiUrl = config.getString("me.arcanis.ffxivbis.bis-provider.xivapi-url")
|
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
|
private val xivapiKey = Try(config.getString("me.arcanis.ffxivbis.bis-provider.xivapi-key")).toOption
|
||||||
|
|
||||||
private val preloadedItems: Map[Long, PieceType.PieceType] =
|
private val preloadedItems: Map[Long, PieceType] =
|
||||||
config
|
config
|
||||||
.getConfigList("me.arcanis.ffxivbis.bis-provider.cached-items")
|
.getConfigList("me.arcanis.ffxivbis.bis-provider.cached-items")
|
||||||
.asScala
|
.asScala
|
||||||
@ -31,17 +31,16 @@ trait XivApi extends RequestExecutor {
|
|||||||
}
|
}
|
||||||
.toMap
|
.toMap
|
||||||
|
|
||||||
def getPieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType.PieceType]] = {
|
def getPieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType]] = {
|
||||||
val (local, remote) = itemIds.foldLeft((Map.empty[Long, PieceType.PieceType], Seq.empty[Long])) {
|
val (local, remote) = itemIds.foldLeft((Map.empty[Long, PieceType], Seq.empty[Long])) { case ((l, r), id) =>
|
||||||
case ((l, r), id) =>
|
if (preloadedItems.contains(id)) (l.updated(id, preloadedItems(id)), r)
|
||||||
if (preloadedItems.contains(id)) (l.updated(id, preloadedItems(id)), r)
|
else (l, r :+ id)
|
||||||
else (l, r :+ id)
|
|
||||||
}
|
}
|
||||||
if (remote.isEmpty) Future.successful(local)
|
if (remote.isEmpty) Future.successful(local)
|
||||||
else remotePieceType(remote).map(_ ++ local)
|
else remotePieceType(remote).map(_ ++ local)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def remotePieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType.PieceType]] = {
|
private def remotePieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType]] = {
|
||||||
val uriForItems = Uri(xivapiUrl)
|
val uriForItems = Uri(xivapiUrl)
|
||||||
.withPath(Uri.Path / "item")
|
.withPath(Uri.Path / "item")
|
||||||
.withQuery(
|
.withQuery(
|
||||||
@ -111,7 +110,7 @@ object XivApi {
|
|||||||
|
|
||||||
private def parseXivapiJsonToType(
|
private def parseXivapiJsonToType(
|
||||||
shops: Map[Long, (String, Long)]
|
shops: Map[Long, (String, Long)]
|
||||||
)(js: JsObject)(implicit executionContext: ExecutionContext): Future[Map[Long, PieceType.PieceType]] =
|
)(js: JsObject)(implicit executionContext: ExecutionContext): Future[Map[Long, PieceType]] =
|
||||||
Future {
|
Future {
|
||||||
val shopMap = js.fields("Results") match {
|
val shopMap = js.fields("Results") match {
|
||||||
case array: JsArray =>
|
case array: JsArray =>
|
||||||
|
@ -17,7 +17,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
|
|
||||||
trait Parser extends StrictLogging {
|
trait Parser extends StrictLogging {
|
||||||
|
|
||||||
def parse(job: Job.Job, js: JsObject)(implicit executionContext: ExecutionContext): Future[Map[String, Long]]
|
def parse(job: Job, js: JsObject)(implicit executionContext: ExecutionContext): Future[Map[String, Long]]
|
||||||
|
|
||||||
def uri(root: Uri, id: String): Uri
|
def uri(root: Uri, id: String): Uri
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
|
|
||||||
object Ariyala extends Parser {
|
object Ariyala extends Parser {
|
||||||
|
|
||||||
override def parse(job: Job.Job, js: JsObject)(implicit
|
override def parse(job: Job, js: JsObject)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Map[String, Long]] =
|
): Future[Map[String, Long]] =
|
||||||
Future {
|
Future {
|
||||||
|
@ -18,7 +18,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
|
|
||||||
object Etro extends Parser {
|
object Etro extends Parser {
|
||||||
|
|
||||||
override def parse(job: Job.Job, js: JsObject)(implicit
|
override def parse(job: Job, js: JsObject)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Map[String, Long]] =
|
): Future[Map[String, Long]] =
|
||||||
Future {
|
Future {
|
||||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
|||||||
|
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage
|
||||||
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.service.database.Database
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
|
|
||||||
trait DatabaseBiSHandler { this: Database =>
|
trait DatabaseBiSHandler { this: Database =>
|
||||||
|
@ -11,7 +11,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
|||||||
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext}
|
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext}
|
||||||
import akka.actor.typed.{Behavior, DispatcherSelector}
|
import akka.actor.typed.{Behavior, DispatcherSelector}
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.messages.{BisDatabaseMessage, DatabaseMessage, LootDatabaseMessage, PartyDatabaseMessage, UserDatabaseMessage}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage
|
||||||
|
import me.arcanis.ffxivbis.messages.DatabaseMessage.{BisDatabaseMessage, LootDatabaseMessage, PartyDatabaseMessage, UserDatabaseMessage}
|
||||||
import me.arcanis.ffxivbis.service.database.Database
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
import me.arcanis.ffxivbis.storage.DatabaseProfile
|
import me.arcanis.ffxivbis.storage.DatabaseProfile
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
|||||||
|
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage
|
||||||
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.models.Loot
|
import me.arcanis.ffxivbis.models.Loot
|
||||||
import me.arcanis.ffxivbis.service.database.Database
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
|||||||
|
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage
|
||||||
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.models.{BiS, Player}
|
import me.arcanis.ffxivbis.models.{BiS, Player}
|
||||||
import me.arcanis.ffxivbis.service.database.Database
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
|||||||
|
|
||||||
import akka.actor.typed.Behavior
|
import akka.actor.typed.Behavior
|
||||||
import akka.actor.typed.scaladsl.Behaviors
|
import akka.actor.typed.scaladsl.Behaviors
|
||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages.DatabaseMessage
|
||||||
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.service.database.Database
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
|
|
||||||
trait DatabaseUserHandler { this: Database =>
|
trait DatabaseUserHandler { this: Database =>
|
||||||
|
@ -16,6 +16,7 @@ import me.arcanis.ffxivbis.models.{Loot, Piece, PlayerId}
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.sql.DataSource
|
import javax.sql.DataSource
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
class DatabaseProfile(override val executionContext: ExecutionContext, config: Config)
|
class DatabaseProfile(override val executionContext: ExecutionContext, config: Config)
|
||||||
extends StrictLogging
|
extends StrictLogging
|
||||||
@ -31,7 +32,7 @@ class DatabaseProfile(override val executionContext: ExecutionContext, config: C
|
|||||||
val dataSourceConfig = DatabaseConnection.getDataSourceConfig(profile)
|
val dataSourceConfig = DatabaseConnection.getDataSourceConfig(profile)
|
||||||
new HikariDataSource(dataSourceConfig)
|
new HikariDataSource(dataSourceConfig)
|
||||||
} catch {
|
} catch {
|
||||||
case exception: Exception =>
|
case NonFatal(exception) =>
|
||||||
logger.error("exception during storage initialization", exception)
|
logger.error("exception during storage initialization", exception)
|
||||||
throw exception
|
throw exception
|
||||||
}
|
}
|
||||||
|
16
src/test/scala/me/arcanis/ffxivbis/ApplicationTest.scala
Normal file
16
src/test/scala/me/arcanis/ffxivbis/ApplicationTest.scala
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package me.arcanis.ffxivbis
|
||||||
|
|
||||||
|
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class ApplicationTest extends ScalaTestWithActorTestKit(Settings.withRandomDatabase)
|
||||||
|
with AnyWordSpecLike {
|
||||||
|
|
||||||
|
"application" must {
|
||||||
|
|
||||||
|
"load" in {
|
||||||
|
testKit.spawn[Nothing](Application())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
20
src/test/scala/me/arcanis/ffxivbis/ConfigurationTest.scala
Normal file
20
src/test/scala/me/arcanis/ffxivbis/ConfigurationTest.scala
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package me.arcanis.ffxivbis
|
||||||
|
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class ConfigurationTest extends AnyWordSpecLike with Matchers {
|
||||||
|
|
||||||
|
private val requiredPaths =
|
||||||
|
Seq("akka.http.server.transparent-head-requests")
|
||||||
|
|
||||||
|
"configuration helper" must {
|
||||||
|
|
||||||
|
requiredPaths.foreach { path =>
|
||||||
|
s"has $path propery" in {
|
||||||
|
Configuration.load().hasPath(path) shouldBe true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -6,49 +6,50 @@ import me.arcanis.ffxivbis.models._
|
|||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
||||||
object Fixtures {
|
object Fixtures {
|
||||||
|
|
||||||
lazy val bis: BiS = BiS(
|
lazy val bis: BiS = BiS(
|
||||||
Seq(
|
Seq(
|
||||||
Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
Piece.Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
||||||
Head(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Head(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Body(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Body(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Hands(pieceType = PieceType.Tome, Job.DNC),
|
Piece.Hands(pieceType = PieceType.Tome, Job.DNC),
|
||||||
Legs(pieceType = PieceType.Tome, Job.DNC),
|
Piece.Legs(pieceType = PieceType.Tome, Job.DNC),
|
||||||
Feet(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Feet(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Ears(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Ears(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Neck(pieceType = PieceType.Tome, Job.DNC),
|
Piece.Neck(pieceType = PieceType.Tome, Job.DNC),
|
||||||
Wrist(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Wrist(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
Piece.Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
||||||
Ring(pieceType = PieceType.Tome, Job.DNC, "right ring")
|
Piece.Ring(pieceType = PieceType.Tome, Job.DNC, "right ring")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
lazy val bis2: BiS = BiS(
|
lazy val bis2: BiS = BiS(
|
||||||
Seq(
|
Seq(
|
||||||
Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
Piece.Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
||||||
Head(pieceType = PieceType.Tome, Job.DNC),
|
Piece.Head(pieceType = PieceType.Tome, Job.DNC),
|
||||||
Body(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Body(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Hands(pieceType = PieceType.Tome, Job.DNC),
|
Piece.Hands(pieceType = PieceType.Tome, Job.DNC),
|
||||||
Legs(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Legs(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Feet(pieceType = PieceType.Tome, Job.DNC),
|
Piece.Feet(pieceType = PieceType.Tome, Job.DNC),
|
||||||
Ears(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Ears(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Neck(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Neck(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Wrist(pieceType = PieceType.Savage, Job.DNC),
|
Piece.Wrist(pieceType = PieceType.Savage, Job.DNC),
|
||||||
Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
Piece.Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
||||||
Ring(pieceType = PieceType.Savage, Job.DNC, "right ring")
|
Piece.Ring(pieceType = PieceType.Savage, Job.DNC, "right ring")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
lazy val bis3: BiS = BiS(
|
lazy val bis3: BiS = BiS(
|
||||||
Seq(
|
Seq(
|
||||||
Weapon(pieceType = PieceType.Savage ,Job.SGE),
|
Piece.Weapon(pieceType = PieceType.Savage ,Job.SGE),
|
||||||
Head(pieceType = PieceType.Tome, Job.SGE),
|
Piece.Head(pieceType = PieceType.Tome, Job.SGE),
|
||||||
Body(pieceType = PieceType.Savage, Job.SGE),
|
Piece.Body(pieceType = PieceType.Savage, Job.SGE),
|
||||||
Hands(pieceType = PieceType.Tome, Job.SGE),
|
Piece.Hands(pieceType = PieceType.Tome, Job.SGE),
|
||||||
Legs(pieceType = PieceType.Tome, Job.SGE),
|
Piece.Legs(pieceType = PieceType.Tome, Job.SGE),
|
||||||
Feet(pieceType = PieceType.Savage, Job.SGE),
|
Piece.Feet(pieceType = PieceType.Savage, Job.SGE),
|
||||||
Ears(pieceType = PieceType.Savage, Job.SGE),
|
Piece.Ears(pieceType = PieceType.Savage, Job.SGE),
|
||||||
Neck(pieceType = PieceType.Tome, Job.SGE),
|
Piece.Neck(pieceType = PieceType.Tome, Job.SGE),
|
||||||
Wrist(pieceType = PieceType.Savage, Job.SGE),
|
Piece.Wrist(pieceType = PieceType.Savage, Job.SGE),
|
||||||
Ring(pieceType = PieceType.Savage, Job.SGE, "left ring"),
|
Piece.Ring(pieceType = PieceType.Savage, Job.SGE, "left ring"),
|
||||||
Ring(pieceType = PieceType.Tome, Job.SGE, "right ring")
|
Piece.Ring(pieceType = PieceType.Tome, Job.SGE, "right ring")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,16 +59,16 @@ object Fixtures {
|
|||||||
lazy val link4: String = "https://etro.gg/gearset/865fc886-994f-4c28-8fc1-4379f160a916"
|
lazy val link4: String = "https://etro.gg/gearset/865fc886-994f-4c28-8fc1-4379f160a916"
|
||||||
lazy val link5: String = "https://ffxiv.ariyala.com/1FGU0"
|
lazy val link5: String = "https://ffxiv.ariyala.com/1FGU0"
|
||||||
|
|
||||||
lazy val lootWeapon: Piece = Weapon(pieceType = PieceType.Tome, Job.AnyJob)
|
lazy val lootWeapon: Piece = Piece.Weapon(pieceType = PieceType.Tome, Job.AnyJob)
|
||||||
lazy val lootBody: Piece = Body(pieceType = PieceType.Savage, Job.AnyJob)
|
lazy val lootBody: Piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)
|
||||||
lazy val lootBodyCrafted: Piece = Body(pieceType = PieceType.Crafted, Job.AnyJob)
|
lazy val lootBodyCrafted: Piece = Piece.Body(pieceType = PieceType.Crafted, Job.AnyJob)
|
||||||
lazy val lootHands: Piece = Hands(pieceType = PieceType.Tome, Job.AnyJob)
|
lazy val lootHands: Piece = Piece.Hands(pieceType = PieceType.Tome, Job.AnyJob)
|
||||||
lazy val lootLegs: Piece = Legs(pieceType = PieceType.Savage, Job.AnyJob)
|
lazy val lootLegs: Piece = Piece.Legs(pieceType = PieceType.Savage, Job.AnyJob)
|
||||||
lazy val lootEars: Piece = Ears(pieceType = PieceType.Savage, Job.AnyJob)
|
lazy val lootEars: Piece = Piece.Ears(pieceType = PieceType.Savage, Job.AnyJob)
|
||||||
lazy val lootRing: Piece = Ring(pieceType = PieceType.Tome, Job.AnyJob)
|
lazy val lootRing: Piece = Piece.Ring(pieceType = PieceType.Tome, Job.AnyJob)
|
||||||
lazy val lootLeftRing: Piece = Ring(pieceType = PieceType.Tome, Job.AnyJob, "left ring")
|
lazy val lootLeftRing: Piece = Piece.Ring(pieceType = PieceType.Tome, Job.AnyJob, "left ring")
|
||||||
lazy val lootRightRing: Piece = Ring(pieceType = PieceType.Tome, Job.AnyJob, "right ring")
|
lazy val lootRightRing: Piece = Piece.Ring(pieceType = PieceType.Tome, Job.AnyJob, "right ring")
|
||||||
lazy val lootUpgrade: Piece = BodyUpgrade
|
lazy val lootUpgrade: Piece = Piece.BodyUpgrade
|
||||||
lazy val loot: Seq[Piece] = Seq(lootBody, lootHands, lootLegs, lootUpgrade)
|
lazy val loot: Seq[Piece] = Seq(lootBody, lootHands, lootLegs, lootUpgrade)
|
||||||
|
|
||||||
lazy val partyId: String = Party.randomPartyId
|
lazy val partyId: String = Party.randomPartyId
|
||||||
@ -84,5 +85,5 @@ object Fixtures {
|
|||||||
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
||||||
|
|
||||||
lazy val authProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(Some(userAdmin))
|
lazy val authProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(Some(userAdmin))
|
||||||
lazy val rejectingProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(None)
|
lazy val rejectingAuthProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(None)
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,12 @@ import com.typesafe.config.{Config, ConfigFactory, ConfigValueFactory}
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object Settings {
|
object Settings {
|
||||||
|
|
||||||
def config(values: Map[String, AnyRef]): Config = {
|
def config(values: Map[String, AnyRef]): Config = {
|
||||||
@scala.annotation.tailrec
|
@scala.annotation.tailrec
|
||||||
def replace(acc: Config, iter: List[(String, AnyRef)]): Config = iter match {
|
def replace(config: Config, iter: List[(String, AnyRef)]): Config = iter match {
|
||||||
case Nil => acc
|
case Nil => config
|
||||||
case (key -> value) :: tail => replace(acc.withValue(key, ConfigValueFactory.fromAnyRef(value)), tail)
|
case (key -> value) :: tail => replace(config.withValue(key, ConfigValueFactory.fromAnyRef(value)), tail)
|
||||||
}
|
}
|
||||||
|
|
||||||
val default = ConfigFactory.load()
|
val default = ConfigFactory.load()
|
||||||
@ -23,7 +24,9 @@ object Settings {
|
|||||||
if (databaseFile.exists)
|
if (databaseFile.exists)
|
||||||
databaseFile.delete()
|
databaseFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
def randomDatabasePath: String = File.createTempFile("ffxivdb-",".db").toPath.toString
|
def randomDatabasePath: String = File.createTempFile("ffxivdb-",".db").toPath.toString
|
||||||
|
|
||||||
def withRandomDatabase: Config =
|
def withRandomDatabase: Config =
|
||||||
config(Map("me.arcanis.ffxivbis.database.sqlite.jdbcUrl" -> s"jdbc:sqlite:$randomDatabasePath"))
|
config(Map("me.arcanis.ffxivbis.database.sqlite.jdbcUrl" -> s"jdbc:sqlite:$randomDatabasePath"))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.arcanis.ffxivbis.http
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
|
||||||
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
import akka.http.scaladsl.server.Route
|
import akka.http.scaladsl.server.Route
|
||||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
import me.arcanis.ffxivbis.Fixtures
|
import me.arcanis.ffxivbis.Fixtures
|
||||||
@ -25,7 +25,7 @@ class AuthorizationTest extends AnyWordSpecLike with Matchers with ScalatestRout
|
|||||||
}
|
}
|
||||||
|
|
||||||
"reject credentials" in {
|
"reject credentials" in {
|
||||||
val route = new RootView(Fixtures.rejectingProvider).routes
|
val route = new RootView(Fixtures.rejectingAuthProvider).routes
|
||||||
|
|
||||||
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> Route.seal(route) ~> check {
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> Route.seal(route) ~> check {
|
||||||
status shouldEqual StatusCodes.Unauthorized
|
status shouldEqual StatusCodes.Unauthorized
|
||||||
|
@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
|||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage.{AddPlayer, AddUser}
|
||||||
import me.arcanis.ffxivbis.models.{BiS, Job}
|
import me.arcanis.ffxivbis.models.{BiS, Job}
|
||||||
import me.arcanis.ffxivbis.service.PartyService
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
import me.arcanis.ffxivbis.service.bis.BisProvider
|
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||||
@ -53,15 +53,6 @@ class BiSEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteT
|
|||||||
super.afterAll()
|
super.afterAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def compareBiSResponse(actual: PlayerModel, expected: PlayerModel): Unit = {
|
|
||||||
actual.partyId shouldEqual expected.partyId
|
|
||||||
actual.nick shouldEqual expected.nick
|
|
||||||
actual.job shouldEqual expected.job
|
|
||||||
Compare.seqEquals(actual.bis.get, expected.bis.get) shouldEqual true
|
|
||||||
actual.link shouldEqual expected.link
|
|
||||||
actual.priority shouldEqual expected.priority
|
|
||||||
}
|
|
||||||
|
|
||||||
"api v1 bis endpoint" must {
|
"api v1 bis endpoint" must {
|
||||||
|
|
||||||
"create best in slot set from ariyala" in {
|
"create best in slot set from ariyala" in {
|
||||||
@ -216,4 +207,13 @@ class BiSEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteT
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def compareBiSResponse(actual: PlayerModel, expected: PlayerModel): Unit = {
|
||||||
|
actual.partyId shouldEqual expected.partyId
|
||||||
|
actual.nick shouldEqual expected.nick
|
||||||
|
actual.job shouldEqual expected.job
|
||||||
|
Compare.seqEquals(actual.bis.get, expected.bis.get) shouldEqual true
|
||||||
|
actual.link shouldEqual expected.link
|
||||||
|
actual.priority shouldEqual expected.priority
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package me.arcanis.ffxivbis.http.api.v1
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.StatusCodes
|
||||||
|
import akka.http.scaladsl.model.headers.Allow
|
||||||
|
import akka.http.scaladsl.server.Directives.{path, _}
|
||||||
|
import akka.http.scaladsl.server.Route
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class HttpHandlerTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest with JsonSupport with HttpHandler {
|
||||||
|
|
||||||
|
"http handler" must {
|
||||||
|
|
||||||
|
"convert IllegalArgumentException into 400 response" in {
|
||||||
|
Get("/400") ~> withExceptionHandler("400", failWith(new IllegalArgumentException(""))) ~> check {
|
||||||
|
status shouldEqual StatusCodes.BadRequest
|
||||||
|
responseAs[ErrorModel] shouldEqual ErrorModel("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"convert IllegalArgumentException into 400 response with details" in {
|
||||||
|
Get("/400") ~> withExceptionHandler("400", failWith(new IllegalArgumentException("message"))) ~> check {
|
||||||
|
status shouldEqual StatusCodes.BadRequest
|
||||||
|
responseAs[ErrorModel] shouldEqual ErrorModel("message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"convert exception message to error response" in {
|
||||||
|
Get("/500") ~> withExceptionHandler("500", failWith(new ArithmeticException)) ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
responseAs[ErrorModel] shouldEqual ErrorModel("unknown server error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"process OPTIONS request" in {
|
||||||
|
Options("/200") ~> withRejectionHandler() ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
headers.collectFirst { case header: Allow => header } should not be empty
|
||||||
|
responseAs[String] shouldBe empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"reject unknown request" in {
|
||||||
|
Post("/200") ~> withRejectionHandler() ~> check {
|
||||||
|
status shouldEqual StatusCodes.MethodNotAllowed
|
||||||
|
headers.collectFirst { case header: Allow => header } should not be empty
|
||||||
|
responseAs[ErrorModel].message should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"handle 404 response" in {
|
||||||
|
Get("/404") ~> withRejectionHandler() ~> check {
|
||||||
|
status shouldEqual StatusCodes.NotFound
|
||||||
|
responseAs[ErrorModel] shouldEqual ErrorModel("The requested resource could not be found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private def single(uri: String, completeWith: Route) =
|
||||||
|
path(uri)(get(completeWith))
|
||||||
|
|
||||||
|
private def withExceptionHandler(uri: String = "200", completeWith: Route = complete(StatusCodes.OK)) =
|
||||||
|
Route.seal {
|
||||||
|
handleExceptions(exceptionHandler) {
|
||||||
|
single(uri, completeWith)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def withRejectionHandler(uri: String = "200", completeWith: Route = complete(StatusCodes.OK)) =
|
||||||
|
Route.seal {
|
||||||
|
handleRejections(rejectionHandler) {
|
||||||
|
single(uri, completeWith)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
|||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage.{AddPlayer, AddUser}
|
||||||
import me.arcanis.ffxivbis.service.PartyService
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
import me.arcanis.ffxivbis.service.database.{Database, Migration}
|
import me.arcanis.ffxivbis.service.database.{Database, Migration}
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
|
@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
|||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.messages.AddUser
|
import me.arcanis.ffxivbis.messages.DatabaseMessage.AddUser
|
||||||
import me.arcanis.ffxivbis.models.PartyDescription
|
import me.arcanis.ffxivbis.models.PartyDescription
|
||||||
import me.arcanis.ffxivbis.service.PartyService
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
import me.arcanis.ffxivbis.service.bis.BisProvider
|
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||||
|
@ -7,9 +7,8 @@ import akka.http.scaladsl.model.{StatusCodes, Uri}
|
|||||||
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis
|
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage.{AddPlayer, AddUser}
|
||||||
import me.arcanis.ffxivbis.service.PartyService
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
import me.arcanis.ffxivbis.service.bis.BisProvider
|
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||||
import me.arcanis.ffxivbis.service.database.{Database, Migration}
|
import me.arcanis.ffxivbis.service.database.{Database, Migration}
|
||||||
|
@ -46,7 +46,7 @@ class BiSTest extends AnyWordSpecLike with Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"return upgrade list" in {
|
"return upgrade list" in {
|
||||||
Compare.mapEquals(Fixtures.bis.upgrades, Map[PieceUpgrade, Int](BodyUpgrade -> 2, AccessoryUpgrade -> 3)) shouldEqual true
|
Compare.mapEquals(Fixtures.bis.upgrades, Map[Piece.PieceUpgrade, Int](Piece.BodyUpgrade -> 2, Piece.AccessoryUpgrade -> 3)) shouldEqual true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ class PieceTest extends AnyWordSpecLike with Matchers {
|
|||||||
"piece model" must {
|
"piece model" must {
|
||||||
|
|
||||||
"return upgrade" in {
|
"return upgrade" in {
|
||||||
Fixtures.lootWeapon.upgrade shouldEqual Some(WeaponUpgrade)
|
Fixtures.lootWeapon.upgrade shouldEqual Some(Piece.WeaponUpgrade)
|
||||||
Fixtures.lootBody.upgrade shouldEqual None
|
Fixtures.lootBody.upgrade shouldEqual None
|
||||||
Fixtures.lootHands.upgrade shouldEqual Some(BodyUpgrade)
|
Fixtures.lootHands.upgrade shouldEqual Some(Piece.BodyUpgrade)
|
||||||
Fixtures.lootLegs.upgrade shouldEqual None
|
Fixtures.lootLegs.upgrade shouldEqual None
|
||||||
Fixtures.lootEars.upgrade shouldEqual None
|
Fixtures.lootEars.upgrade shouldEqual None
|
||||||
Fixtures.lootLeftRing.upgrade shouldEqual Some(AccessoryUpgrade)
|
Fixtures.lootLeftRing.upgrade shouldEqual Some(Piece.AccessoryUpgrade)
|
||||||
Fixtures.lootRightRing.upgrade shouldEqual Some(AccessoryUpgrade)
|
Fixtures.lootRightRing.upgrade shouldEqual Some(Piece.AccessoryUpgrade)
|
||||||
}
|
}
|
||||||
|
|
||||||
"build piece from string" in {
|
"build piece from string" in {
|
||||||
|
@ -2,7 +2,7 @@ package me.arcanis.ffxivbis.service
|
|||||||
|
|
||||||
import akka.actor.testkit.typed.scaladsl.ActorTestKit
|
import akka.actor.testkit.typed.scaladsl.ActorTestKit
|
||||||
import akka.actor.typed.scaladsl.AskPattern.Askable
|
import akka.actor.typed.scaladsl.AskPattern.Askable
|
||||||
import me.arcanis.ffxivbis.messages.DownloadBiS
|
import me.arcanis.ffxivbis.messages.BiSProviderMessage.DownloadBiS
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.service.bis.BisProvider
|
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
@ -28,10 +28,10 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
|
|||||||
val testKit = ActorTestKit(Settings.withRandomDatabase)
|
val testKit = ActorTestKit(Settings.withRandomDatabase)
|
||||||
val provider = testKit.spawn(BisProvider())
|
val provider = testKit.spawn(BisProvider())
|
||||||
|
|
||||||
val dncSet = Await.result(provider.ask(DownloadBiS(Fixtures.link, Job.DNC, _) )(timeout, testKit.scheduler), timeout)
|
val dncSet = Await.result(provider.ask(DownloadBiS(Fixtures.link, Job.DNC, _))(timeout, testKit.scheduler), timeout)
|
||||||
dnc = dnc.withBiS(Some(dncSet))
|
dnc = dnc.withBiS(Some(dncSet))
|
||||||
|
|
||||||
val drgSet = Await.result(provider.ask(DownloadBiS(Fixtures.link2, Job.DRG, _) )(timeout, testKit.scheduler), timeout)
|
val drgSet = Await.result(provider.ask(DownloadBiS(Fixtures.link2, Job.DRG, _))(timeout, testKit.scheduler), timeout)
|
||||||
drg = drg.withBiS(Some(drgSet))
|
drg = drg.withBiS(Some(drgSet))
|
||||||
|
|
||||||
default = default.withPlayer(dnc).withPlayer(drg)
|
default = default.withPlayer(dnc).withPlayer(drg)
|
||||||
@ -43,11 +43,11 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
|
|||||||
"loot selector" must {
|
"loot selector" must {
|
||||||
|
|
||||||
"suggest loot by isRequired" in {
|
"suggest loot by isRequired" in {
|
||||||
toPlayerId(default.suggestLoot(Head(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(dnc.playerId, drg.playerId)
|
toPlayerId(default.suggestLoot(Piece.Head(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(dnc.playerId, drg.playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"suggest loot if a player already have it" in {
|
"suggest loot if a player already have it" in {
|
||||||
val piece = Body(pieceType = PieceType.Savage, Job.AnyJob)
|
val piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)
|
||||||
val party = default.withPlayer(dnc.withLoot(piece))
|
val party = default.withPlayer(dnc.withLoot(piece))
|
||||||
|
|
||||||
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||||
@ -56,34 +56,34 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
|
|||||||
"suggest upgrade" in {
|
"suggest upgrade" in {
|
||||||
val party = default.withPlayer(
|
val party = default.withPlayer(
|
||||||
dnc.withBiS(
|
dnc.withBiS(
|
||||||
Some(dnc.bis.withPiece(Weapon(pieceType = PieceType.Tome, Job.DNC)))
|
Some(dnc.bis.withPiece(Piece.Weapon(pieceType = PieceType.Tome, Job.DNC)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
toPlayerId(party.suggestLoot(WeaponUpgrade)) shouldEqual Seq(dnc.playerId, drg.playerId)
|
toPlayerId(party.suggestLoot(Piece.WeaponUpgrade)) shouldEqual Seq(dnc.playerId, drg.playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"suggest loot by priority" in {
|
"suggest loot by priority" in {
|
||||||
val party = default.withPlayer(dnc.copy(priority = 2))
|
val party = default.withPlayer(dnc.copy(priority = 2))
|
||||||
|
|
||||||
toPlayerId(party.suggestLoot(Body(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId)
|
toPlayerId(party.suggestLoot(Piece.Body(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"suggest loot by bis pieces got" in {
|
"suggest loot by bis pieces got" in {
|
||||||
val party = default.withPlayer(dnc.withLoot(Head(pieceType = PieceType.Savage, Job.AnyJob)))
|
val party = default.withPlayer(dnc.withLoot(Piece.Head(pieceType = PieceType.Savage, Job.AnyJob)))
|
||||||
|
|
||||||
toPlayerId(party.suggestLoot(Body(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId)
|
toPlayerId(party.suggestLoot(Piece.Body(pieceType = PieceType.Savage, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"suggest loot by this piece got" in {
|
"suggest loot by this piece got" in {
|
||||||
val piece = Body(pieceType = PieceType.Tome, Job.AnyJob)
|
val piece = Piece.Body(pieceType = PieceType.Tome, Job.AnyJob)
|
||||||
val party = default.withPlayer(dnc.withLoot(piece))
|
val party = default.withPlayer(dnc.withLoot(piece))
|
||||||
|
|
||||||
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"suggest loot by total piece got" in {
|
"suggest loot by total piece got" in {
|
||||||
val piece = Body(pieceType = PieceType.Tome, Job.AnyJob)
|
val piece = Piece.Body(pieceType = PieceType.Tome, Job.AnyJob)
|
||||||
val party = default
|
val party = default
|
||||||
.withPlayer(dnc.withLoot(Seq(piece, piece).map(pieceToLoot)))
|
.withPlayer(dnc.withLoot(Seq(piece, piece).map(pieceToLoot)))
|
||||||
.withPlayer(drg.withLoot(piece))
|
.withPlayer(drg.withLoot(piece))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.arcanis.ffxivbis.service.bis
|
package me.arcanis.ffxivbis.service.bis
|
||||||
|
|
||||||
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||||
import me.arcanis.ffxivbis.messages.DownloadBiS
|
import me.arcanis.ffxivbis.messages.BiSProviderMessage.DownloadBiS
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
import org.scalatest.wordspec.AnyWordSpecLike
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
@ -2,7 +2,7 @@ package me.arcanis.ffxivbis.service.database
|
|||||||
|
|
||||||
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||||
import akka.actor.typed.scaladsl.AskPattern.Askable
|
import akka.actor.typed.scaladsl.AskPattern.Askable
|
||||||
import me.arcanis.ffxivbis.messages.{AddPieceToBis, AddPlayer, GetBiS, RemovePieceFromBiS}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.utils.Compare
|
import me.arcanis.ffxivbis.utils.Compare
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
@ -70,7 +70,7 @@ class DatabaseBiSHandlerTest extends ScalaTestWithActorTestKit(Settings.withRand
|
|||||||
|
|
||||||
"update piece in bis set" in {
|
"update piece in bis set" in {
|
||||||
val updateProbe = testKit.createTestProbe[Unit]()
|
val updateProbe = testKit.createTestProbe[Unit]()
|
||||||
val newPiece = Hands(pieceType = PieceType.Savage, Job.DNC)
|
val newPiece = Piece.Hands(pieceType = PieceType.Savage, Job.DNC)
|
||||||
|
|
||||||
database ! AddPieceToBis(Fixtures.playerEmpty.playerId, newPiece, updateProbe.ref)
|
database ! AddPieceToBis(Fixtures.playerEmpty.playerId, newPiece, updateProbe.ref)
|
||||||
updateProbe.expectMessage(askTimeout, ())
|
updateProbe.expectMessage(askTimeout, ())
|
||||||
|
@ -2,8 +2,7 @@ package me.arcanis.ffxivbis.service.database
|
|||||||
|
|
||||||
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||||
import akka.actor.typed.scaladsl.AskPattern.Askable
|
import akka.actor.typed.scaladsl.AskPattern.Askable
|
||||||
import ch.qos.logback.core.util.FixedDelay
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.messages.{AddPieceTo, AddPlayer, GetLoot, RemovePieceFrom}
|
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.utils.Compare
|
import me.arcanis.ffxivbis.utils.Compare
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.arcanis.ffxivbis.service.database
|
package me.arcanis.ffxivbis.service.database
|
||||||
|
|
||||||
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||||
import me.arcanis.ffxivbis.messages.{AddPlayer, GetParty, GetPlayer, RemovePlayer}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.utils.Compare
|
import me.arcanis.ffxivbis.utils.Compare
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package me.arcanis.ffxivbis.service.database
|
package me.arcanis.ffxivbis.service.database
|
||||||
|
|
||||||
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||||
import me.arcanis.ffxivbis.messages.{AddUser, DeleteUser, GetUser, GetUsers}
|
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||||
import me.arcanis.ffxivbis.models.User
|
import me.arcanis.ffxivbis.models.User
|
||||||
import me.arcanis.ffxivbis.utils.Compare
|
import me.arcanis.ffxivbis.utils.Compare
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
@ -80,5 +80,6 @@ class DatabaseUserHandlerTest extends ScalaTestWithActorTestKit(Settings.withRan
|
|||||||
database ! GetUser(Fixtures.partyId, Fixtures.userGet.username, probe.ref)
|
database ! GetUser(Fixtures.partyId, Fixtures.userGet.username, probe.ref)
|
||||||
probe.expectMessage(askTimeout, None)
|
probe.expectMessage(askTimeout, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package me.arcanis.ffxivbis.utils
|
package me.arcanis.ffxivbis.utils
|
||||||
|
|
||||||
object Compare {
|
object Compare {
|
||||||
|
|
||||||
def mapEquals[K, T](left: Map[K, T], right: Map[K, T]): Boolean =
|
def mapEquals[K, T](left: Map[K, T], right: Map[K, T]): Boolean =
|
||||||
left.forall {
|
left.forall {
|
||||||
case (key, value) => right.contains(key) && right(key) == value
|
case (key, value) => right.contains(key) && right(key) == value
|
||||||
|
@ -8,5 +8,4 @@ import scala.language.implicitConversions
|
|||||||
object Converters {
|
object Converters {
|
||||||
|
|
||||||
implicit def pieceToLoot(piece: Piece): Loot = Loot(-1, piece, Instant.ofEpochMilli(0), isFreeLoot = false)
|
implicit def pieceToLoot(piece: Piece): Loot = Loot(-1, piece, Instant.ofEpochMilli(0), isFreeLoot = false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
43
src/test/scala/me/arcanis/ffxivbis/utils/ImplicitsTest.scala
Normal file
43
src/test/scala/me/arcanis/ffxivbis/utils/ImplicitsTest.scala
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package me.arcanis.ffxivbis.utils
|
||||||
|
|
||||||
|
import akka.util.Timeout
|
||||||
|
import me.arcanis.ffxivbis.Settings
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
|
class ImplicitsTest extends AnyWordSpecLike with Matchers {
|
||||||
|
|
||||||
|
import me.arcanis.ffxivbis.utils.Implicits._
|
||||||
|
|
||||||
|
"configuration extension" must {
|
||||||
|
|
||||||
|
"return finite duration" in {
|
||||||
|
val config = Settings.config(Map("value" -> "1m"))
|
||||||
|
config.getFiniteDuration("value") shouldBe FiniteDuration(1, TimeUnit.MINUTES)
|
||||||
|
}
|
||||||
|
|
||||||
|
"return optional string" in {
|
||||||
|
val config = Settings.config(Map("value" -> "string"))
|
||||||
|
config.getOptString("value") shouldBe Some("string")
|
||||||
|
}
|
||||||
|
|
||||||
|
"return None for missing optional string" in {
|
||||||
|
val config = Settings.config(Map.empty)
|
||||||
|
config.getOptString("value") shouldBe None
|
||||||
|
}
|
||||||
|
|
||||||
|
"return None for empty optional string" in {
|
||||||
|
val config = Settings.config(Map("value" -> ""))
|
||||||
|
config.getOptString("value") shouldBe None
|
||||||
|
}
|
||||||
|
|
||||||
|
"return timeout" in {
|
||||||
|
val config = Settings.config(Map("value" -> "1m"))
|
||||||
|
config.getTimeout("value") shouldBe Timeout(1, TimeUnit.MINUTES)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user