mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-24 17:27: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
|
||||
|
||||
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 com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
||||
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 java.util.concurrent.TimeUnit
|
||||
|
@ -16,13 +16,15 @@ import com.typesafe.scalalogging.StrictLogging
|
||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||
import spray.json._
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
trait HttpHandler extends StrictLogging { this: JsonSupport =>
|
||||
|
||||
def exceptionHandler: ExceptionHandler = ExceptionHandler {
|
||||
case ex: IllegalArgumentException =>
|
||||
complete(StatusCodes.BadRequest, ErrorModel(ex.getMessage))
|
||||
case exception: IllegalArgumentException =>
|
||||
complete(StatusCodes.BadRequest, ErrorModel(exception.getMessage))
|
||||
|
||||
case other: Exception =>
|
||||
case NonFatal(other) =>
|
||||
logger.error("exception during request completion", other)
|
||||
complete(StatusCodes.InternalServerError, ErrorModel("unknown server error"))
|
||||
}
|
||||
|
@ -9,5 +9,6 @@
|
||||
package me.arcanis.ffxivbis.http.api.v1.json
|
||||
|
||||
object ApiAction extends Enumeration {
|
||||
|
||||
val add, remove = Value
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ 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._
|
||||
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||
import me.arcanis.ffxivbis.messages.Message
|
||||
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId}
|
||||
|
||||
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.{ActorRef, Scheduler}
|
||||
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 scala.concurrent.Future
|
||||
@ -20,6 +21,6 @@ trait BisProviderHelper {
|
||||
|
||||
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, _))
|
||||
}
|
||||
|
@ -12,7 +12,8 @@ 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._
|
||||
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||
import me.arcanis.ffxivbis.messages.Message
|
||||
import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId, PlayerIdWithCounters}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
@ -12,7 +12,8 @@ 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._
|
||||
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||
import me.arcanis.ffxivbis.messages.Message
|
||||
import me.arcanis.ffxivbis.models.{PartyDescription, Player, PlayerId}
|
||||
|
||||
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.{ActorRef, Scheduler}
|
||||
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 scala.concurrent.Future
|
||||
|
@ -13,7 +13,10 @@ import me.arcanis.ffxivbis.models.{BiS, Job}
|
||||
|
||||
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 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
|
||||
}
|
||||
|
||||
// bis handler
|
||||
trait BisDatabaseMessage extends DatabaseMessage
|
||||
object DatabaseMessage {
|
||||
|
||||
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||
extends BisDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||
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
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
// loot handler
|
||||
trait LootDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||
extends LootDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||
extends LootDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||
extends LootDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
||||
extends LootDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
// party handler
|
||||
trait PartyDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = player.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = partyDescription.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
// user handler
|
||||
trait UserDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||
override val partyId: String = user.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
// bis handler
|
||||
trait BisDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||
extends BisDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||
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
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
// loot handler
|
||||
trait LootDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||
extends LootDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||
extends LootDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||
extends LootDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
||||
extends LootDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
// party handler
|
||||
trait PartyDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = player.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = playerId.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||
override val partyId: String = partyDescription.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
// user handler
|
||||
trait UserDatabaseMessage extends DatabaseMessage
|
||||
|
||||
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||
override val partyId: String = user.partyId
|
||||
override val isReadOnly: Boolean = false
|
||||
}
|
||||
|
||||
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage {
|
||||
override val isReadOnly: Boolean = true
|
||||
}
|
||||
|
||||
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage {
|
||||
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]) {
|
||||
|
||||
def hasPiece(piece: Piece): Boolean = piece match {
|
||||
case upgrade: PieceUpgrade => upgrades.contains(upgrade)
|
||||
case upgrade: Piece.PieceUpgrade => upgrades.contains(upgrade)
|
||||
case _ => pieces.contains(piece)
|
||||
}
|
||||
|
||||
def upgrades: Map[PieceUpgrade, Int] =
|
||||
def upgrades: Map[Piece.PieceUpgrade, Int] =
|
||||
pieces
|
||||
.groupBy(_.upgrade)
|
||||
.foldLeft(Map.empty[PieceUpgrade, Int]) {
|
||||
.foldLeft(Map.empty[Piece.PieceUpgrade, Int]) {
|
||||
case (acc, (Some(k), v)) => acc + (k -> v.size)
|
||||
case (acc, _) => acc
|
||||
}
|
||||
@ -43,5 +43,5 @@ case class BiS(pieces: Seq[Piece]) {
|
||||
|
||||
object BiS {
|
||||
|
||||
def empty: BiS = BiS(Seq.empty)
|
||||
val empty: BiS = BiS(Seq.empty)
|
||||
}
|
||||
|
@ -8,6 +8,26 @@
|
||||
*/
|
||||
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 {
|
||||
|
||||
sealed trait RightSide
|
||||
@ -26,54 +46,38 @@ object Job {
|
||||
object BodyTanks 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 {
|
||||
val leftSide: LeftSide = null
|
||||
val rightSide: RightSide = null
|
||||
override val leftSide: LeftSide = null
|
||||
override val rightSide: RightSide = null
|
||||
}
|
||||
|
||||
trait Casters extends Job {
|
||||
val leftSide: LeftSide = BodyCasters
|
||||
val rightSide: RightSide = AccessoriesInt
|
||||
override val leftSide: LeftSide = BodyCasters
|
||||
override val rightSide: RightSide = AccessoriesInt
|
||||
}
|
||||
trait Healers extends Job {
|
||||
val leftSide: LeftSide = BodyHealers
|
||||
val rightSide: RightSide = AccessoriesMnd
|
||||
override val leftSide: LeftSide = BodyHealers
|
||||
override val rightSide: RightSide = AccessoriesMnd
|
||||
}
|
||||
trait Mnks extends Job {
|
||||
val leftSide: LeftSide = BodyMnks
|
||||
val rightSide: RightSide = AccessoriesStr
|
||||
override val leftSide: LeftSide = BodyMnks
|
||||
override val rightSide: RightSide = AccessoriesStr
|
||||
}
|
||||
trait Drgs extends Job {
|
||||
val leftSide: LeftSide = BodyDrgs
|
||||
val rightSide: RightSide = AccessoriesStr
|
||||
override val leftSide: LeftSide = BodyDrgs
|
||||
override val rightSide: RightSide = AccessoriesStr
|
||||
}
|
||||
trait Nins extends Job {
|
||||
override val leftSide: LeftSide = BodyNins
|
||||
override val rightSide: RightSide = AccessoriesDex
|
||||
}
|
||||
trait Tanks extends Job {
|
||||
val leftSide: LeftSide = BodyTanks
|
||||
val rightSide: RightSide = AccessoriesVit
|
||||
override val leftSide: LeftSide = BodyTanks
|
||||
override val rightSide: RightSide = AccessoriesVit
|
||||
}
|
||||
trait Ranges extends Job {
|
||||
val leftSide: LeftSide = BodyRanges
|
||||
val rightSide: RightSide = AccessoriesDex
|
||||
override val leftSide: LeftSide = BodyRanges
|
||||
override val rightSide: RightSide = AccessoriesDex
|
||||
}
|
||||
|
||||
case object PLD extends Tanks
|
||||
@ -89,10 +93,7 @@ object Job {
|
||||
case object MNK extends Mnks
|
||||
case object DRG extends Drgs
|
||||
case object RPR extends Drgs
|
||||
case object NIN extends Job {
|
||||
val leftSide: LeftSide = BodyNins
|
||||
val rightSide: RightSide = AccessoriesDex
|
||||
}
|
||||
case object NIN extends Nins
|
||||
case object SAM extends Mnks
|
||||
|
||||
case object BRD extends Ranges
|
||||
@ -103,11 +104,11 @@ object Job {
|
||||
case object SMN 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)
|
||||
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 {
|
||||
case Some(value) => value
|
||||
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) {
|
||||
|
||||
def isFreeLootToString: String = if (isFreeLoot) "yes" else "no"
|
||||
|
||||
def isFreeLootToInt: Int = if (isFreeLoot) 1 else 0
|
||||
lazy val isFreeLootToInt: Int = if (isFreeLoot) 1 else 0
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import me.arcanis.ffxivbis.service.LootSelector
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Random
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
case class Party(partyDescription: PartyDescription, rules: Seq[String], players: Map[PlayerId, Player])
|
||||
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")
|
||||
copy(players = players + (player.playerId -> player))
|
||||
} catch {
|
||||
case exception: Exception =>
|
||||
case NonFatal(exception) =>
|
||||
logger.error("cannot add player", exception)
|
||||
this
|
||||
}
|
||||
|
@ -8,10 +8,7 @@
|
||||
*/
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
case class PartyDescription(partyId: String, partyAlias: Option[String]) {
|
||||
|
||||
def alias: String = partyAlias.getOrElse(partyId)
|
||||
}
|
||||
case class PartyDescription(partyId: String, partyAlias: Option[String])
|
||||
|
||||
object PartyDescription {
|
||||
|
||||
|
@ -10,20 +10,20 @@ package me.arcanis.ffxivbis.models
|
||||
|
||||
sealed trait Piece extends Equals {
|
||||
|
||||
def pieceType: PieceType.PieceType
|
||||
def pieceType: PieceType
|
||||
|
||||
def job: Job.Job
|
||||
def job: Job
|
||||
|
||||
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
|
||||
Some(this).collect {
|
||||
case _: PieceAccessory if isTome => AccessoryUpgrade
|
||||
case _: PieceBody if isTome => BodyUpgrade
|
||||
case _: PieceWeapon if isTome => WeaponUpgrade
|
||||
case _: Piece.PieceAccessory if isTome => Piece.AccessoryUpgrade
|
||||
case _: Piece.PieceBody if isTome => Piece.BodyUpgrade
|
||||
case _: Piece.PieceWeapon if isTome => Piece.WeaponUpgrade
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,83 +31,84 @@ sealed trait Piece extends Equals {
|
||||
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 {
|
||||
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 {
|
||||
case "weapon" => Weapon(pieceType, job)
|
||||
case "head" => Head(pieceType, job)
|
||||
@ -125,7 +126,7 @@ object Piece {
|
||||
case other => throw new Error(s"Unknown item type $other")
|
||||
}
|
||||
|
||||
lazy val available: Seq[String] = Seq(
|
||||
val available: Seq[String] = Seq(
|
||||
"weapon",
|
||||
"head",
|
||||
"body",
|
||||
|
@ -8,17 +8,16 @@
|
||||
*/
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
sealed trait PieceType
|
||||
|
||||
object PieceType {
|
||||
|
||||
sealed trait PieceType
|
||||
|
||||
case object Crafted extends PieceType
|
||||
case object Tome extends PieceType
|
||||
case object Savage extends PieceType
|
||||
case object Tome extends PieceType
|
||||
case object Crafted extends PieceType
|
||||
case object Artifact extends PieceType
|
||||
|
||||
lazy val available: Seq[PieceType] =
|
||||
Seq(Crafted, Tome, Savage, Artifact)
|
||||
val available: Seq[PieceType] = Seq(Savage, Tome, Crafted, Artifact)
|
||||
|
||||
def withName(pieceType: String): PieceType =
|
||||
available.find(_.toString.equalsIgnoreCase(pieceType)) match {
|
||||
|
@ -11,7 +11,7 @@ package me.arcanis.ffxivbis.models
|
||||
case class Player(
|
||||
id: Long,
|
||||
partyId: String,
|
||||
job: Job.Job,
|
||||
job: Job,
|
||||
nick: String,
|
||||
bis: BiS,
|
||||
loot: Seq[Loot],
|
||||
@ -51,7 +51,7 @@ case class Player(
|
||||
piece match {
|
||||
case None => 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
|
||||
}
|
||||
|
||||
|
@ -13,14 +13,14 @@ import scala.util.matching.Regex
|
||||
|
||||
trait PlayerIdBase {
|
||||
|
||||
def job: Job.Job
|
||||
def job: Job
|
||||
|
||||
def nick: String
|
||||
|
||||
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 {
|
||||
|
||||
|
@ -10,7 +10,7 @@ package me.arcanis.ffxivbis.models
|
||||
|
||||
case class PlayerIdWithCounters(
|
||||
partyId: String,
|
||||
job: Job.Job,
|
||||
job: Job,
|
||||
nick: String,
|
||||
isRequired: Boolean,
|
||||
priority: Int,
|
||||
@ -24,8 +24,6 @@ case class PlayerIdWithCounters(
|
||||
def gt(that: PlayerIdWithCounters, orderBy: Seq[String]): Boolean =
|
||||
withCounters(orderBy) > that.withCounters(orderBy)
|
||||
|
||||
def isRequiredToString: String = if (isRequired) "yes" else "no"
|
||||
|
||||
def playerId: PlayerId = PlayerId(partyId, job, nick)
|
||||
|
||||
private val counters: Map[String, Int] = Map(
|
||||
@ -47,13 +45,13 @@ object PlayerIdWithCounters {
|
||||
|
||||
def >(that: PlayerCountersComparator): Boolean = {
|
||||
@scala.annotation.tailrec
|
||||
def compareLists(left: List[Int], right: List[Int]): Boolean =
|
||||
def compare(left: Seq[Int], right: Seq[Int]): Boolean =
|
||||
(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 (_, _) => 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
|
||||
|
||||
object Permission extends Enumeration {
|
||||
|
||||
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)
|
||||
|
||||
private def handle(cache: Map[String, Party]): Message.Handler = {
|
||||
case ForgetParty(partyId) =>
|
||||
case ControlMessage.ForgetParty(partyId) =>
|
||||
Behaviors.receiveMessage(handle(cache - partyId))
|
||||
|
||||
case GetNewPartyId(client) =>
|
||||
case ControlMessage.GetNewPartyId(client) =>
|
||||
getPartyId.foreach(client ! _)
|
||||
Behaviors.same
|
||||
|
||||
case StoreParty(partyId, party) =>
|
||||
case ControlMessage.StoreParty(partyId, party) =>
|
||||
Behaviors.receiveMessage(handle(cache.updated(partyId, party)))
|
||||
|
||||
case GetParty(partyId, client) =>
|
||||
case DatabaseMessage.GetParty(partyId, client) =>
|
||||
val party = cache.get(partyId) match {
|
||||
case Some(party) => Future.successful(party)
|
||||
case None =>
|
||||
storage.ask(ref => GetParty(partyId, ref)).map { party =>
|
||||
context.self ! StoreParty(partyId, party)
|
||||
context.system.scheduler.scheduleOnce(cacheTimeout, () => context.self ! ForgetParty(partyId))
|
||||
storage.ask(ref => DatabaseMessage.GetParty(partyId, ref)).map { party =>
|
||||
context.self ! ControlMessage.StoreParty(partyId, party)
|
||||
context.system.scheduler
|
||||
.scheduleOnce(cacheTimeout, () => context.self ! ControlMessage.ForgetParty(partyId))
|
||||
party
|
||||
}
|
||||
}
|
||||
@ -67,7 +68,7 @@ class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMes
|
||||
|
||||
private def getPartyId: Future[String] = {
|
||||
val partyId = Party.randomPartyId
|
||||
storage.ask(ref => Exists(partyId, ref)).flatMap {
|
||||
storage.ask(ref => DatabaseMessage.Exists(partyId, ref)).flatMap {
|
||||
case true => getPartyId
|
||||
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.http.scaladsl.model._
|
||||
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.service.bis.parser.Parser
|
||||
import me.arcanis.ffxivbis.service.bis.parser.impl.{Ariyala, Etro}
|
||||
@ -21,6 +21,7 @@ import spray.json._
|
||||
|
||||
import java.nio.file.Paths
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.control.NonFatal
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
class BisProvider(context: ActorContext[BiSProviderMessage])
|
||||
@ -32,7 +33,7 @@ class BisProvider(context: ActorContext[BiSProviderMessage])
|
||||
|
||||
override def onMessage(msg: BiSProviderMessage): Behavior[BiSProviderMessage] =
|
||||
msg match {
|
||||
case DownloadBiS(link, job, client) =>
|
||||
case BiSProviderMessage.DownloadBiS(link, job, client) =>
|
||||
get(link, job).onComplete {
|
||||
case Success(items) => client ! BiS(items)
|
||||
case Failure(exception) =>
|
||||
@ -46,7 +47,7 @@ class BisProvider(context: ActorContext[BiSProviderMessage])
|
||||
Behaviors.same
|
||||
}
|
||||
|
||||
private def get(link: String, job: Job.Job): Future[Seq[Piece]] =
|
||||
private def get(link: String, job: Job): Future[Seq[Piece]] =
|
||||
try {
|
||||
val url = Uri(link)
|
||||
val id = Paths.get(link).normalize.getFileName.toString
|
||||
@ -55,7 +56,7 @@ class BisProvider(context: ActorContext[BiSProviderMessage])
|
||||
val uri = parser.uri(url, id)
|
||||
sendRequest(uri, BisProvider.parseBisJsonToPieces(job, parser, getPieceType))
|
||||
} 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))
|
||||
|
||||
private def parseBisJsonToPieces(
|
||||
job: Job.Job,
|
||||
job: Job,
|
||||
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]] =
|
||||
idParser.parse(job, js).flatMap { pieces =>
|
||||
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 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
|
||||
.getConfigList("me.arcanis.ffxivbis.bis-provider.cached-items")
|
||||
.asScala
|
||||
@ -31,17 +31,16 @@ trait XivApi extends RequestExecutor {
|
||||
}
|
||||
.toMap
|
||||
|
||||
def getPieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType.PieceType]] = {
|
||||
val (local, remote) = itemIds.foldLeft((Map.empty[Long, PieceType.PieceType], Seq.empty[Long])) {
|
||||
case ((l, r), id) =>
|
||||
if (preloadedItems.contains(id)) (l.updated(id, preloadedItems(id)), r)
|
||||
else (l, r :+ id)
|
||||
def getPieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType]] = {
|
||||
val (local, remote) = itemIds.foldLeft((Map.empty[Long, PieceType], Seq.empty[Long])) { case ((l, r), id) =>
|
||||
if (preloadedItems.contains(id)) (l.updated(id, preloadedItems(id)), r)
|
||||
else (l, r :+ id)
|
||||
}
|
||||
if (remote.isEmpty) Future.successful(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)
|
||||
.withPath(Uri.Path / "item")
|
||||
.withQuery(
|
||||
@ -111,7 +110,7 @@ object XivApi {
|
||||
|
||||
private def parseXivapiJsonToType(
|
||||
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 {
|
||||
val shopMap = js.fields("Results") match {
|
||||
case array: JsArray =>
|
||||
|
@ -17,7 +17,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
object Ariyala extends Parser {
|
||||
|
||||
override def parse(job: Job.Job, js: JsObject)(implicit
|
||||
override def parse(job: Job, js: JsObject)(implicit
|
||||
executionContext: ExecutionContext
|
||||
): Future[Map[String, Long]] =
|
||||
Future {
|
||||
|
@ -18,7 +18,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
object Etro extends Parser {
|
||||
|
||||
override def parse(job: Job.Job, js: JsObject)(implicit
|
||||
override def parse(job: Job, js: JsObject)(implicit
|
||||
executionContext: ExecutionContext
|
||||
): Future[Map[String, Long]] =
|
||||
Future {
|
||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
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
|
||||
|
||||
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.{Behavior, DispatcherSelector}
|
||||
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.storage.DatabaseProfile
|
||||
|
||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
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.service.database.Database
|
||||
|
||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
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.service.database.Database
|
||||
|
||||
|
@ -10,7 +10,8 @@ package me.arcanis.ffxivbis.service.database.impl
|
||||
|
||||
import akka.actor.typed.Behavior
|
||||
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
|
||||
|
||||
trait DatabaseUserHandler { this: Database =>
|
||||
|
@ -16,6 +16,7 @@ import me.arcanis.ffxivbis.models.{Loot, Piece, PlayerId}
|
||||
import java.time.Instant
|
||||
import javax.sql.DataSource
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
class DatabaseProfile(override val executionContext: ExecutionContext, config: Config)
|
||||
extends StrictLogging
|
||||
@ -31,7 +32,7 @@ class DatabaseProfile(override val executionContext: ExecutionContext, config: C
|
||||
val dataSourceConfig = DatabaseConnection.getDataSourceConfig(profile)
|
||||
new HikariDataSource(dataSourceConfig)
|
||||
} catch {
|
||||
case exception: Exception =>
|
||||
case NonFatal(exception) =>
|
||||
logger.error("exception during storage initialization", 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
|
||||
|
||||
object Fixtures {
|
||||
|
||||
lazy val bis: BiS = BiS(
|
||||
Seq(
|
||||
Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
||||
Head(pieceType = PieceType.Savage, Job.DNC),
|
||||
Body(pieceType = PieceType.Savage, Job.DNC),
|
||||
Hands(pieceType = PieceType.Tome, Job.DNC),
|
||||
Legs(pieceType = PieceType.Tome, Job.DNC),
|
||||
Feet(pieceType = PieceType.Savage, Job.DNC),
|
||||
Ears(pieceType = PieceType.Savage, Job.DNC),
|
||||
Neck(pieceType = PieceType.Tome, Job.DNC),
|
||||
Wrist(pieceType = PieceType.Savage, Job.DNC),
|
||||
Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
||||
Ring(pieceType = PieceType.Tome, Job.DNC, "right ring")
|
||||
Piece.Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
||||
Piece.Head(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Body(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Hands(pieceType = PieceType.Tome, Job.DNC),
|
||||
Piece.Legs(pieceType = PieceType.Tome, Job.DNC),
|
||||
Piece.Feet(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Ears(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Neck(pieceType = PieceType.Tome, Job.DNC),
|
||||
Piece.Wrist(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
||||
Piece.Ring(pieceType = PieceType.Tome, Job.DNC, "right ring")
|
||||
)
|
||||
)
|
||||
lazy val bis2: BiS = BiS(
|
||||
Seq(
|
||||
Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
||||
Head(pieceType = PieceType.Tome, Job.DNC),
|
||||
Body(pieceType = PieceType.Savage, Job.DNC),
|
||||
Hands(pieceType = PieceType.Tome, Job.DNC),
|
||||
Legs(pieceType = PieceType.Savage, Job.DNC),
|
||||
Feet(pieceType = PieceType.Tome, Job.DNC),
|
||||
Ears(pieceType = PieceType.Savage, Job.DNC),
|
||||
Neck(pieceType = PieceType.Savage, Job.DNC),
|
||||
Wrist(pieceType = PieceType.Savage, Job.DNC),
|
||||
Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
||||
Ring(pieceType = PieceType.Savage, Job.DNC, "right ring")
|
||||
Piece.Weapon(pieceType = PieceType.Savage ,Job.DNC),
|
||||
Piece.Head(pieceType = PieceType.Tome, Job.DNC),
|
||||
Piece.Body(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Hands(pieceType = PieceType.Tome, Job.DNC),
|
||||
Piece.Legs(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Feet(pieceType = PieceType.Tome, Job.DNC),
|
||||
Piece.Ears(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Neck(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Wrist(pieceType = PieceType.Savage, Job.DNC),
|
||||
Piece.Ring(pieceType = PieceType.Tome, Job.DNC, "left ring"),
|
||||
Piece.Ring(pieceType = PieceType.Savage, Job.DNC, "right ring")
|
||||
)
|
||||
)
|
||||
lazy val bis3: BiS = BiS(
|
||||
Seq(
|
||||
Weapon(pieceType = PieceType.Savage ,Job.SGE),
|
||||
Head(pieceType = PieceType.Tome, Job.SGE),
|
||||
Body(pieceType = PieceType.Savage, Job.SGE),
|
||||
Hands(pieceType = PieceType.Tome, Job.SGE),
|
||||
Legs(pieceType = PieceType.Tome, Job.SGE),
|
||||
Feet(pieceType = PieceType.Savage, Job.SGE),
|
||||
Ears(pieceType = PieceType.Savage, Job.SGE),
|
||||
Neck(pieceType = PieceType.Tome, Job.SGE),
|
||||
Wrist(pieceType = PieceType.Savage, Job.SGE),
|
||||
Ring(pieceType = PieceType.Savage, Job.SGE, "left ring"),
|
||||
Ring(pieceType = PieceType.Tome, Job.SGE, "right ring")
|
||||
Piece.Weapon(pieceType = PieceType.Savage ,Job.SGE),
|
||||
Piece.Head(pieceType = PieceType.Tome, Job.SGE),
|
||||
Piece.Body(pieceType = PieceType.Savage, Job.SGE),
|
||||
Piece.Hands(pieceType = PieceType.Tome, Job.SGE),
|
||||
Piece.Legs(pieceType = PieceType.Tome, Job.SGE),
|
||||
Piece.Feet(pieceType = PieceType.Savage, Job.SGE),
|
||||
Piece.Ears(pieceType = PieceType.Savage, Job.SGE),
|
||||
Piece.Neck(pieceType = PieceType.Tome, Job.SGE),
|
||||
Piece.Wrist(pieceType = PieceType.Savage, Job.SGE),
|
||||
Piece.Ring(pieceType = PieceType.Savage, Job.SGE, "left 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 link5: String = "https://ffxiv.ariyala.com/1FGU0"
|
||||
|
||||
lazy val lootWeapon: Piece = Weapon(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
lazy val lootBody: Piece = Body(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
lazy val lootBodyCrafted: Piece = Body(pieceType = PieceType.Crafted, Job.AnyJob)
|
||||
lazy val lootHands: Piece = Hands(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
lazy val lootLegs: Piece = Legs(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
lazy val lootEars: Piece = Ears(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
lazy val lootRing: Piece = Ring(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
lazy val lootLeftRing: Piece = Ring(pieceType = PieceType.Tome, Job.AnyJob, "left ring")
|
||||
lazy val lootRightRing: Piece = Ring(pieceType = PieceType.Tome, Job.AnyJob, "right ring")
|
||||
lazy val lootUpgrade: Piece = BodyUpgrade
|
||||
lazy val lootWeapon: Piece = Piece.Weapon(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
lazy val lootBody: Piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
lazy val lootBodyCrafted: Piece = Piece.Body(pieceType = PieceType.Crafted, Job.AnyJob)
|
||||
lazy val lootHands: Piece = Piece.Hands(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
lazy val lootLegs: Piece = Piece.Legs(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
lazy val lootEars: Piece = Piece.Ears(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
lazy val lootRing: Piece = Piece.Ring(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
lazy val lootLeftRing: Piece = Piece.Ring(pieceType = PieceType.Tome, Job.AnyJob, "left ring")
|
||||
lazy val lootRightRing: Piece = Piece.Ring(pieceType = PieceType.Tome, Job.AnyJob, "right ring")
|
||||
lazy val lootUpgrade: Piece = Piece.BodyUpgrade
|
||||
lazy val loot: Seq[Piece] = Seq(lootBody, lootHands, lootLegs, lootUpgrade)
|
||||
|
||||
lazy val partyId: String = Party.randomPartyId
|
||||
@ -84,5 +85,5 @@ object Fixtures {
|
||||
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
||||
|
||||
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
|
||||
|
||||
object Settings {
|
||||
|
||||
def config(values: Map[String, AnyRef]): Config = {
|
||||
@scala.annotation.tailrec
|
||||
def replace(acc: Config, iter: List[(String, AnyRef)]): Config = iter match {
|
||||
case Nil => acc
|
||||
case (key -> value) :: tail => replace(acc.withValue(key, ConfigValueFactory.fromAnyRef(value)), tail)
|
||||
def replace(config: Config, iter: List[(String, AnyRef)]): Config = iter match {
|
||||
case Nil => config
|
||||
case (key -> value) :: tail => replace(config.withValue(key, ConfigValueFactory.fromAnyRef(value)), tail)
|
||||
}
|
||||
|
||||
val default = ConfigFactory.load()
|
||||
@ -23,7 +24,9 @@ object Settings {
|
||||
if (databaseFile.exists)
|
||||
databaseFile.delete()
|
||||
}
|
||||
|
||||
def randomDatabasePath: String = File.createTempFile("ffxivdb-",".db").toPath.toString
|
||||
|
||||
def withRandomDatabase: Config =
|
||||
config(Map("me.arcanis.ffxivbis.database.sqlite.jdbcUrl" -> s"jdbc:sqlite:$randomDatabasePath"))
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
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.{StatusCodes, Uri}
|
||||
import akka.http.scaladsl.server.Route
|
||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
@ -25,7 +25,7 @@ class AuthorizationTest extends AnyWordSpecLike with Matchers with ScalatestRout
|
||||
}
|
||||
|
||||
"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 {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
|
@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||
import akka.testkit.TestKit
|
||||
import com.typesafe.config.Config
|
||||
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.service.PartyService
|
||||
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||
@ -53,15 +53,6 @@ class BiSEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteT
|
||||
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 {
|
||||
|
||||
"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 com.typesafe.config.Config
|
||||
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.database.{Database, Migration}
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
|
@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||
import akka.testkit.TestKit
|
||||
import com.typesafe.config.Config
|
||||
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.service.PartyService
|
||||
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.testkit.TestKit
|
||||
import com.typesafe.config.Config
|
||||
import me.arcanis.ffxivbis
|
||||
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.bis.BisProvider
|
||||
import me.arcanis.ffxivbis.service.database.{Database, Migration}
|
||||
|
@ -46,7 +46,7 @@ class BiSTest extends AnyWordSpecLike with Matchers {
|
||||
}
|
||||
|
||||
"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 {
|
||||
|
||||
"return upgrade" in {
|
||||
Fixtures.lootWeapon.upgrade shouldEqual Some(WeaponUpgrade)
|
||||
Fixtures.lootWeapon.upgrade shouldEqual Some(Piece.WeaponUpgrade)
|
||||
Fixtures.lootBody.upgrade shouldEqual None
|
||||
Fixtures.lootHands.upgrade shouldEqual Some(BodyUpgrade)
|
||||
Fixtures.lootHands.upgrade shouldEqual Some(Piece.BodyUpgrade)
|
||||
Fixtures.lootLegs.upgrade shouldEqual None
|
||||
Fixtures.lootEars.upgrade shouldEqual None
|
||||
Fixtures.lootLeftRing.upgrade shouldEqual Some(AccessoryUpgrade)
|
||||
Fixtures.lootRightRing.upgrade shouldEqual Some(AccessoryUpgrade)
|
||||
Fixtures.lootLeftRing.upgrade shouldEqual Some(Piece.AccessoryUpgrade)
|
||||
Fixtures.lootRightRing.upgrade shouldEqual Some(Piece.AccessoryUpgrade)
|
||||
}
|
||||
|
||||
"build piece from string" in {
|
||||
|
@ -2,7 +2,7 @@ package me.arcanis.ffxivbis.service
|
||||
|
||||
import akka.actor.testkit.typed.scaladsl.ActorTestKit
|
||||
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.service.bis.BisProvider
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
@ -28,10 +28,10 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
|
||||
val testKit = ActorTestKit(Settings.withRandomDatabase)
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
default = default.withPlayer(dnc).withPlayer(drg)
|
||||
@ -43,11 +43,11 @@ class LootSelectorTest extends AnyWordSpecLike with Matchers with BeforeAndAfter
|
||||
"loot selector" must {
|
||||
|
||||
"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 {
|
||||
val piece = Body(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
val piece = Piece.Body(pieceType = PieceType.Savage, Job.AnyJob)
|
||||
val party = default.withPlayer(dnc.withLoot(piece))
|
||||
|
||||
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 {
|
||||
val party = default.withPlayer(
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
val piece = Body(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
val piece = Piece.Body(pieceType = PieceType.Tome, Job.AnyJob)
|
||||
val party = default.withPlayer(dnc.withLoot(piece))
|
||||
|
||||
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||
}
|
||||
|
||||
"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
|
||||
.withPlayer(dnc.withLoot(Seq(piece, piece).map(pieceToLoot)))
|
||||
.withPlayer(drg.withLoot(piece))
|
||||
|
@ -1,7 +1,7 @@
|
||||
package me.arcanis.ffxivbis.service.bis
|
||||
|
||||
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.{Fixtures, Settings}
|
||||
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.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.utils.Compare
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
@ -70,7 +70,7 @@ class DatabaseBiSHandlerTest extends ScalaTestWithActorTestKit(Settings.withRand
|
||||
|
||||
"update piece in bis set" in {
|
||||
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)
|
||||
updateProbe.expectMessage(askTimeout, ())
|
||||
|
@ -2,8 +2,7 @@ package me.arcanis.ffxivbis.service.database
|
||||
|
||||
import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
|
||||
import akka.actor.typed.scaladsl.AskPattern.Askable
|
||||
import ch.qos.logback.core.util.FixedDelay
|
||||
import me.arcanis.ffxivbis.messages.{AddPieceTo, AddPlayer, GetLoot, RemovePieceFrom}
|
||||
import me.arcanis.ffxivbis.messages.DatabaseMessage._
|
||||
import me.arcanis.ffxivbis.models._
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package me.arcanis.ffxivbis.service.database
|
||||
|
||||
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.utils.Compare
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package me.arcanis.ffxivbis.service.database
|
||||
|
||||
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.utils.Compare
|
||||
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)
|
||||
probe.expectMessage(askTimeout, None)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package me.arcanis.ffxivbis.utils
|
||||
|
||||
object Compare {
|
||||
|
||||
def mapEquals[K, T](left: Map[K, T], right: Map[K, T]): Boolean =
|
||||
left.forall {
|
||||
case (key, value) => right.contains(key) && right(key) == value
|
||||
|
@ -8,5 +8,4 @@ import scala.language.implicitConversions
|
||||
object Converters {
|
||||
|
||||
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