From 99ed2705a28c092ee843868832b68405dd78f802 Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Thu, 6 Jan 2022 06:19:57 +0300 Subject: [PATCH] another test for bis part --- .../arcanis/ffxivbis/http/PlayerHelper.scala | 2 +- .../arcanis/ffxivbis/http/view/BiSView.scala | 2 +- .../ffxivbis/http/view/LootSuggestView.scala | 2 ++ .../messages/BiSProviderMessage.scala | 5 +++- .../ffxivbis/service/bis/Ariyala.scala | 8 +++---- .../ffxivbis/service/bis/BisProvider.scala | 23 +++++++++---------- .../arcanis/ffxivbis/service/bis/Etro.scala | 10 ++++---- .../ffxivbis/service/bis/IdParser.scala | 16 +++++++++++++ .../arcanis/ffxivbis/service/bis/XivApi.scala | 15 ++++++------ .../scala/me/arcanis/ffxivbis/Fixtures.scala | 16 +++++++++++++ .../service/bis/BisProviderTest.scala | 6 +++++ 11 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala diff --git a/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala index c035bdc..e9cb78d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala @@ -24,7 +24,7 @@ trait PlayerHelper extends BisProviderHelper { def addPlayer(player: Player) (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(ref => AddPlayer(player, ref)).map { res => - player.link match { + player.link.map(_.trim).filter(_.nonEmpty) match { case Some(link) => downloadBiS(link, player.job).map { bis => bis.pieces.map(piece => storage.ask(AddPieceToBis(player.playerId, piece, _))) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala index 81d3f23..d17a517 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala @@ -74,7 +74,7 @@ class BiSView(override val storage: ActorRef[Message], } PlayerId(partyId, player) match { - case Some(playerId) => (maybePiece, maybePieceType, action, maybeLink) match { + case Some(playerId) => (maybePiece, maybePieceType, action, maybeLink.map(_.trim).filter(_.nonEmpty)) match { case (Some(piece), Some(pieceType), "add", _) => bisAction(playerId, piece, pieceType)(addPieceBiS(playerId, _)) case (Some(piece), Some(pieceType), "remove", _) => diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala index 24359d1..02581b7 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala @@ -89,6 +89,8 @@ object LootSuggestView { body( h2("Suggest loot"), + for (part <- piece) yield p(s"Piece ${part.piece} (${part.pieceType})"), + ErrorView.template(error), SearchLineView.template, diff --git a/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala b/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala index 36768cb..daa5c48 100644 --- a/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala +++ b/src/main/scala/me/arcanis/ffxivbis/messages/BiSProviderMessage.scala @@ -5,4 +5,7 @@ import me.arcanis.ffxivbis.models.{BiS, Job} sealed trait BiSProviderMessage -case class DownloadBiS(link: String, job: Job.Job, replyTo: ActorRef[BiS]) extends BiSProviderMessage +case class DownloadBiS(link: String, job: Job.Job, replyTo: ActorRef[BiS]) extends BiSProviderMessage { + + require(link.nonEmpty && link.trim == link, "Link must be not empty and contain no spaces") +} diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala index 4e3a724..e99b883 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala @@ -14,10 +14,10 @@ import spray.json.{JsNumber, JsObject, JsString, deserializationError} import scala.concurrent.{ExecutionContext, Future} -object Ariyala { +object Ariyala extends IdParser { - def idParser(job: Job.Job, js: JsObject) - (implicit executionContext: ExecutionContext): Future[Map[String, Long]] = + override def parse(job: Job.Job, js: JsObject) + (implicit executionContext: ExecutionContext): Future[Map[String, Long]] = Future { val apiJob = js.fields.get("content") match { case Some(JsString(value)) => value @@ -37,7 +37,7 @@ object Ariyala { } } - def uri(root: Uri, id: String): Uri = + override def uri(root: Uri, id: String): Uri = root .withPath(Uri.Path / "store.app") .withQuery(Uri.Query(Map("identifier" -> id))) diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala index eb3bb94..8b8f929 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala @@ -9,7 +9,6 @@ package me.arcanis.ffxivbis.service.bis import java.nio.file.Paths - import akka.actor.ClassicActorSystemProvider import akka.actor.typed.{Behavior, PostStop, Signal} import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors} @@ -20,6 +19,7 @@ import me.arcanis.ffxivbis.models.{BiS, Job, Piece, PieceType} import spray.json._ import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Failure, Success} class BisProvider(context: ActorContext[BiSProviderMessage]) extends AbstractBehavior[BiSProviderMessage](context) with XivApi with StrictLogging { @@ -29,7 +29,11 @@ class BisProvider(context: ActorContext[BiSProviderMessage]) override def onMessage(msg: BiSProviderMessage): Behavior[BiSProviderMessage] = msg match { case DownloadBiS(link, job, client) => - get(link, job).map(BiS(_)).foreach(client ! _) + get(link, job).onComplete { + case Success(items) => client ! BiS(items) + case Failure(exception) => + logger.error("received exception while getting items", exception) + } Behaviors.same } @@ -43,14 +47,9 @@ class BisProvider(context: ActorContext[BiSProviderMessage]) val url = Uri(link) val id = Paths.get(link).normalize.getFileName.toString - val (idParser, uri) = - if (url.authority.host.address().contains("etro")) { - (Etro.idParser(_, _), Etro.uri(url, id)) - } else { - (Ariyala.idParser(_, _), Ariyala.uri(url, id)) - } - - sendRequest(uri, BisProvider.parseBisJsonToPieces(job, idParser, getPieceType)) + val parser = if (url.authority.host.address().contains("etro")) Etro else Ariyala + val uri = parser.uri(url, id) + sendRequest(uri, BisProvider.parseBisJsonToPieces(job, parser, getPieceType)) } } @@ -60,11 +59,11 @@ object BisProvider { Behaviors.setup[BiSProviderMessage](context => new BisProvider(context)) private def parseBisJsonToPieces(job: Job.Job, - idParser: (Job.Job, JsObject) => Future[Map[String, Long]], + idParser: IdParser, pieceTypes: Seq[Long] => Future[Map[Long, PieceType.PieceType]]) (js: JsObject) (implicit executionContext: ExecutionContext): Future[Seq[Piece]] = - idParser(job, js).flatMap { pieces => + idParser.parse(job, js).flatMap { pieces => pieceTypes(pieces.values.toSeq).map { types => pieces.view.mapValues(types).map { case (piece, pieceType) => Piece(piece, pieceType, job) diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala index a7b644b..e516bfe 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala @@ -10,14 +10,14 @@ package me.arcanis.ffxivbis.service.bis import akka.http.scaladsl.model.Uri import me.arcanis.ffxivbis.models.Job -import spray.json.{JsNumber, JsObject} +import spray.json.{JsNumber, JsObject, deserializationError} import scala.concurrent.{ExecutionContext, Future} -object Etro { +object Etro extends IdParser { - def idParser(job: Job.Job, js: JsObject) - (implicit executionContext: ExecutionContext): Future[Map[String, Long]] = + override def parse(job: Job.Job, js: JsObject) + (implicit executionContext: ExecutionContext): Future[Map[String, Long]] = Future { js.fields.foldLeft(Map.empty[String, Long]) { case (acc, (key, JsNumber(id))) => BisProvider.remapKey(key).map(k => acc + (k -> id.toLong)).getOrElse(acc) @@ -25,6 +25,6 @@ object Etro { } } - def uri(root: Uri, id: String): Uri = + override def uri(root: Uri, id: String): Uri = root.withPath(Uri.Path / "api" / "gearsets" / id) } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala new file mode 100644 index 0000000..af30586 --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala @@ -0,0 +1,16 @@ +package me.arcanis.ffxivbis.service.bis + +import akka.http.scaladsl.model.Uri +import com.typesafe.scalalogging.StrictLogging +import me.arcanis.ffxivbis.models.Job +import spray.json.JsObject + +import scala.concurrent.{ExecutionContext, Future} + +trait IdParser extends StrictLogging { + + def parse(job: Job.Job, js: JsObject) + (implicit executionContext: ExecutionContext): Future[Map[String, Long]] + + def uri(root: Uri, id: String): Uri +} diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala index 464a650..3f3c1b3 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala @@ -70,7 +70,7 @@ object XivApi { js.fields("Results") match { case array: JsArray => array.elements.map(_.asJsObject.getFields("ID", "GameContentLinks") match { - case Seq(JsNumber(id), shop) => id.toLong -> extractTraderId(shop.asJsObject) + case Seq(JsNumber(id), shop: JsObject) => id.toLong -> extractTraderId(shop.asJsObject) case other => throw deserializationError(s"Could not parse $other") }).toMap case other => throw deserializationError(s"Could not parse $other") @@ -83,18 +83,19 @@ object XivApi { Future { val shopMap = js.fields("Results") match { case array: JsArray => - array.elements.map { shop => - shop.asJsObject.fields("ID") match { - case JsNumber(id) => id.toLong -> shop.asJsObject - case other => throw deserializationError(s"Could not parse $other") - } + array.elements.collect { + case shop: JsObject => + shop.asJsObject.fields("ID") match { + case JsNumber(id) => id.toLong -> shop.asJsObject + case other => throw deserializationError(s"Could not parse $other") + } }.toMap case other => throw deserializationError(s"Could not parse $other") } shops.map { case (itemId, (index, shopId)) => val pieceType = - if (index == "crafted" && shopId == -1) PieceType.Crafted + if (index == "crafted" && shopId == -1L) PieceType.Crafted else { Try(shopMap(shopId).fields(s"ItemCost$index").asJsObject) .toOption diff --git a/src/test/scala/me/arcanis/ffxivbis/Fixtures.scala b/src/test/scala/me/arcanis/ffxivbis/Fixtures.scala index c8dbe31..042e793 100644 --- a/src/test/scala/me/arcanis/ffxivbis/Fixtures.scala +++ b/src/test/scala/me/arcanis/ffxivbis/Fixtures.scala @@ -33,11 +33,27 @@ object Fixtures { 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") + ) + ) lazy val link: String = "https://ffxiv.ariyala.com/19V5R" lazy val link2: String = "https://ffxiv.ariyala.com/1A0WM" lazy val link3: String = "https://etro.gg/gearset/26a67536-b4ce-4adc-a46a-f70e348bb138" 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) diff --git a/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala index a5aef87..654c034 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/bis/BisProviderTest.scala @@ -23,6 +23,12 @@ class BisProviderTest extends ScalaTestWithActorTestKit(Settings.withRandomDatab probe.expectMessage(askTimeout, Fixtures.bis) } + "get best in slot set (ariyala 2)" in { + val probe = testKit.createTestProbe[BiS]() + provider ! DownloadBiS(Fixtures.link5, Job.SGE, probe.ref) + probe.expectMessage(askTimeout, Fixtures.bis3) + } + "get best in slot set (etro)" in { val probe = testKit.createTestProbe[BiS]() provider ! DownloadBiS(Fixtures.link3, Job.DNC, probe.ref)