diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 0000000..f9c3393 --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,22 @@ +name: tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + run-tests: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup JDK + uses: actions/setup-java@v2 + with: + distribution: temurin + java-version: 8 + - name: Build and Test + run: sbt -v +test \ No newline at end of file diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..e35820f --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,35 @@ +version = 3.3.1 + +runner.dialect = "scala213" + +maxColumn = 120 + +align.preset = none + +continuationIndent { + defnSite = 2 + extendSite = 2 +} + +rewrite { + rules = [ + AvoidInfix, + RedundantBraces, + RedundantParens, + SortImports, + SortModifiers + ] + + redundantBraces { + generalExpressions = yes + ifElseExpressions = yes + includeUnitMethods = yes + methodBodies = yes + parensForOneLineApply = yes + stringInterpolation = yes + } + +} + +importSelectors = singleLine +trailingCommas = preserve diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0d21599..0000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: scala -scala: - - 2.13.1 - -sbt_args: -no-colors - -script: - - sbt compile - - sbt test diff --git a/README.md b/README.md index 6005f4e..30dc677 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # FFXIV BiS -[![Build Status](https://travis-ci.org/arcan1s/ffxivbis.svg?branch=master)](https://travis-ci.org/arcan1s/ffxivbis) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/arcan1s/ffxivbis) +[![Build status](https://github.com/arcan1s/ffxivbis/actions/workflows/run-tests.yml/badge.svg)](https://github.com/arcan1s/ffxivbis/actions/workflows/run-tests.yml) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/arcan1s/ffxivbis) Service which allows to manage savage loot distribution easy. diff --git a/project/plugins.sbt b/project/plugins.sbt index cf376fc..53e3cd6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,4 @@ addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3") addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.4") addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1") diff --git a/src/main/scala/me/arcanis/ffxivbis/Application.scala b/src/main/scala/me/arcanis/ffxivbis/Application.scala index 1f893d4..d91737c 100644 --- a/src/main/scala/me/arcanis/ffxivbis/Application.scala +++ b/src/main/scala/me/arcanis/ffxivbis/Application.scala @@ -16,24 +16,24 @@ import akka.stream.Materializer import com.typesafe.scalalogging.StrictLogging import me.arcanis.ffxivbis.http.RootEndpoint import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.{Database, PartyService} +import me.arcanis.ffxivbis.service.database.Database +import me.arcanis.ffxivbis.service.PartyService import me.arcanis.ffxivbis.storage.Migration import scala.concurrent.ExecutionContext +import scala.jdk.CollectionConverters._ import scala.util.{Failure, Success} -class Application(context: ActorContext[Nothing]) - extends AbstractBehavior[Nothing](context) with StrictLogging { +class Application(context: ActorContext[Nothing]) extends AbstractBehavior[Nothing](context) with StrictLogging { logger.info("root supervisor started") startApplication() override def onMessage(msg: Nothing): Behavior[Nothing] = Behaviors.unhandled - override def onSignal: PartialFunction[Signal, Behavior[Nothing]] = { - case PostStop => - logger.info("root supervisor stopped") - Behaviors.same + override def onSignal: PartialFunction[Signal, Behavior[Nothing]] = { case PostStop => + logger.info("root supervisor stopped") + Behaviors.same } private def startApplication(): Unit = { @@ -45,7 +45,7 @@ class Application(context: ActorContext[Nothing]) implicit val materializer: Materializer = Materializer(context) Migration(config) match { - case Success(_) => + case Success(result) if result.success => val bisProvider = context.spawn(BisProvider(), "bis-provider") val storage = context.spawn(Database(), "storage") val party = context.spawn(PartyService(storage), "party") @@ -54,6 +54,11 @@ class Application(context: ActorContext[Nothing]) val flow = Route.toFlow(http.route)(context.system) Http(context.system).newServerAt(host, port).bindFlow(flow) + case Success(result) => + logger.error(s"migration completed with error, executed ${result.migrationsExecuted}") + result.migrations.asScala.foreach(o => logger.info(s"=> ${o.description} (${o.executionTime})")) + context.system.terminate() + case Failure(exception) => logger.error("exception during migration", exception) context.system.terminate() diff --git a/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala b/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala index 97946cc..356887d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/Authorization.scala @@ -25,8 +25,7 @@ trait Authorization { def storage: ActorRef[Message] - def authenticateBasicBCrypt[T](realm: String, - authenticate: (String, String) => Future[Option[T]]): Directive1[T] = { + def authenticateBasicBCrypt[T](realm: String, authenticate: (String, String) => Future[Option[T]]): Directive1[T] = { def challenge = HttpChallenges.basic(realm) extractCredentials.flatMap { @@ -39,22 +38,34 @@ trait Authorization { } } - def authenticator(scope: Permission.Value, partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = + def authenticator(scope: Permission.Value, partyId: String)(username: String, password: String)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Option[String]] = storage.ask(GetUser(partyId, username, _)).map { case Some(user) if user.verify(password) && user.verityScope(scope) => Some(username) case _ => None } - def authAdmin(partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = + def authAdmin(partyId: String)(username: String, password: String)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Option[String]] = authenticator(Permission.admin, partyId)(username, password) - def authGet(partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = + def authGet(partyId: String)(username: String, password: String)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Option[String]] = authenticator(Permission.get, partyId)(username, password) - def authPost(partyId: String)(username: String, password: String) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Option[String]] = + def authPost(partyId: String)(username: String, password: String)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Option[String]] = authenticator(Permission.post, partyId)(username, password) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala index 2ee6a26..f1a5fce 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/BiSHelper.scala @@ -21,32 +21,38 @@ trait BiSHelper extends BisProviderHelper { def storage: ActorRef[Message] - def addPieceBiS(playerId: PlayerId, piece: Piece) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def addPieceBiS(playerId: PlayerId, piece: Piece)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(AddPieceToBis(playerId, piece.withJob(playerId.job), _)) - def bis(partyId: String, playerId: Option[PlayerId]) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[Player]] = + def bis(partyId: String, playerId: Option[PlayerId])(implicit + timeout: Timeout, + scheduler: Scheduler + ): Future[Seq[Player]] = storage.ask(GetBiS(partyId, playerId, _)) - def doModifyBiS(action: ApiAction.Value, playerId: PlayerId, piece: Piece) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def doModifyBiS(action: ApiAction.Value, playerId: PlayerId, piece: Piece)(implicit + timeout: Timeout, + scheduler: Scheduler + ): Future[Unit] = action match { case ApiAction.add => addPieceBiS(playerId, piece) case ApiAction.remove => removePieceBiS(playerId, piece) } - def putBiS(playerId: PlayerId, link: String) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = { + def putBiS(playerId: PlayerId, link: String)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Unit] = storage.ask(RemovePiecesFromBiS(playerId, _)).flatMap { _ => - downloadBiS(link, playerId.job).flatMap { bis => - Future.traverse(bis.pieces)(addPieceBiS(playerId, _)) - }.map(_ => ()) + downloadBiS(link, playerId.job) + .flatMap { bis => + Future.traverse(bis.pieces)(addPieceBiS(playerId, _)) + } + .map(_ => ()) } - } - def removePieceBiS(playerId: PlayerId, piece: Piece) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def removePieceBiS(playerId: PlayerId, piece: Piece)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(RemovePieceFromBiS(playerId, piece, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala index 6f81ef7..3ddc56e 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/BisProviderHelper.scala @@ -20,7 +20,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.Job)(implicit timeout: Timeout, scheduler: Scheduler): Future[BiS] = provider.ask(DownloadBiS(link, job, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala index a556bd0..57b1351 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/LootHelper.scala @@ -21,28 +21,35 @@ trait LootHelper { def storage: ActorRef[Message] - def addPieceLoot(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = - storage.ask( - AddPieceTo(playerId, piece, isFreeLoot, _)) + def addPieceLoot(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean)(implicit + timeout: Timeout, + scheduler: Scheduler + ): Future[Unit] = + storage.ask(AddPieceTo(playerId, piece, isFreeLoot, _)) - def doModifyLoot(action: ApiAction.Value, playerId: PlayerId, piece: Piece, maybeFree: Option[Boolean]) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def doModifyLoot(action: ApiAction.Value, playerId: PlayerId, piece: Piece, maybeFree: Option[Boolean])(implicit + timeout: Timeout, + scheduler: Scheduler + ): Future[Unit] = (action, maybeFree) match { case (ApiAction.add, Some(isFreeLoot)) => addPieceLoot(playerId, piece, isFreeLoot) case (ApiAction.remove, _) => removePieceLoot(playerId, piece) case _ => throw new IllegalArgumentException(s"Invalid combinantion of action $action and fee loot $maybeFree") } - def loot(partyId: String, playerId: Option[PlayerId]) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[Player]] = + def loot(partyId: String, playerId: Option[PlayerId])(implicit + timeout: Timeout, + scheduler: Scheduler + ): Future[Seq[Player]] = storage.ask(GetLoot(partyId, playerId, _)) - def removePieceLoot(playerId: PlayerId, piece: Piece) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def removePieceLoot(playerId: PlayerId, piece: Piece)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(RemovePieceFrom(playerId, piece, _)) - def suggestPiece(partyId: String, piece: Piece) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Seq[PlayerIdWithCounters]] = + def suggestPiece(partyId: String, piece: Piece)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Seq[PlayerIdWithCounters]] = storage.ask(SuggestLoot(partyId, piece, _)).map(_.result) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala index e9cb78d..f10e7b2 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/PlayerHelper.scala @@ -21,31 +21,42 @@ trait PlayerHelper extends BisProviderHelper { def storage: ActorRef[Message] - def addPlayer(player: Player) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = - storage.ask(ref => AddPlayer(player, ref)).map { res => - 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, _))) - }.map(_ => res) - case None => Future.successful(res) + def addPlayer( + player: Player + )(implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = + storage + .ask(ref => AddPlayer(player, ref)) + .map { res => + 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, _))) + } + .map(_ => res) + case None => Future.successful(res) + } } - }.flatten + .flatten - def doModifyPlayer(action: ApiAction.Value, player: Player) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def doModifyPlayer(action: ApiAction.Value, player: Player)(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Unit] = action match { case ApiAction.add => addPlayer(player) case ApiAction.remove => removePlayer(player.playerId) } - def getPartyDescription(partyId: String) - (implicit timeout: Timeout, scheduler: Scheduler): Future[PartyDescription] = + def getPartyDescription(partyId: String)(implicit timeout: Timeout, scheduler: Scheduler): Future[PartyDescription] = storage.ask(GetPartyDescription(partyId, _)) - def getPlayers(partyId: String, maybePlayerId: Option[PlayerId]) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Seq[Player]] = + def getPlayers(partyId: String, maybePlayerId: Option[PlayerId])(implicit + executionContext: ExecutionContext, + timeout: Timeout, + scheduler: Scheduler + ): Future[Seq[Player]] = maybePlayerId match { case Some(playerId) => storage.ask(GetPlayer(playerId, _)).map(_.toSeq) @@ -53,11 +64,11 @@ trait PlayerHelper extends BisProviderHelper { storage.ask(GetParty(partyId, _)).map(_.players.values.toSeq) } - def removePlayer(playerId: PlayerId) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def removePlayer(playerId: PlayerId)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(RemovePlayer(playerId, _)) - def updateDescription(partyDescription: PartyDescription) - (implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def updateDescription( + partyDescription: PartyDescription + )(implicit executionContext: ExecutionContext, timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(UpdateParty(partyDescription, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala index eb3117b..8bd921e 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/RootEndpoint.scala @@ -19,9 +19,7 @@ import me.arcanis.ffxivbis.http.api.v1.RootApiV1Endpoint import me.arcanis.ffxivbis.http.view.RootView import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} -class RootEndpoint(system: ActorSystem[Nothing], - storage: ActorRef[Message], - provider: ActorRef[BiSProviderMessage]) +class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], provider: ActorRef[BiSProviderMessage]) extends StrictLogging { import me.arcanis.ffxivbis.utils.Implicits._ @@ -41,7 +39,10 @@ class RootEndpoint(system: ActorSystem[Nothing], val start = Instant.now.toEpochMilli mapResponse { response => val time = (Instant.now.toEpochMilli - start) / 1000.0 - httpLogger.debug(s"""- - [${Instant.now}] "${context.request.method.name()} ${context.request.uri.path}" ${response.status.intValue()} ${response.entity.getContentLengthOption.getAsLong} $time""") + httpLogger.debug( + s"""- - [${Instant.now}] "${context.request.method.name()} ${context.request.uri.path}" ${response.status + .intValue()} ${response.entity.getContentLengthOption.getAsLong} $time""" + ) response } } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala b/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala index 698ffa8..0c6dc4c 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/Swagger.scala @@ -18,9 +18,12 @@ import scala.io.Source class Swagger(config: Config) extends SwaggerHttpService { override val apiClasses: Set[Class[_]] = Set( - classOf[api.v1.BiSEndpoint], classOf[api.v1.LootEndpoint], - classOf[api.v1.PartyEndpoint], classOf[api.v1.PlayerEndpoint], - classOf[api.v1.TypesEndpoint], classOf[api.v1.UserEndpoint] + classOf[api.v1.BiSEndpoint], + classOf[api.v1.LootEndpoint], + classOf[api.v1.PartyEndpoint], + classOf[api.v1.PlayerEndpoint], + classOf[api.v1.TypesEndpoint], + classOf[api.v1.UserEndpoint] ) override val info: Info = Info( diff --git a/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala b/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala index 3086e3a..dc6c202 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/UserHelper.scala @@ -20,22 +20,18 @@ trait UserHelper { def storage: ActorRef[Message] - def addUser(user: User, isHashedPassword: Boolean) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def addUser(user: User, isHashedPassword: Boolean)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(AddUser(user, isHashedPassword, _)) def newPartyId(implicit timeout: Timeout, scheduler: Scheduler): Future[String] = storage.ask(GetNewPartyId) - def user(partyId: String, username: String) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Option[User]] = + def user(partyId: String, username: String)(implicit timeout: Timeout, scheduler: Scheduler): Future[Option[User]] = storage.ask(GetUser(partyId, username, _)) - def users(partyId: String) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[User]] = + def users(partyId: String)(implicit timeout: Timeout, scheduler: Scheduler): Future[Seq[User]] = storage.ask(GetUsers(partyId, _)) - def removeUser(partyId: String, username: String) - (implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = + def removeUser(partyId: String, username: String)(implicit timeout: Timeout, scheduler: Scheduler): Future[Unit] = storage.ask(DeleteUser(partyId, username, _)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala index 2d7bf4d..c6e06c4 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpoint.scala @@ -28,32 +28,51 @@ import me.arcanis.ffxivbis.models.PlayerId import scala.util.{Failure, Success} @Path("api/v1") -class BiSEndpoint(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends BiSHelper with Authorization with JsonSupport { +class BiSEndpoint(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])(implicit + timeout: Timeout, + scheduler: Scheduler +) extends BiSHelper + with Authorization + with JsonSupport { def route: Route = createBiS ~ getBiS ~ modifyBiS @PUT @Path("party/{partyId}/bis") @Consumes(value = Array("application/json")) - @Operation(summary = "create best in slot", description = "Create the best in slot set", + @Operation( + summary = "create best in slot", + description = "Create the best in slot set", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "player best in slot description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[PlayerBiSLinkResponse])))), + requestBody = new RequestBody( + description = "player best in slot description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[PlayerBiSLinkResponse]))) + ), responses = Array( new ApiResponse(responseCode = "201", description = "Best in slot set has been created"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))), tags = Array("best in slot"), @@ -78,23 +97,44 @@ class BiSEndpoint(override val storage: ActorRef[Message], @GET @Path("party/{partyId}/bis") @Produces(value = Array("application/json")) - @Operation(summary = "get best in slot", description = "Return the best in slot items", + @Operation( + summary = "get best in slot", + description = "Return the best in slot items", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), - new Parameter(name = "nick", in = ParameterIn.QUERY, description = "player nick name to filter", example = "Siuan Sanche"), + new Parameter( + name = "nick", + in = ParameterIn.QUERY, + description = "player nick name to filter", + example = "Siuan Sanche" + ), new Parameter(name = "job", in = ParameterIn.QUERY, description = "player job to filter", example = "DNC"), ), responses = Array( - new ApiResponse(responseCode = "200", description = "Best in slot", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerResponse])) - ))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Best in slot", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerResponse])) + ) + ) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))), tags = Array("best in slot"), @@ -120,22 +160,39 @@ class BiSEndpoint(override val storage: ActorRef[Message], @POST @Path("party/{partyId}/bis") @Consumes(value = Array("application/json")) - @Operation(summary = "modify best in slot", description = "Add or remove an item from the best in slot", + @Operation( + summary = "modify best in slot", + description = "Add or remove an item from the best in slot", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "action and piece description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[PieceActionResponse])))), + requestBody = new RequestBody( + description = "action and piece description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[PieceActionResponse]))) + ), responses = Array( new ApiResponse(responseCode = "202", description = "Best in slot set has been modified"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))), tags = Array("best in slot"), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala index 92215c6..437821d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpoint.scala @@ -28,32 +28,55 @@ import me.arcanis.ffxivbis.models.PlayerId import scala.util.{Failure, Success} @Path("api/v1") -class LootEndpoint(override val storage: ActorRef[Message]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends LootHelper with Authorization with JsonSupport with HttpHandler { +class LootEndpoint(override val storage: ActorRef[Message])(implicit timeout: Timeout, scheduler: Scheduler) + extends LootHelper + with Authorization + with JsonSupport + with HttpHandler { def route: Route = getLoot ~ modifyLoot @GET @Path("party/{partyId}/loot") @Produces(value = Array("application/json")) - @Operation(summary = "get loot list", description = "Return the looted items", + @Operation( + summary = "get loot list", + description = "Return the looted items", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), - new Parameter(name = "nick", in = ParameterIn.QUERY, description = "player nick name to filter", example = "Siuan Sanche"), + new Parameter( + name = "nick", + in = ParameterIn.QUERY, + description = "player nick name to filter", + example = "Siuan Sanche" + ), new Parameter(name = "job", in = ParameterIn.QUERY, description = "player job to filter", example = "DNC"), ), responses = Array( - new ApiResponse(responseCode = "200", description = "Loot list", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerResponse])) - ))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Loot list", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerResponse])) + ) + ) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))), tags = Array("loot"), @@ -78,22 +101,39 @@ class LootEndpoint(override val storage: ActorRef[Message]) @POST @Consumes(value = Array("application/json")) @Path("party/{partyId}/loot") - @Operation(summary = "modify loot list", description = "Add or remove an item from the loot list", + @Operation( + summary = "modify loot list", + description = "Add or remove an item from the loot list", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "action and piece description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[PieceActionResponse])))), + requestBody = new RequestBody( + description = "action and piece description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[PieceActionResponse]))) + ), responses = Array( new ApiResponse(responseCode = "202", description = "Loot list has been modified"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))), tags = Array("loot"), @@ -119,25 +159,47 @@ class LootEndpoint(override val storage: ActorRef[Message]) @Path("party/{partyId}/loot") @Consumes(value = Array("application/json")) @Produces(value = Array("application/json")) - @Operation(summary = "suggest loot", description = "Suggest loot piece to party", + @Operation( + summary = "suggest loot", + description = "Suggest loot piece to party", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "piece description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[PieceResponse])))), + requestBody = new RequestBody( + description = "piece description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[PieceResponse]))) + ), responses = Array( - new ApiResponse(responseCode = "200", description = "Players with counters ordered by priority to get this item", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerIdWithCountersResponse])), - ))), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Players with counters ordered by priority to get this item", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerIdWithCountersResponse])), + ) + ) + ), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))), tags = Array("loot"), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala index c6a8a64..f41806a 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpoint.scala @@ -27,29 +27,47 @@ import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import scala.util.{Failure, Success} @Path("api/v1") -class PartyEndpoint(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends PlayerHelper with Authorization with JsonSupport with HttpHandler { +class PartyEndpoint(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])( + implicit + timeout: Timeout, + scheduler: Scheduler +) extends PlayerHelper + with Authorization + with JsonSupport + with HttpHandler { def route: Route = getPartyDescription ~ modifyPartyDescription @GET @Path("party/{partyId}/description") @Produces(value = Array("application/json")) - @Operation(summary = "get party description", description = "Return the party description", + @Operation( + summary = "get party description", + description = "Return the party description", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), responses = Array( - new ApiResponse(responseCode = "200", description = "Party description", - content = Array(new Content(schema = new Schema(implementation = classOf[PartyDescriptionResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Party description", + content = Array(new Content(schema = new Schema(implementation = classOf[PartyDescriptionResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))), tags = Array("party"), @@ -71,22 +89,39 @@ class PartyEndpoint(override val storage: ActorRef[Message], @POST @Consumes(value = Array("application/json")) @Path("party/{partyId}/description") - @Operation(summary = "modify party description", description = "Edit party description", + @Operation( + summary = "modify party description", + description = "Edit party description", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "new party description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[PartyDescriptionResponse])))), + requestBody = new RequestBody( + description = "new party description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[PartyDescriptionResponse]))) + ), responses = Array( new ApiResponse(responseCode = "202", description = "Party description has been modified"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))), tags = Array("party"), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala index 823a70f..44b543d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpoint.scala @@ -28,33 +28,58 @@ import me.arcanis.ffxivbis.models.PlayerId import scala.util.{Failure, Success} @Path("api/v1") -class PlayerEndpoint(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends PlayerHelper with Authorization with JsonSupport with HttpHandler { +class PlayerEndpoint(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])( + implicit + timeout: Timeout, + scheduler: Scheduler +) extends PlayerHelper + with Authorization + with JsonSupport + with HttpHandler { def route: Route = getParty ~ modifyParty @GET @Path("party/{partyId}") @Produces(value = Array("application/json")) - @Operation(summary = "get party", description = "Return the players who belong to the party", + @Operation( + summary = "get party", + description = "Return the players who belong to the party", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), - new Parameter(name = "nick", in = ParameterIn.QUERY, description = "player nick name to filter", example = "Siuan Sanche"), + new Parameter( + name = "nick", + in = ParameterIn.QUERY, + description = "player nick name to filter", + example = "Siuan Sanche" + ), new Parameter(name = "job", in = ParameterIn.QUERY, description = "player job to filter", example = "DNC"), ), responses = Array( - new ApiResponse(responseCode = "200", description = "Players list", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerResponse])), - ))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Players list", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[PlayerResponse])), + ) + ) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))), tags = Array("party"), @@ -79,22 +104,39 @@ class PlayerEndpoint(override val storage: ActorRef[Message], @POST @Path("party/{partyId}") @Consumes(value = Array("application/json")) - @Operation(summary = "modify party", description = "Add or remove a player from party list", + @Operation( + summary = "modify party", + description = "Add or remove a player from party list", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "player description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[PlayerActionResponse])))), + requestBody = new RequestBody( + description = "player description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[PlayerActionResponse]))) + ), responses = Array( new ApiResponse(responseCode = "202", description = "Party has been modified"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))), tags = Array("party"), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala index e4c8e36..a33c547 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/RootApiV1Endpoint.scala @@ -16,10 +16,11 @@ import com.typesafe.config.Config import me.arcanis.ffxivbis.http.api.v1.json.JsonSupport import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} -class RootApiV1Endpoint(storage: ActorRef[Message], - provider: ActorRef[BiSProviderMessage], - config: Config)(implicit timeout: Timeout, scheduler: Scheduler) - extends JsonSupport with HttpHandler { +class RootApiV1Endpoint(storage: ActorRef[Message], provider: ActorRef[BiSProviderMessage], config: Config)(implicit + timeout: Timeout, + scheduler: Scheduler +) extends JsonSupport + with HttpHandler { private val biSEndpoint = new BiSEndpoint(storage, provider) private val lootEndpoint = new LootEndpoint(storage) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpoint.scala index 2cea2c6..e009720 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/TypesEndpoint.scala @@ -26,14 +26,24 @@ class TypesEndpoint(config: Config) extends JsonSupport { @GET @Path("types/jobs") @Produces(value = Array("application/json")) - @Operation(summary = "jobs list", description = "Returns the available jobs", + @Operation( + summary = "jobs list", + description = "Returns the available jobs", responses = Array( - new ApiResponse(responseCode = "200", description = "List of available jobs", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[String])) - ))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "List of available jobs", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[String])) + ) + ) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), tags = Array("types"), ) @@ -47,14 +57,24 @@ class TypesEndpoint(config: Config) extends JsonSupport { @GET @Path("types/permissions") @Produces(value = Array("application/json")) - @Operation(summary = "permissions list", description = "Returns the available permissions", + @Operation( + summary = "permissions list", + description = "Returns the available permissions", responses = Array( - new ApiResponse(responseCode = "200", description = "List of available permissions", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[String])) - ))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "List of available permissions", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[String])) + ) + ) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), tags = Array("types"), ) @@ -68,14 +88,24 @@ class TypesEndpoint(config: Config) extends JsonSupport { @GET @Path("types/pieces") @Produces(value = Array("application/json")) - @Operation(summary = "pieces list", description = "Returns the available pieces", + @Operation( + summary = "pieces list", + description = "Returns the available pieces", responses = Array( - new ApiResponse(responseCode = "200", description = "List of available pieces", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[String])) - ))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "List of available pieces", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[String])) + ) + ) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), tags = Array("types"), ) @@ -89,14 +119,24 @@ class TypesEndpoint(config: Config) extends JsonSupport { @GET @Path("types/pieces/types") @Produces(value = Array("application/json")) - @Operation(summary = "piece types list", description = "Returns the available piece types", + @Operation( + summary = "piece types list", + description = "Returns the available piece types", responses = Array( - new ApiResponse(responseCode = "200", description = "List of available piece types", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[String])) - ))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "List of available piece types", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[String])) + ) + ) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), tags = Array("types"), ) @@ -110,14 +150,24 @@ class TypesEndpoint(config: Config) extends JsonSupport { @GET @Path("types/priority") @Produces(value = Array("application/json")) - @Operation(summary = "priority list", description = "Returns the current priority list", + @Operation( + summary = "priority list", + description = "Returns the current priority list", responses = Array( - new ApiResponse(responseCode = "200", description = "Priority order", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[String])) - ))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Priority order", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[String])) + ) + ) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), tags = Array("types"), ) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala index 55e8a33..d5b9597 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpoint.scala @@ -28,26 +28,41 @@ import me.arcanis.ffxivbis.models.Permission import scala.util.{Failure, Success} @Path("api/v1") -class UserEndpoint(override val storage: ActorRef[Message]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends UserHelper with Authorization with JsonSupport { +class UserEndpoint(override val storage: ActorRef[Message])(implicit timeout: Timeout, scheduler: Scheduler) + extends UserHelper + with Authorization + with JsonSupport { def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers @PUT @Path("party") @Consumes(value = Array("application/json")) - @Operation(summary = "create new party", description = "Create new party with specified ID", - requestBody = new RequestBody(description = "party administrator description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[UserResponse])))), + @Operation( + summary = "create new party", + description = "Create new party with specified ID", + requestBody = new RequestBody( + description = "party administrator description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[UserResponse]))) + ), responses = Array( new ApiResponse(responseCode = "200", description = "Party has been created"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "406", description = "Party with the specified ID already exists", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "406", + description = "Party with the specified ID already exists", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), tags = Array("party"), ) @@ -73,22 +88,39 @@ class UserEndpoint(override val storage: ActorRef[Message]) @POST @Path("party/{partyId}/users") @Consumes(value = Array("application/json")) - @Operation(summary = "create new user", description = "Add an user to the specified party", + @Operation( + summary = "create new user", + description = "Add an user to the specified party", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), - requestBody = new RequestBody(description = "user description", required = true, - content = Array(new Content(schema = new Schema(implementation = classOf[UserResponse])))), + requestBody = new RequestBody( + description = "user description", + required = true, + content = Array(new Content(schema = new Schema(implementation = classOf[UserResponse]))) + ), responses = Array( new ApiResponse(responseCode = "201", description = "User has been created"), - new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "400", + description = "Invalid parameters were supplied", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))), tags = Array("users"), @@ -112,19 +144,30 @@ class UserEndpoint(override val storage: ActorRef[Message]) @DELETE @Path("party/{partyId}/users/{username}") - @Operation(summary = "remove user", description = "Remove an user from the specified party", + @Operation( + summary = "remove user", + description = "Remove an user from the specified party", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), new Parameter(name = "username", in = ParameterIn.PATH, description = "username to remove", example = "siuan"), ), responses = Array( new ApiResponse(responseCode = "202", description = "User has been removed"), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))), tags = Array("users"), @@ -146,21 +189,37 @@ class UserEndpoint(override val storage: ActorRef[Message]) @GET @Path("party/{partyId}/users") @Produces(value = Array("application/json")) - @Operation(summary = "get users", description = "Return the list of users belong to party", + @Operation( + summary = "get users", + description = "Return the list of users belong to party", parameters = Array( new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"), ), responses = Array( - new ApiResponse(responseCode = "200", description = "Users list", - content = Array(new Content( - array = new ArraySchema(schema = new Schema(implementation = classOf[UserResponse])), - ))), - new ApiResponse(responseCode = "401", description = "Supplied authorization is invalid", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "403", description = "Access is forbidden", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), - new ApiResponse(responseCode = "500", description = "Internal server error", - content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse])))), + new ApiResponse( + responseCode = "200", + description = "Users list", + content = Array( + new Content( + array = new ArraySchema(schema = new Schema(implementation = classOf[UserResponse])), + ) + ) + ), + new ApiResponse( + responseCode = "401", + description = "Supplied authorization is invalid", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "403", + description = "Access is forbidden", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), + new ApiResponse( + responseCode = "500", + description = "Internal server error", + content = Array(new Content(schema = new Schema(implementation = classOf[ErrorResponse]))) + ), ), security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("admin"))), tags = Array("users"), diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/ErrorResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/ErrorResponse.scala index 9601e3c..ba3e7a0 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/ErrorResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/ErrorResponse.scala @@ -10,5 +10,4 @@ package me.arcanis.ffxivbis.http.api.v1.json import io.swagger.v3.oas.annotations.media.Schema -case class ErrorResponse( - @Schema(description = "error message", required = true) message: String) +case class ErrorResponse(@Schema(description = "error message", required = true) message: String) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/JsonSupport.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/JsonSupport.scala index 9f74d9f..4486244 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/JsonSupport.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/JsonSupport.scala @@ -42,7 +42,9 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol { implicit val partyIdFormat: RootJsonFormat[PartyIdResponse] = jsonFormat1(PartyIdResponse.apply) implicit val pieceFormat: RootJsonFormat[PieceResponse] = jsonFormat3(PieceResponse.apply) implicit val lootFormat: RootJsonFormat[LootResponse] = jsonFormat3(LootResponse.apply) - implicit val partyDescriptionFormat: RootJsonFormat[PartyDescriptionResponse] = jsonFormat2(PartyDescriptionResponse.apply) + implicit val partyDescriptionFormat: RootJsonFormat[PartyDescriptionResponse] = jsonFormat2( + PartyDescriptionResponse.apply + ) implicit val playerFormat: RootJsonFormat[PlayerResponse] = jsonFormat7(PlayerResponse.apply) implicit val playerActionFormat: RootJsonFormat[PlayerActionResponse] = jsonFormat2(PlayerActionResponse.apply) implicit val playerIdFormat: RootJsonFormat[PlayerIdResponse] = jsonFormat3(PlayerIdResponse.apply) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala index d07121e..0bf0d42 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/LootResponse.scala @@ -8,7 +8,8 @@ import me.arcanis.ffxivbis.models.Loot case class LootResponse( @Schema(description = "looted piece", required = true) piece: PieceResponse, @Schema(description = "loot timestamp", required = true) timestamp: Instant, - @Schema(description = "is loot free for all", required = true) isFreeLoot: Boolean) { + @Schema(description = "is loot free for all", required = true) isFreeLoot: Boolean +) { def toLoot: Loot = Loot(-1, piece.toPiece, timestamp, isFreeLoot) } @@ -17,4 +18,4 @@ object LootResponse { def fromLoot(loot: Loot): LootResponse = LootResponse(PieceResponse.fromPiece(loot.piece), loot.timestamp, loot.isFreeLoot) -} \ No newline at end of file +} diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala index fd5135b..7111c14 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyDescriptionResponse.scala @@ -13,7 +13,8 @@ import me.arcanis.ffxivbis.models.PartyDescription case class PartyDescriptionResponse( @Schema(description = "party id", required = true) partyId: String, - @Schema(description = "party name") partyAlias: Option[String]) { + @Schema(description = "party name") partyAlias: Option[String] +) { def toDescription: PartyDescription = PartyDescription(partyId, partyAlias) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyIdResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyIdResponse.scala index 78cdfb1..81bfcff 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyIdResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PartyIdResponse.scala @@ -10,5 +10,4 @@ package me.arcanis.ffxivbis.http.api.v1.json import io.swagger.v3.oas.annotations.media.Schema -case class PartyIdResponse( - @Schema(description = "party id", required = true) partyId: String) +case class PartyIdResponse(@Schema(description = "party id", required = true) partyId: String) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceActionResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceActionResponse.scala index d81b563..b90026c 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceActionResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceActionResponse.scala @@ -11,7 +11,13 @@ package me.arcanis.ffxivbis.http.api.v1.json import io.swagger.v3.oas.annotations.media.Schema case class PieceActionResponse( - @Schema(description = "action to perform", required = true, `type` = "string", allowableValues = Array("add", "remove")) action: ApiAction.Value, + @Schema( + description = "action to perform", + required = true, + `type` = "string", + allowableValues = Array("add", "remove") + ) action: ApiAction.Value, @Schema(description = "piece description", required = true) piece: PieceResponse, @Schema(description = "player description", required = true) playerId: PlayerIdResponse, - @Schema(description = "is piece free to roll or not") isFreeLoot: Option[Boolean]) + @Schema(description = "is piece free to roll or not") isFreeLoot: Option[Boolean] +) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala index 56a05ee..9a7e172 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PieceResponse.scala @@ -14,7 +14,8 @@ import me.arcanis.ffxivbis.models.{Job, Piece, PieceType} case class PieceResponse( @Schema(description = "piece type", required = true) pieceType: String, @Schema(description = "job name to which piece belong or AnyJob", required = true, example = "DNC") job: String, - @Schema(description = "piece name", required = true, example = "body") piece: String) { + @Schema(description = "piece name", required = true, example = "body") piece: String +) { def toPiece: Piece = Piece(piece, PieceType.withName(pieceType), Job.withName(job)) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerActionResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerActionResponse.scala index 742b1e3..f79ca93 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerActionResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerActionResponse.scala @@ -11,5 +11,12 @@ package me.arcanis.ffxivbis.http.api.v1.json import io.swagger.v3.oas.annotations.media.Schema case class PlayerActionResponse( - @Schema(description = "action to perform", required = true, `type` = "string", allowableValues = Array("add", "remove"), example = "add") action: ApiAction.Value, - @Schema(description = "player description", required = true) playerId: PlayerResponse) + @Schema( + description = "action to perform", + required = true, + `type` = "string", + allowableValues = Array("add", "remove"), + example = "add" + ) action: ApiAction.Value, + @Schema(description = "player description", required = true) playerId: PlayerResponse +) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerBiSLinkResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerBiSLinkResponse.scala index 3c790ff..b4625ff 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerBiSLinkResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerBiSLinkResponse.scala @@ -11,5 +11,10 @@ package me.arcanis.ffxivbis.http.api.v1.json import io.swagger.v3.oas.annotations.media.Schema case class PlayerBiSLinkResponse( - @Schema(description = "link to player best in slot", required = true, example = "https://ffxiv.ariyala.com/19V5R") link: String, - @Schema(description = "player description", required = true) playerId: PlayerIdResponse) + @Schema( + description = "link to player best in slot", + required = true, + example = "https://ffxiv.ariyala.com/19V5R" + ) link: String, + @Schema(description = "player description", required = true) playerId: PlayerIdResponse +) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala index 726398b..2b54925 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdResponse.scala @@ -14,7 +14,8 @@ import me.arcanis.ffxivbis.models.{Job, PlayerId} case class PlayerIdResponse( @Schema(description = "unique party ID. Required in responses", example = "abcdefgh") partyId: Option[String], @Schema(description = "job name", required = true, example = "DNC") job: String, - @Schema(description = "player nick name", required = true, example = "Siuan Sanche") nick: String) { + @Schema(description = "player nick name", required = true, example = "Siuan Sanche") nick: String +) { def withPartyId(partyId: String): PlayerId = PlayerId(partyId, Job.withName(job), nick) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala index 84ac265..8485102 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerIdWithCountersResponse.scala @@ -20,7 +20,8 @@ case class PlayerIdWithCountersResponse( @Schema(description = "count of savage pieces in best in slot", required = true) bisCountTotal: Int, @Schema(description = "count of looted pieces", required = true) lootCount: Int, @Schema(description = "count of looted pieces which are parts of best in slot", required = true) lootCountBiS: Int, - @Schema(description = "total count of looted pieces", required = true) lootCountTotal: Int) + @Schema(description = "total count of looted pieces", required = true) lootCountTotal: Int +) object PlayerIdWithCountersResponse { @@ -34,5 +35,6 @@ object PlayerIdWithCountersResponse { playerIdWithCounters.bisCountTotal, playerIdWithCounters.lootCount, playerIdWithCounters.lootCountBiS, - playerIdWithCounters.lootCountTotal) + playerIdWithCounters.lootCountTotal + ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala index b645c0b..2a4b987 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/PlayerResponse.scala @@ -18,20 +18,32 @@ case class PlayerResponse( @Schema(description = "pieces in best in slot") bis: Option[Seq[PieceResponse]], @Schema(description = "looted pieces") loot: Option[Seq[LootResponse]], @Schema(description = "link to best in slot", example = "https://ffxiv.ariyala.com/19V5R") link: Option[String], - @Schema(description = "player loot priority", `type` = "number") priority: Option[Int]) { + @Schema(description = "player loot priority", `type` = "number") priority: Option[Int] +) { def toPlayer: Player = - Player(-1, partyId, Job.withName(job), nick, + Player( + -1, + partyId, + Job.withName(job), + nick, BiS(bis.getOrElse(Seq.empty).map(_.toPiece)), loot.getOrElse(Seq.empty).map(_.toLoot), - link, priority.getOrElse(0)) + link, + priority.getOrElse(0) + ) } object PlayerResponse { def fromPlayer(player: Player): PlayerResponse = - PlayerResponse(player.partyId, player.job.toString, player.nick, + PlayerResponse( + player.partyId, + player.job.toString, + player.nick, Some(player.bis.pieces.map(PieceResponse.fromPiece)), Some(player.loot.map(LootResponse.fromLoot)), - player.link, Some(player.priority)) + player.link, + Some(player.priority) + ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala index b1c1abf..6786140 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/api/v1/json/UserResponse.scala @@ -15,7 +15,12 @@ case class UserResponse( @Schema(description = "unique party ID", required = true, example = "abcdefgh") partyId: String, @Schema(description = "username to login to party", required = true, example = "siuan") username: String, @Schema(description = "password to login to party", required = true, example = "pa55w0rd") password: String, - @Schema(description = "user permission", defaultValue = "get", allowableValues = Array("get", "post", "admin")) permission: Option[Permission.Value] = None) { + @Schema( + description = "user permission", + defaultValue = "get", + allowableValues = Array("get", "post", "admin") + ) permission: Option[Permission.Value] = None +) { def toUser: User = User(partyId, username, password, permission.getOrElse(Permission.get)) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala index f6c9a79..5a9d049 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/BasePartyView.scala @@ -18,10 +18,12 @@ import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} import scala.util.{Failure, Success} -class BasePartyView(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends PlayerHelper with Authorization { +class BasePartyView(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])( + implicit + timeout: Timeout, + scheduler: Scheduler +) extends PlayerHelper + with Authorization { def route: Route = getIndex @@ -47,25 +49,25 @@ object BasePartyView { import scalatags.Text.tags2.{title => titleTag} def root(partyId: String): Text.TypedTag[String] = - a(href:=s"/party/$partyId", title:="root")("root") + a(href := s"/party/$partyId", title := "root")("root") def template(partyId: String, alias: String): String = "" + - html(lang:="en", + html( + lang := "en", head( titleTag(s"Party $alias"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( h2(s"Party $alias"), br, - h2(a(href:=s"/party/$partyId/players", title:="party")("party")), - h2(a(href:=s"/party/$partyId/bis", title:="bis management")("best in slot")), - h2(a(href:=s"/party/$partyId/loot", title:="loot management")("loot")), - h2(a(href:=s"/party/$partyId/suggest", title:="suggest loot")("suggest")), + h2(a(href := s"/party/$partyId/players", title := "party")("party")), + h2(a(href := s"/party/$partyId/bis", title := "bis management")("best in slot")), + h2(a(href := s"/party/$partyId/loot", title := "loot management")("loot")), + h2(a(href := s"/party/$partyId/suggest", title := "suggest loot")("suggest")), hr, - h2(a(href:=s"/party/$partyId/users", title:="user management")("users")) + h2(a(href := s"/party/$partyId/users", title := "user management")("users")) ) ) } 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 d17a517..57a5c42 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/BiSView.scala @@ -20,10 +20,11 @@ import me.arcanis.ffxivbis.models.{Piece, PieceType, Player, PlayerId} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -class BiSView(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends BiSHelper with Authorization { +class BiSView(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])(implicit + timeout: Timeout, + scheduler: Scheduler +) extends BiSHelper + with Authorization { def route: Route = getBiS ~ modifyBiS @@ -33,11 +34,13 @@ class BiSView(override val storage: ActorRef[Message], authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ => get { complete { - bis(partyId, None).map { players => - BiSView.template(partyId, players, None) - }.map { text => - (StatusCodes.OK, RootView.toHtml(text)) - } + bis(partyId, None) + .map { players => + BiSView.template(partyId, players, None) + } + .map { text => + (StatusCodes.OK, RootView.toHtml(text)) + } } } } @@ -49,21 +52,30 @@ class BiSView(override val storage: ActorRef[Message], extractExecutionContext { implicit executionContext => authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ => post { - formFields("player".as[String], "piece".as[String].?, "piece_type".as[String].?, "link".as[String].?, "action".as[String]) { - (player, maybePiece, maybePieceType, maybeLink, action) => - onComplete(modifyBiSCall(partyId, player, maybePiece, maybePieceType, maybeLink, action)) { _ => - redirect(s"/party/$partyId/bis", StatusCodes.Found) - } + formFields( + "player".as[String], + "piece".as[String].?, + "piece_type".as[String].?, + "link".as[String].?, + "action".as[String] + ) { (player, maybePiece, maybePieceType, maybeLink, action) => + onComplete(modifyBiSCall(partyId, player, maybePiece, maybePieceType, maybeLink, action)) { _ => + redirect(s"/party/$partyId/bis", StatusCodes.Found) + } } } } } } - private def modifyBiSCall(partyId: String, player: String, - maybePiece: Option[String], maybePieceType: Option[String], - maybeLink: Option[String], action: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { + private def modifyBiSCall( + partyId: String, + player: String, + maybePiece: Option[String], + maybePieceType: Option[String], + maybeLink: Option[String], + action: String + )(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { def getPiece(playerId: PlayerId, piece: String, pieceType: String) = Try(Piece(piece, PieceType.withName(pieceType), playerId.job)).toOption @@ -74,14 +86,15 @@ class BiSView(override val storage: ActorRef[Message], } PlayerId(partyId, player) 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", _) => - bisAction(playerId, piece, pieceType)(removePieceBiS(playerId, _)) - case (_, _, "create", Some(link)) => putBiS(playerId, link) - case _ => Future.failed(new Error(s"Could not perform $action")) - } + 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", _) => + bisAction(playerId, piece, pieceType)(removePieceBiS(playerId, _)) + case (_, _, "create", Some(link)) => putBiS(playerId, link) + case _ => Future.failed(new Error(s"Could not perform $action")) + } case _ => Future.failed(new Error(s"Could not construct player id from `$player`")) } } @@ -93,63 +106,68 @@ object BiSView { def template(partyId: String, party: Seq[Player], error: Option[String]): String = "" + - html(lang:="en", + html( + lang := "en", head( titleTag("Best in slot"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( h2("Best in slot"), - ErrorView.template(error), SearchLineView.template, - - form(action:=s"/party/$partyId/bis", method:="post")( - select(name:="player", id:="player", title:="player") - (for (player <- party) yield option(player.playerId.toString)), - select(name:="piece", id:="piece", title:="piece") - (for (piece <- Piece.available) yield option(piece)), - select(name:="piece_type", id:="piece_type", title:="piece type") - (for (pieceType <- PieceType.available) yield option(pieceType.toString)), - input(name:="action", id:="action", `type`:="hidden", value:="add"), - input(name:="add", id:="add", `type`:="submit", value:="add") + form(action := s"/party/$partyId/bis", method := "post")( + select(name := "player", id := "player", title := "player")( + for (player <- party) yield option(player.playerId.toString) + ), + select(name := "piece", id := "piece", title := "piece")( + for (piece <- Piece.available) yield option(piece) + ), + select(name := "piece_type", id := "piece_type", title := "piece type")( + for (pieceType <- PieceType.available) yield option(pieceType.toString) + ), + input(name := "action", id := "action", `type` := "hidden", value := "add"), + input(name := "add", id := "add", `type` := "submit", value := "add") ), - - form(action:=s"/party/$partyId/bis", method:="post")( - select(name:="player", id:="player", title:="player") - (for (player <- party) yield option(player.playerId.toString)), - input(name:="link", id:="link", placeholder:="player bis link", title:="link", `type`:="text"), - input(name:="action", id:="action", `type`:="hidden", value:="create"), - input(name:="add", id:="add", `type`:="submit", value:="add") + form(action := s"/party/$partyId/bis", method := "post")( + select(name := "player", id := "player", title := "player")( + for (player <- party) yield option(player.playerId.toString) + ), + input(name := "link", id := "link", placeholder := "player bis link", title := "link", `type` := "text"), + input(name := "action", id := "action", `type` := "hidden", value := "create"), + input(name := "add", id := "add", `type` := "submit", value := "add") ), - - table(id:="result")( + table(id := "result")( tr( th("player"), th("piece"), th("piece type"), th("") ), - for (player <- party; piece <- player.bis.pieces) yield tr( - td(`class`:="include_search")(player.playerId.toString), - td(`class`:="include_search")(piece.piece), - td(piece.pieceType.toString), - td( - form(action:=s"/party/$partyId/bis", method:="post")( - input(name:="player", id:="player", `type`:="hidden", value:=player.playerId.toString), - input(name:="piece", id:="piece", `type`:="hidden", value:=piece.piece), - input(name:="piece_type", id:="piece_type", `type`:="hidden", value:=piece.pieceType.toString), - input(name:="action", id:="action", `type`:="hidden", value:="remove"), - input(name:="remove", id:="remove", `type`:="submit", value:="x") + for (player <- party; piece <- player.bis.pieces) + yield tr( + td(`class` := "include_search")(player.playerId.toString), + td(`class` := "include_search")(piece.piece), + td(piece.pieceType.toString), + td( + form(action := s"/party/$partyId/bis", method := "post")( + input(name := "player", id := "player", `type` := "hidden", value := player.playerId.toString), + input(name := "piece", id := "piece", `type` := "hidden", value := piece.piece), + input( + name := "piece_type", + id := "piece_type", + `type` := "hidden", + value := piece.pieceType.toString + ), + input(name := "action", id := "action", `type` := "hidden", value := "remove"), + input(name := "remove", id := "remove", `type` := "submit", value := "x") + ) ) ) - ) ), - ExportToCSVView.template, BasePartyView.root(partyId), - script(src:="/static/table_search.js", `type`:="text/javascript") + script(src := "/static/table_search.js", `type` := "text/javascript") ) ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala index 5ce1ff7..9a35e81 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/ErrorView.scala @@ -14,7 +14,7 @@ import scalatags.Text.all._ object ErrorView { def template(error: Option[String]): Text.TypedTag[String] = error match { - case Some(text) => p(id:="error", s"Error occurs: $text") + case Some(text) => p(id := "error", s"Error occurs: $text") case None => p("") } } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala index 3edc9ee..0b76606 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/ExportToCSVView.scala @@ -15,7 +15,7 @@ object ExportToCSVView { def template: Text.TypedTag[String] = div( - button(onclick:="exportTableToCsv('result.csv')")("Export to CSV"), - script(src:="/static/table_export.js", `type`:="text/javascript") + button(onclick := "exportTableToCsv('result.csv')")("Export to CSV"), + script(src := "/static/table_export.js", `type` := "text/javascript") ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala index 8da73a0..97d772f 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/IndexView.scala @@ -20,10 +20,11 @@ import me.arcanis.ffxivbis.models.{PartyDescription, Permission, User} import scala.concurrent.Future import scala.util.{Failure, Success} -class IndexView(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends PlayerHelper with UserHelper { +class IndexView(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])(implicit + timeout: Timeout, + scheduler: Scheduler +) extends PlayerHelper + with UserHelper { def route: Route = createParty ~ getIndex @@ -31,19 +32,20 @@ class IndexView(override val storage: ActorRef[Message], path("party") { extractExecutionContext { implicit executionContext => post { - formFields("username".as[String], "password".as[String], "alias".as[String].?) { (username, password, maybeAlias) => - onComplete { - newPartyId.flatMap { partyId => - val user = User(partyId, username, password, Permission.admin) - addUser(user, isHashedPassword = false).flatMap { _ => - if (maybeAlias.getOrElse("").isEmpty) Future.successful(partyId) - else updateDescription(PartyDescription(partyId, maybeAlias)).map(_ => partyId) + formFields("username".as[String], "password".as[String], "alias".as[String].?) { + (username, password, maybeAlias) => + onComplete { + newPartyId.flatMap { partyId => + val user = User(partyId, username, password, Permission.admin) + addUser(user, isHashedPassword = false).flatMap { _ => + if (maybeAlias.getOrElse("").isEmpty) Future.successful(partyId) + else updateDescription(PartyDescription(partyId, maybeAlias)).map(_ => partyId) + } } + } { + case Success(partyId) => redirect(s"/party/$partyId", StatusCodes.Found) + case Failure(exception) => throw exception } - } { - case Success(partyId) => redirect(s"/party/$partyId", StatusCodes.Found) - case Failure(exception) => throw exception - } } } } @@ -69,26 +71,34 @@ object IndexView { html( head( titleTag("FFXIV loot helper"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( - form(action:=s"party", method:="post")( + form(action := s"party", method := "post")( label("create a new party"), - input(name:="alias", id:="alias", placeholder:="party alias", title:="alias", `type`:="text"), - input(name:="username", id:="username", placeholder:="username", title:="username", `type`:="text"), - input(name:="password", id:="password", placeholder:="password", title:="password", `type`:="password"), - input(name:="add", id:="add", `type`:="submit", value:="add") + input(name := "alias", id := "alias", placeholder := "party alias", title := "alias", `type` := "text"), + input( + name := "username", + id := "username", + placeholder := "username", + title := "username", + `type` := "text" + ), + input( + name := "password", + id := "password", + placeholder := "password", + title := "password", + `type` := "password" + ), + input(name := "add", id := "add", `type` := "submit", value := "add") ), - br, - - form(action:="/", method:="get")( + form(action := "/", method := "get")( label("already have party?"), - input(name:="partyId", id:="partyId", placeholder:="party id", title:="party id", `type`:="text"), - input(name:="go", id:="go", `type`:="submit", value:="go") + input(name := "partyId", id := "partyId", placeholder := "party id", title := "party id", `type` := "text"), + input(name := "go", id := "go", `type` := "submit", value := "go") ) ) ) } - 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 02581b7..a15d023 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/LootSuggestView.scala @@ -20,9 +20,9 @@ import me.arcanis.ffxivbis.models.{Job, Piece, PieceType, PlayerIdWithCounters} import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success, Try} -class LootSuggestView(override val storage: ActorRef[Message]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends LootHelper with Authorization { +class LootSuggestView(override val storage: ActorRef[Message])(implicit timeout: Timeout, scheduler: Scheduler) + extends LootHelper + with Authorization { def route: Route = getIndex ~ suggestLoot @@ -65,8 +65,10 @@ class LootSuggestView(override val storage: ActorRef[Message]) } } - private def suggestLootCall(partyId: String, maybePiece: Option[Piece]) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[PlayerIdWithCounters]] = + private def suggestLootCall(partyId: String, maybePiece: Option[Piece])(implicit + executionContext: ExecutionContext, + timeout: Timeout + ): Future[Seq[PlayerIdWithCounters]] = maybePiece match { case Some(piece) => suggestPiece(partyId, piece) case _ => Future.failed(new Error(s"Could not construct piece from `$maybePiece`")) @@ -77,36 +79,40 @@ object LootSuggestView { import scalatags.Text.all._ import scalatags.Text.tags2.{title => titleTag} - def template(partyId: String, party: Seq[PlayerIdWithCounters], piece: Option[Piece], - isFreeLoot: Boolean, error: Option[String]): String = + def template( + partyId: String, + party: Seq[PlayerIdWithCounters], + piece: Option[Piece], + isFreeLoot: Boolean, + error: Option[String] + ): String = "" + - html(lang:="en", + html( + lang := "en", head( titleTag("Suggest loot"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( h2("Suggest loot"), - for (part <- piece) yield p(s"Piece ${part.piece} (${part.pieceType})"), - ErrorView.template(error), SearchLineView.template, - - form(action:=s"/party/$partyId/suggest", method:="post")( - select(name:="piece", id:="piece", title:="piece") - (for (piece <- Piece.available) yield option(piece)), - select(name:="job", id:="job", title:="job") - (for (job <- Job.availableWithAnyJob) yield option(job.toString)), - select(name:="piece_type", id:="piece_type", title:="piece type") - (for (pieceType <- PieceType.available) yield option(pieceType.toString)), - input(name:="free_loot", id:="free_loot", title:="is free loot", `type`:="checkbox"), - label(`for`:="free_loot")("is free loot"), - input(name:="suggest", id:="suggest", `type`:="submit", value:="suggest") + form(action := s"/party/$partyId/suggest", method := "post")( + select(name := "piece", id := "piece", title := "piece")( + for (piece <- Piece.available) yield option(piece) + ), + select(name := "job", id := "job", title := "job")( + for (job <- Job.availableWithAnyJob) yield option(job.toString) + ), + select(name := "piece_type", id := "piece_type", title := "piece type")( + for (pieceType <- PieceType.available) yield option(pieceType.toString) + ), + input(name := "free_loot", id := "free_loot", title := "is free loot", `type` := "checkbox"), + label(`for` := "free_loot")("is free loot"), + input(name := "suggest", id := "suggest", `type` := "submit", value := "suggest") ), - - table(id:="result")( + table(id := "result")( tr( th("player"), th("is required"), @@ -115,28 +121,43 @@ object LootSuggestView { th("total pieces looted"), th("") ), - for (player <- party) yield tr( - td(`class`:="include_search")(player.playerId.toString), - td(player.isRequiredToString), - td(player.lootCount), - td(player.lootCountBiS), - td(player.lootCountTotal), - td( - form(action:=s"/party/$partyId/loot", method:="post")( - input(name:="player", id:="player", `type`:="hidden", value:=player.playerId.toString), - input(name:="piece", id:="piece", `type`:="hidden", value:=piece.map(_.piece).getOrElse("")), - input(name:="piece_type", id:="piece_type", `type`:="hidden", value:=piece.map(_.pieceType.toString).getOrElse("")), - input(name:="free_loot", id:="free_loot", `type`:="hidden", value:=(if (isFreeLoot) "yes" else "no")), - input(name:="action", id:="action", `type`:="hidden", value:="add"), - input(name:="add", id:="add", `type`:="submit", value:="add") + for (player <- party) + yield tr( + td(`class` := "include_search")(player.playerId.toString), + td(player.isRequiredToString), + td(player.lootCount), + td(player.lootCountBiS), + td(player.lootCountTotal), + td( + form(action := s"/party/$partyId/loot", method := "post")( + input(name := "player", id := "player", `type` := "hidden", value := player.playerId.toString), + input( + name := "piece", + id := "piece", + `type` := "hidden", + value := piece.map(_.piece).getOrElse("") + ), + input( + name := "piece_type", + id := "piece_type", + `type` := "hidden", + value := piece.map(_.pieceType.toString).getOrElse("") + ), + input( + name := "free_loot", + id := "free_loot", + `type` := "hidden", + value := (if (isFreeLoot) "yes" else "no") + ), + input(name := "action", id := "action", `type` := "hidden", value := "add"), + input(name := "add", id := "add", `type` := "submit", value := "add") + ) ) ) - ) ), - ExportToCSVView.template, BasePartyView.root(partyId), - script(src:="/static/table_search.js", `type`:="text/javascript") + script(src := "/static/table_search.js", `type` := "text/javascript") ) ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala index db10da8..a4d6ef2 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/LootView.scala @@ -20,9 +20,9 @@ import me.arcanis.ffxivbis.models.{Piece, PieceType, Player, PlayerId} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -class LootView(override val storage: ActorRef[Message]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends LootHelper with Authorization { +class LootView(override val storage: ActorRef[Message])(implicit timeout: Timeout, scheduler: Scheduler) + extends LootHelper + with Authorization { def route: Route = getLoot ~ modifyLoot @@ -32,11 +32,13 @@ class LootView(override val storage: ActorRef[Message]) authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ => get { complete { - loot(partyId, None).map { players => - LootView.template(partyId, players, None) - }.map { text => - (StatusCodes.OK, RootView.toHtml(text)) - } + loot(partyId, None) + .map { players => + LootView.template(partyId, players, None) + } + .map { text => + (StatusCodes.OK, RootView.toHtml(text)) + } } } } @@ -48,32 +50,42 @@ class LootView(override val storage: ActorRef[Message]) extractExecutionContext { implicit executionContext => authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ => post { - formFields("player".as[String], "piece".as[String], "piece_type".as[String], "action".as[String], "free_loot".as[String].?) { - (player, piece, pieceType, action, isFreeLoot) => - onComplete(modifyLootCall(partyId, player, piece, pieceType, isFreeLoot, action)) { _ => - redirect(s"/party/$partyId/loot", StatusCodes.Found) - } + formFields( + "player".as[String], + "piece".as[String], + "piece_type".as[String], + "action".as[String], + "free_loot".as[String].? + ) { (player, piece, pieceType, action, isFreeLoot) => + onComplete(modifyLootCall(partyId, player, piece, pieceType, isFreeLoot, action)) { _ => + redirect(s"/party/$partyId/loot", StatusCodes.Found) + } } } } } } - private def modifyLootCall(partyId: String, player: String, maybePiece: String, - maybePieceType: String, maybeFreeLoot: Option[String], - action: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { + private def modifyLootCall( + partyId: String, + player: String, + maybePiece: String, + maybePieceType: String, + maybeFreeLoot: Option[String], + action: String + )(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { import me.arcanis.ffxivbis.utils.Implicits._ def getPiece(playerId: PlayerId) = Try(Piece(maybePiece, PieceType.withName(maybePieceType), playerId.job)).toOption PlayerId(partyId, player) match { - case Some(playerId) => (getPiece(playerId), action) match { - case (Some(piece), "add") => addPieceLoot(playerId, piece, maybeFreeLoot) - case (Some(piece), "remove") => removePieceLoot(playerId, piece) - case _ => Future.failed(new Error(s"Could not construct piece from `$maybePiece ($maybePieceType)`")) - } + case Some(playerId) => + (getPiece(playerId), action) match { + case (Some(piece), "add") => addPieceLoot(playerId, piece, maybeFreeLoot) + case (Some(piece), "remove") => removePieceLoot(playerId, piece) + case _ => Future.failed(new Error(s"Could not construct piece from `$maybePiece ($maybePieceType)`")) + } case _ => Future.failed(new Error(s"Could not construct player id from `$player`")) } } @@ -85,32 +97,32 @@ object LootView { def template(partyId: String, party: Seq[Player], error: Option[String]): String = "" + - html(lang:="en", + html( + lang := "en", head( titleTag("Loot"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( h2("Loot"), - ErrorView.template(error), SearchLineView.template, - - form(action:=s"/party/$partyId/loot", method:="post")( - select(name:="player", id:="player", title:="player") - (for (player <- party) yield option(player.playerId.toString)), - select(name:="piece", id:="piece", title:="piece") - (for (piece <- Piece.available) yield option(piece)), - select(name:="piece_type", id:="piece_type", title:="piece type") - (for (pieceType <- PieceType.available) yield option(pieceType.toString)), - input(name:="free_loot", id:="free_loot", title:="is free loot", `type`:="checkbox"), - label(`for`:="free_loot")("is free loot"), - input(name:="action", id:="action", `type`:="hidden", value:="add"), - input(name:="add", id:="add", `type`:="submit", value:="add") + form(action := s"/party/$partyId/loot", method := "post")( + select(name := "player", id := "player", title := "player")( + for (player <- party) yield option(player.playerId.toString) + ), + select(name := "piece", id := "piece", title := "piece")( + for (piece <- Piece.available) yield option(piece) + ), + select(name := "piece_type", id := "piece_type", title := "piece type")( + for (pieceType <- PieceType.available) yield option(pieceType.toString) + ), + input(name := "free_loot", id := "free_loot", title := "is free loot", `type` := "checkbox"), + label(`for` := "free_loot")("is free loot"), + input(name := "action", id := "action", `type` := "hidden", value := "add"), + input(name := "add", id := "add", `type` := "submit", value := "add") ), - - table(id:="result")( + table(id := "result")( tr( th("player"), th("piece"), @@ -119,28 +131,33 @@ object LootView { th("timestamp"), th("") ), - for (player <- party; loot <- player.loot) yield tr( - td(`class`:="include_search")(player.playerId.toString), - td(`class`:="include_search")(loot.piece.piece), - td(loot.piece.pieceType.toString), - td(loot.isFreeLootToString), - td(loot.timestamp.toString), - td( - form(action:=s"/party/$partyId/loot", method:="post")( - input(name:="player", id:="player", `type`:="hidden", value:=player.playerId.toString), - input(name:="piece", id:="piece", `type`:="hidden", value:=loot.piece.piece), - input(name:="piece_type", id:="piece_type", `type`:="hidden", value:=loot.piece.pieceType.toString), - input(name:="free_loot", id:="free_loot", `type`:="hidden", value:=loot.isFreeLootToString), - input(name:="action", id:="action", `type`:="hidden", value:="remove"), - input(name:="remove", id:="remove", `type`:="submit", value:="x") + for (player <- party; loot <- player.loot) + yield tr( + td(`class` := "include_search")(player.playerId.toString), + td(`class` := "include_search")(loot.piece.piece), + td(loot.piece.pieceType.toString), + td(loot.isFreeLootToString), + td(loot.timestamp.toString), + td( + form(action := s"/party/$partyId/loot", method := "post")( + input(name := "player", id := "player", `type` := "hidden", value := player.playerId.toString), + input(name := "piece", id := "piece", `type` := "hidden", value := loot.piece.piece), + input( + name := "piece_type", + id := "piece_type", + `type` := "hidden", + value := loot.piece.pieceType.toString + ), + input(name := "free_loot", id := "free_loot", `type` := "hidden", value := loot.isFreeLootToString), + input(name := "action", id := "action", `type` := "hidden", value := "remove"), + input(name := "remove", id := "remove", `type` := "submit", value := "x") + ) ) ) - ) ), - ExportToCSVView.template, BasePartyView.root(partyId), - script(src:="/static/table_search.js", `type`:="text/javascript") + script(src := "/static/table_search.js", `type` := "text/javascript") ) ) diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala index 343b325..25c850d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/PlayerView.scala @@ -19,10 +19,11 @@ import me.arcanis.ffxivbis.models._ import scala.concurrent.{ExecutionContext, Future} -class PlayerView(override val storage: ActorRef[Message], - override val provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends PlayerHelper with Authorization { +class PlayerView(override val storage: ActorRef[Message], override val provider: ActorRef[BiSProviderMessage])(implicit + timeout: Timeout, + scheduler: Scheduler +) extends PlayerHelper + with Authorization { def route: Route = getParty ~ modifyParty @@ -32,11 +33,13 @@ class PlayerView(override val storage: ActorRef[Message], authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ => get { complete { - getPlayers(partyId, None).map { players => - PlayerView.template(partyId, players.map(_.withCounters(None)), None) - }.map { text => - (StatusCodes.OK, RootView.toHtml(text)) - } + getPlayers(partyId, None) + .map { players => + PlayerView.template(partyId, players.map(_.withCounters(None)), None) + } + .map { text => + (StatusCodes.OK, RootView.toHtml(text)) + } } } } @@ -48,21 +51,30 @@ class PlayerView(override val storage: ActorRef[Message], extractExecutionContext { implicit executionContext => authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ => post { - formFields("nick".as[String], "job".as[String], "priority".as[Int].?, "link".as[String].?, "action".as[String]) { - (nick, job, maybePriority, maybeLink, action) => - onComplete(modifyPartyCall(partyId, nick, job, maybePriority, maybeLink, action)) { _ => - redirect(s"/party/$partyId/players", StatusCodes.Found) - } + formFields( + "nick".as[String], + "job".as[String], + "priority".as[Int].?, + "link".as[String].?, + "action".as[String] + ) { (nick, job, maybePriority, maybeLink, action) => + onComplete(modifyPartyCall(partyId, nick, job, maybePriority, maybeLink, action)) { _ => + redirect(s"/party/$partyId/players", StatusCodes.Found) + } } } } } } - private def modifyPartyCall(partyId: String, nick: String, job: String, - maybePriority: Option[Int], maybeLink: Option[String], - action: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { + private def modifyPartyCall( + partyId: String, + nick: String, + job: String, + maybePriority: Option[Int], + maybeLink: Option[String], + action: String + )(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { def maybePlayerId = PlayerId(partyId, Some(nick), Some(job)) def player(playerId: PlayerId) = Player(-1, partyId, playerId.job, playerId.nick, BiS.empty, Seq.empty, maybeLink, maybePriority.getOrElse(0)) @@ -81,29 +93,32 @@ object PlayerView { def template(partyId: String, party: Seq[PlayerIdWithCounters], error: Option[String]): String = "" + - html(lang:="en", + html( + lang := "en", head( titleTag("Party"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( h2("Party"), - ErrorView.template(error), SearchLineView.template, - - form(action:=s"/party/$partyId/players", method:="post")( - input(name:="nick", id:="nick", placeholder:="nick", title:="nick", `type`:="nick"), - select(name:="job", id:="job", title:="job") - (for (job <- Job.available) yield option(job.toString)), - input(name:="link", id:="link", placeholder:="player bis link", title:="link", `type`:="text"), - input(name:="prioiry", id:="priority", placeholder:="priority", title:="priority", `type`:="number", value:="0"), - input(name:="action", id:="action", `type`:="hidden", value:="add"), - input(name:="add", id:="add", `type`:="submit", value:="add") + form(action := s"/party/$partyId/players", method := "post")( + input(name := "nick", id := "nick", placeholder := "nick", title := "nick", `type` := "nick"), + select(name := "job", id := "job", title := "job")(for (job <- Job.available) yield option(job.toString)), + input(name := "link", id := "link", placeholder := "player bis link", title := "link", `type` := "text"), + input( + name := "prioiry", + id := "priority", + placeholder := "priority", + title := "priority", + `type` := "number", + value := "0" + ), + input(name := "action", id := "action", `type` := "hidden", value := "add"), + input(name := "add", id := "add", `type` := "submit", value := "add") ), - - table(id:="result")( + table(id := "result")( tr( th("nick"), th("job"), @@ -112,26 +127,26 @@ object PlayerView { th("priority"), th("") ), - for (player <- party) yield tr( - td(`class`:="include_search")(player.nick), - td(`class`:="include_search")(player.job.toString), - td(player.lootCountBiS), - td(player.lootCountTotal), - td(player.priority), - td( - form(action:=s"/party/$partyId/players", method:="post")( - input(name:="nick", id:="nick", `type`:="hidden", value:=player.nick), - input(name:="job", id:="job", `type`:="hidden", value:=player.job.toString), - input(name:="action", id:="action", `type`:="hidden", value:="remove"), - input(name:="remove", id:="remove", `type`:="submit", value:="x") + for (player <- party) + yield tr( + td(`class` := "include_search")(player.nick), + td(`class` := "include_search")(player.job.toString), + td(player.lootCountBiS), + td(player.lootCountTotal), + td(player.priority), + td( + form(action := s"/party/$partyId/players", method := "post")( + input(name := "nick", id := "nick", `type` := "hidden", value := player.nick), + input(name := "job", id := "job", `type` := "hidden", value := player.job.toString), + input(name := "action", id := "action", `type` := "hidden", value := "remove"), + input(name := "remove", id := "remove", `type` := "submit", value := "x") + ) ) ) - ) ), - ExportToCSVView.template, BasePartyView.root(partyId), - script(src:="/static/table_search.js", `type`:="text/javascript") + script(src := "/static/table_search.js", `type` := "text/javascript") ) ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala index dabaa0e..62e3fed 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/RootView.scala @@ -15,9 +15,10 @@ import akka.http.scaladsl.server.Route import akka.util.Timeout import me.arcanis.ffxivbis.messages.{BiSProviderMessage, Message} -class RootView(storage: ActorRef[Message], - provider: ActorRef[BiSProviderMessage]) - (implicit timeout: Timeout, scheduler: Scheduler) { +class RootView(storage: ActorRef[Message], provider: ActorRef[BiSProviderMessage])(implicit + timeout: Timeout, + scheduler: Scheduler +) { private val basePartyView = new BasePartyView(storage, provider) private val indexView = new IndexView(storage, provider) @@ -30,7 +31,7 @@ class RootView(storage: ActorRef[Message], def route: Route = basePartyView.route ~ indexView.route ~ - biSView.route ~ lootView.route ~ lootSuggestView.route ~ playerView.route ~ userView.route + biSView.route ~ lootView.route ~ lootSuggestView.route ~ playerView.route ~ userView.route } object RootView { diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala index 9adffd0..ef2274d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/SearchLineView.scala @@ -16,8 +16,11 @@ object SearchLineView { def template: Text.TypedTag[String] = div( input( - `type`:="text", id:="search", onkeyup:="searchTable()", - placeholder:="search for data", title:="search" + `type` := "text", + id := "search", + onkeyup := "searchTable()", + placeholder := "search for data", + title := "search" ) ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala b/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala index 84aa310..68c7536 100644 --- a/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala +++ b/src/main/scala/me/arcanis/ffxivbis/http/view/UserView.scala @@ -20,9 +20,9 @@ import me.arcanis.ffxivbis.models.{Permission, User} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -class UserView(override val storage: ActorRef[Message]) - (implicit timeout: Timeout, scheduler: Scheduler) - extends UserHelper with Authorization { +class UserView(override val storage: ActorRef[Message])(implicit timeout: Timeout, scheduler: Scheduler) + extends UserHelper + with Authorization { def route: Route = getUsers ~ modifyUsers @@ -32,11 +32,13 @@ class UserView(override val storage: ActorRef[Message]) authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ => get { complete { - users(partyId).map { users => - UserView.template(partyId, users, None) - }.map { text => - (StatusCodes.OK, RootView.toHtml(text)) - } + users(partyId) + .map { users => + UserView.template(partyId, users, None) + } + .map { text => + (StatusCodes.OK, RootView.toHtml(text)) + } } } } @@ -50,8 +52,8 @@ class UserView(override val storage: ActorRef[Message]) post { formFields("username".as[String], "password".as[String].?, "permission".as[String].?, "action".as[String]) { (username, maybePassword, maybePermission, action) => - onComplete(modifyUsersCall(partyId, username, maybePassword, maybePermission, action)) { - case _ => redirect(s"/party/$partyId/users", StatusCodes.Found) + onComplete(modifyUsersCall(partyId, username, maybePassword, maybePermission, action)) { case _ => + redirect(s"/party/$partyId/users", StatusCodes.Found) } } } @@ -59,18 +61,26 @@ class UserView(override val storage: ActorRef[Message]) } } - private def modifyUsersCall(partyId: String, username: String, - maybePassword: Option[String], maybePermission: Option[String], - action: String) - (implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { + private def modifyUsersCall( + partyId: String, + username: String, + maybePassword: Option[String], + maybePermission: Option[String], + action: String + )(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Unit] = { def permission: Option[Permission.Value] = maybePermission.flatMap(p => Try(Permission.withName(p)).toOption) action match { - case "add" => (maybePassword, permission) match { - case (Some(password), Some(permission)) => addUser(User(partyId, username, password, permission), isHashedPassword = false) - case _ => Future.failed(new Error(s"Could not construct permission/password from `$maybePermission`/`$maybePassword`")) - } + case "add" => + (maybePassword, permission) match { + case (Some(password), Some(permission)) => + addUser(User(partyId, username, password, permission), isHashedPassword = false) + case _ => + Future.failed( + new Error(s"Could not construct permission/password from `$maybePermission`/`$maybePassword`") + ) + } case "remove" => removeUser(partyId, username) case _ => Future.failed(new Error(s"Could not perform $action")) } @@ -83,48 +93,57 @@ object UserView { def template(partyId: String, users: Seq[User], error: Option[String]) = "" + - html(lang:="en", + html( + lang := "en", head( titleTag("Users"), - link(rel:="stylesheet", `type`:="text/css", href:="/static/styles.css") + link(rel := "stylesheet", `type` := "text/css", href := "/static/styles.css") ), - body( h2("Users"), - ErrorView.template(error), SearchLineView.template, - - form(action:=s"/party/$partyId/users", method:="post")( - input(name:="username", id:="username", placeholder:="username", title:="username", `type`:="text"), - input(name:="password", id:="password", placeholder:="password", title:="password", `type`:="password"), - select(name:="permission", id:="permission", title:="permission")(option("get"), option("post")), - input(name:="action", id:="action", `type`:="hidden", value:="add"), - input(name:="add", id:="add", `type`:="submit", value:="add") + form(action := s"/party/$partyId/users", method := "post")( + input( + name := "username", + id := "username", + placeholder := "username", + title := "username", + `type` := "text" + ), + input( + name := "password", + id := "password", + placeholder := "password", + title := "password", + `type` := "password" + ), + select(name := "permission", id := "permission", title := "permission")(option("get"), option("post")), + input(name := "action", id := "action", `type` := "hidden", value := "add"), + input(name := "add", id := "add", `type` := "submit", value := "add") ), - - table(id:="result")( + table(id := "result")( tr( th("username"), th("permission"), th("") ), - for (user <- users) yield tr( - td(`class`:="include_search")(user.username), - td(user.permission.toString), - td( - form(action:=s"/party/$partyId/users", method:="post")( - input(name:="username", id:="username", `type`:="hidden", value:=user.username.toString), - input(name:="action", id:="action", `type`:="hidden", value:="remove"), - input(name:="remove", id:="remove", `type`:="submit", value:="x") + for (user <- users) + yield tr( + td(`class` := "include_search")(user.username), + td(user.permission.toString), + td( + form(action := s"/party/$partyId/users", method := "post")( + input(name := "username", id := "username", `type` := "hidden", value := user.username.toString), + input(name := "action", id := "action", `type` := "hidden", value := "remove"), + input(name := "remove", id := "remove", `type` := "submit", value := "x") + ) ) ) - ) ), - ExportToCSVView.template, BasePartyView.root(partyId), - script(src:="/static/table_search.js", `type`:="text/javascript") + script(src := "/static/table_search.js", `type` := "text/javascript") ) ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala b/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala index 3d4575b..07e16ed 100644 --- a/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala +++ b/src/main/scala/me/arcanis/ffxivbis/messages/DatabaseMessage.scala @@ -30,7 +30,8 @@ case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) exte } // loot handler -case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit]) extends DatabaseMessage { +case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit]) + extends DatabaseMessage { override def partyId: String = playerId.partyId } @@ -40,7 +41,8 @@ case class RemovePieceFrom(playerId: PlayerId, piece: Piece, replyTo: ActorRef[U override def partyId: String = playerId.partyId } -case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult]) extends DatabaseMessage +case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult]) + extends DatabaseMessage // party handler case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends DatabaseMessage { @@ -74,4 +76,4 @@ case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends DatabaseM case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends DatabaseMessage -case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends DatabaseMessage \ No newline at end of file +case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends DatabaseMessage diff --git a/src/main/scala/me/arcanis/ffxivbis/models/BiS.scala b/src/main/scala/me/arcanis/ffxivbis/models/BiS.scala index bfe2762..9fd5d64 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/BiS.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/BiS.scala @@ -16,18 +16,21 @@ case class BiS(pieces: Seq[Piece]) { } def upgrades: Map[PieceUpgrade, Int] = - pieces.groupBy(_.upgrade).foldLeft(Map.empty[PieceUpgrade, Int]) { - case (acc, (Some(k), v)) => acc + (k -> v.size) - case (acc, _) => acc - } withDefaultValue 0 + pieces + .groupBy(_.upgrade) + .foldLeft(Map.empty[PieceUpgrade, Int]) { + case (acc, (Some(k), v)) => acc + (k -> v.size) + case (acc, _) => acc + } + .withDefaultValue(0) def withPiece(piece: Piece): BiS = copy(pieces :+ piece) def withoutPiece(piece: Piece): BiS = copy(pieces.filterNot(_.strictEqual(piece))) override def equals(obj: Any): Boolean = { def comparePieces(left: Seq[Piece], right: Seq[Piece]): Boolean = - left.groupBy(identity).view.mapValues(_.size).forall { - case (key, count) => right.count(_.strictEqual(key)) == count + left.groupBy(identity).view.mapValues(_.size).forall { case (key, count) => + right.count(_.strictEqual(key)) == count } obj match { diff --git a/src/main/scala/me/arcanis/ffxivbis/models/Party.scala b/src/main/scala/me/arcanis/ffxivbis/models/Party.scala index 1976fc1..a2e5950 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/Party.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/Party.scala @@ -37,15 +37,19 @@ case class Party(partyDescription: PartyDescription, rules: Seq[String], players object Party { - def apply(party: PartyDescription, config: Config, - players: Map[Long, Player], bis: Seq[Loot], loot: Seq[Loot]): Party = { + def apply( + party: PartyDescription, + config: Config, + players: Map[Long, Player], + bis: Seq[Loot], + loot: Seq[Loot] + ): Party = { val bisByPlayer = bis.groupBy(_.playerId).view.mapValues(piece => BiS(piece.map(_.piece))) val lootByPlayer = loot.groupBy(_.playerId).view - val playersWithItems = players.foldLeft(Map.empty[PlayerId, Player]) { - case (acc, (playerId, player)) => - acc + (player.playerId -> player - .withBiS(bisByPlayer.get(playerId)) - .withLoot(lootByPlayer.getOrElse(playerId, Seq.empty))) + val playersWithItems = players.foldLeft(Map.empty[PlayerId, Player]) { case (acc, (playerId, player)) => + acc + (player.playerId -> player + .withBiS(bisByPlayer.get(playerId)) + .withLoot(lootByPlayer.getOrElse(playerId, Seq.empty))) } Party(party, getRules(config), playersWithItems) } diff --git a/src/main/scala/me/arcanis/ffxivbis/models/PartyDescription.scala b/src/main/scala/me/arcanis/ffxivbis/models/PartyDescription.scala index ea87e53..9da1f4e 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/PartyDescription.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/PartyDescription.scala @@ -16,4 +16,4 @@ case class PartyDescription(partyId: String, partyAlias: Option[String]) { object PartyDescription { def empty(partyId: String): PartyDescription = PartyDescription(partyId, None) -} \ No newline at end of file +} diff --git a/src/main/scala/me/arcanis/ffxivbis/models/Piece.scala b/src/main/scala/me/arcanis/ffxivbis/models/Piece.scala index d975439..7dd4603 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/Piece.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/Piece.scala @@ -76,8 +76,11 @@ case class Wrist(override val pieceType: PieceType.PieceType, override val job: 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 { +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 { @@ -120,8 +123,20 @@ object Piece { case other => throw new Error(s"Unknown item type $other") } - lazy val available: Seq[String] = Seq("weapon", - "head", "body", "hands", "legs", "feet", - "ears", "neck", "wrist", "left ring", "right ring", - "accessory upgrade", "body upgrade", "weapon upgrade") + lazy val available: Seq[String] = Seq( + "weapon", + "head", + "body", + "hands", + "legs", + "feet", + "ears", + "neck", + "wrist", + "left ring", + "right ring", + "accessory upgrade", + "body upgrade", + "weapon upgrade" + ) } diff --git a/src/main/scala/me/arcanis/ffxivbis/models/Player.scala b/src/main/scala/me/arcanis/ffxivbis/models/Player.scala index 0927db5..9821da1 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/Player.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/Player.scala @@ -8,14 +8,16 @@ */ package me.arcanis.ffxivbis.models -case class Player(id: Long, - partyId: String, - job: Job.Job, - nick: String, - bis: BiS, - loot: Seq[Loot], - link: Option[String] = None, - priority: Int = 0) { +case class Player( + id: Long, + partyId: String, + job: Job.Job, + nick: String, + bis: BiS, + loot: Seq[Loot], + link: Option[String] = None, + priority: Int = 0 +) { require(job ne Job.AnyJob, "AnyJob is not allowed") val playerId: PlayerId = PlayerId(partyId, job, nick) @@ -25,23 +27,29 @@ case class Player(id: Long, } def withCounters(piece: Option[Piece]): PlayerIdWithCounters = PlayerIdWithCounters( - partyId, job, nick, isRequired(piece), priority, - bisCountTotal(piece), lootCount(piece), - lootCountBiS(piece), lootCountTotal(piece)) + partyId, + job, + nick, + isRequired(piece), + priority, + bisCountTotal(piece), + lootCount(piece), + lootCountBiS(piece), + lootCountTotal(piece) + ) def withLoot(piece: Loot): Player = withLoot(Seq(piece)) def withLoot(list: Seq[Loot]): Player = { require(loot.forall(_.playerId == id), "player id must be same") copy(loot = loot ++ list) } - def isRequired(piece: Option[Piece]): Boolean = { + def isRequired(piece: Option[Piece]): Boolean = piece match { case None => false case Some(p) if !bis.hasPiece(p) => false case Some(p: PieceUpgrade) => bis.upgrades(p) > lootCount(piece) case Some(_) => lootCount(piece) == 0 } - } def bisCountTotal(piece: Option[Piece]): Int = bis.pieces.count(_.pieceType == PieceType.Savage) def lootCount(piece: Option[Piece]): Int = piece match { diff --git a/src/main/scala/me/arcanis/ffxivbis/models/PlayerIdWithCounters.scala b/src/main/scala/me/arcanis/ffxivbis/models/PlayerIdWithCounters.scala index 3e3627d..14703bb 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/PlayerIdWithCounters.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/PlayerIdWithCounters.scala @@ -8,16 +8,17 @@ */ package me.arcanis.ffxivbis.models -case class PlayerIdWithCounters(partyId: String, - job: Job.Job, - nick: String, - isRequired: Boolean, - priority: Int, - bisCountTotal: Int, - lootCount: Int, - lootCountBiS: Int, - lootCountTotal: Int) - extends PlayerIdBase { +case class PlayerIdWithCounters( + partyId: String, + job: Job.Job, + nick: String, + isRequired: Boolean, + priority: Int, + bisCountTotal: Int, + lootCount: Int, + lootCountBiS: Int, + lootCountTotal: Int +) extends PlayerIdBase { import PlayerIdWithCounters._ def gt(that: PlayerIdWithCounters, orderBy: Seq[String]): Boolean = @@ -31,7 +32,8 @@ case class PlayerIdWithCounters(partyId: String, "bisCountTotal" -> bisCountTotal, // the more pieces in bis the more priority "lootCount" -> -lootCount, // the less loot got the more priority "lootCountBiS" -> -lootCountBiS, // the less bis pieces looted the more priority - "lootCountTotal" -> -lootCountTotal) withDefaultValue 0 // the less pieces looted the more priority + "lootCountTotal" -> -lootCountTotal + ).withDefaultValue(0) // the less pieces looted the more priority private def withCounters(orderBy: Seq[String]): PlayerCountersComparator = PlayerCountersComparator(orderBy.map(counters): _*) diff --git a/src/main/scala/me/arcanis/ffxivbis/models/User.scala b/src/main/scala/me/arcanis/ffxivbis/models/User.scala index d15ada3..759b5e2 100644 --- a/src/main/scala/me/arcanis/ffxivbis/models/User.scala +++ b/src/main/scala/me/arcanis/ffxivbis/models/User.scala @@ -14,10 +14,7 @@ object Permission extends Enumeration { val get, post, admin = Value } -case class User(partyId: String, - username: String, - password: String, - permission: Permission.Value) { +case class User(partyId: String, username: String, password: String, permission: Permission.Value) { def hash: String = BCrypt.hashpw(password, BCrypt.gensalt) def verify(plain: String): Boolean = BCrypt.checkpw(plain, password) diff --git a/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala b/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala index 58e8bb0..dbea45a 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/PartyService.scala @@ -20,7 +20,8 @@ import scala.concurrent.duration.FiniteDuration import scala.concurrent.{ExecutionContext, Future} class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMessage]) - extends AbstractBehavior[Message](context) with StrictLogging { + extends AbstractBehavior[Message](context) + with StrictLogging { import me.arcanis.ffxivbis.utils.Implicits._ private val cacheTimeout: FiniteDuration = diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala deleted file mode 100644 index e99b883..0000000 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/Ariyala.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019 Evgeniy Alekseev. - * - * This file is part of ffxivbis - * (see https://github.com/arcan1s/ffxivbis). - * - * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause - */ -package me.arcanis.ffxivbis.service.bis - -import akka.http.scaladsl.model.Uri -import me.arcanis.ffxivbis.models.Job -import spray.json.{JsNumber, JsObject, JsString, deserializationError} - -import scala.concurrent.{ExecutionContext, Future} - -object Ariyala extends IdParser { - - 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 - case other => throw deserializationError(s"Invalid job name $other") - } - js.fields.get("datasets") match { - case Some(datasets: JsObject) => - val fields = datasets.fields - fields.getOrElse(apiJob, fields(job.toString)).asJsObject - .fields("normal").asJsObject - .fields("items").asJsObject - .fields.foldLeft(Map.empty[String, Long]) { - case (acc, (key, JsNumber(id))) => BisProvider.remapKey(key).map(k => acc + (k -> id.toLong)).getOrElse(acc) - case (acc, _) => acc - } - case other => throw deserializationError(s"Invalid json $other") - } - } - - 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 8b8f929..edcfb55 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/BisProvider.scala @@ -16,13 +16,17 @@ import akka.http.scaladsl.model._ import com.typesafe.scalalogging.StrictLogging import me.arcanis.ffxivbis.messages.{BiSProviderMessage, DownloadBiS} 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} 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 { + extends AbstractBehavior[BiSProviderMessage](context) + with XivApi + with StrictLogging { override def system: ClassicActorSystemProvider = context.system @@ -37,20 +41,22 @@ class BisProvider(context: ActorContext[BiSProviderMessage]) Behaviors.same } - override def onSignal: PartialFunction[Signal, Behavior[BiSProviderMessage]] = { - case PostStop => - shutdown() - Behaviors.same + override def onSignal: PartialFunction[Signal, Behavior[BiSProviderMessage]] = { case PostStop => + shutdown() + Behaviors.same } - private def get(link: String, job: Job.Job): Future[Seq[Piece]] = { - val url = Uri(link) - val id = Paths.get(link).normalize.getFileName.toString + private def get(link: String, job: Job.Job): Future[Seq[Piece]] = + try { + val url = Uri(link) + val id = Paths.get(link).normalize.getFileName.toString - 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)) - } + 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)) + } catch { + case exception: Exception => Future.failed(exception) + } } object BisProvider { @@ -58,16 +64,19 @@ object BisProvider { def apply(): Behavior[BiSProviderMessage] = Behaviors.setup[BiSProviderMessage](context => new BisProvider(context)) - private def parseBisJsonToPieces(job: Job.Job, - idParser: IdParser, - pieceTypes: Seq[Long] => Future[Map[Long, PieceType.PieceType]]) - (js: JsObject) - (implicit executionContext: ExecutionContext): Future[Seq[Piece]] = + private def parseBisJsonToPieces( + job: Job.Job, + idParser: Parser, + pieceTypes: Seq[Long] => Future[Map[Long, PieceType.PieceType]] + )(js: JsObject)(implicit executionContext: ExecutionContext): Future[Seq[Piece]] = idParser.parse(job, js).flatMap { pieces => pieceTypes(pieces.values.toSeq).map { types => - pieces.view.mapValues(types).map { - case (piece, pieceType) => Piece(piece, pieceType, job) - }.toSeq + pieces.view + .mapValues(types) + .map { case (piece, pieceType) => + Piece(piece, pieceType, job) + } + .toSeq } } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala deleted file mode 100644 index e516bfe..0000000 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/Etro.scala +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2019 Evgeniy Alekseev. - * - * This file is part of ffxivbis - * (see https://github.com/arcan1s/ffxivbis). - * - * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause - */ -package me.arcanis.ffxivbis.service.bis - -import akka.http.scaladsl.model.Uri -import me.arcanis.ffxivbis.models.Job -import spray.json.{JsNumber, JsObject, deserializationError} - -import scala.concurrent.{ExecutionContext, Future} - -object Etro extends IdParser { - - 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) - case (acc, _) => acc - } - } - - 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/RequestExecutor.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala index eb6be3c..d37dd35 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/RequestExecutor.scala @@ -29,19 +29,23 @@ trait RequestExecutor { system.classicSystem.dispatchers.lookup("me.arcanis.ffxivbis.default-dispatcher") def sendRequest[T](uri: Uri, parser: JsObject => Future[T]): Future[T] = - http.singleRequest(HttpRequest(uri = uri)).map { - case r: HttpResponse if r.status.isRedirection() => - val location = r.header[Location].get.uri - sendRequest(uri.withPath(location.path), parser) - case HttpResponse(status, _, entity, _) if status.isSuccess() => - entity.dataBytes - .fold(ByteString.empty)(_ ++ _) - .map(_.utf8String) - .map(result => parser(result.parseJson.asJsObject)) - .toMat(Sink.head)(Keep.right) - .run().flatten - case other => Future.failed(new Error(s"Invalid response from server $other")) - }.flatten + http + .singleRequest(HttpRequest(uri = uri)) + .map { + case r: HttpResponse if r.status.isRedirection() => + val location = r.header[Location].get.uri + sendRequest(uri.withPath(location.path), parser) + case HttpResponse(status, _, entity, _) if status.isSuccess() => + entity.dataBytes + .fold(ByteString.empty)(_ ++ _) + .map(_.utf8String) + .map(result => parser(result.parseJson.asJsObject)) + .toMat(Sink.head)(Keep.right) + .run() + .flatten + case other => Future.failed(new Error(s"Invalid response from server $other")) + } + .flatten def shutdown(): Unit = http.shutdownAllConnectionPools() } 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 3f3c1b3..88c4fe4 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/XivApi.scala @@ -24,22 +24,30 @@ trait XivApi extends RequestExecutor { def getPieceType(itemIds: Seq[Long]): Future[Map[Long, PieceType.PieceType]] = { val uriForItems = Uri(xivapiUrl) .withPath(Uri.Path / "item") - .withQuery(Uri.Query(Map( - "columns" -> Seq("ID", "GameContentLinks").mkString(","), - "ids" -> itemIds.mkString(","), - "private_key" -> xivapiKey.getOrElse("") - ))) + .withQuery( + Uri.Query( + Map( + "columns" -> Seq("ID", "GameContentLinks").mkString(","), + "ids" -> itemIds.mkString(","), + "private_key" -> xivapiKey.getOrElse("") + ) + ) + ) sendRequest(uriForItems, XivApi.parseXivapiJsonToShop).flatMap { shops => val shopIds = shops.values.map(_._2).toSet val columns = shops.values.map(pair => s"ItemCost${pair._1}").toSet val uriForShops = Uri(xivapiUrl) .withPath(Uri.Path / "specialshop") - .withQuery(Uri.Query(Map( - "columns" -> (columns + "ID").mkString(","), - "ids" -> shopIds.mkString(","), - "private_key" -> xivapiKey.getOrElse("") - ))) + .withQuery( + Uri.Query( + Map( + "columns" -> (columns + "ID").mkString(","), + "ids" -> shopIds.mkString(","), + "private_key" -> xivapiKey.getOrElse("") + ) + ) + ) sendRequest(uriForShops, XivApi.parseXivapiJsonToType(shops)) } @@ -48,14 +56,15 @@ trait XivApi extends RequestExecutor { object XivApi { - private def parseXivapiJsonToShop(js: JsObject) - (implicit executionContext: ExecutionContext): Future[Map[Long, (String, Long)]] = { - def extractTraderId(js: JsObject) = { + private def parseXivapiJsonToShop( + js: JsObject + )(implicit executionContext: ExecutionContext): Future[Map[Long, (String, Long)]] = { + def extractTraderId(js: JsObject) = js.fields - .get("Recipe").map(_ => "crafted" -> -1L) // you can craft this item - .orElse { // lets try shop items - js.fields("SpecialShop").asJsObject - .fields.collectFirst { + .get("Recipe") + .map(_ => "crafted" -> -1L) // you can craft this item + .orElse { // lets try shop items + js.fields("SpecialShop").asJsObject.fields.collectFirst { case (shopName, JsArray(array)) if shopName.startsWith("ItemReceive") => val shopId = array.head match { case JsNumber(id) => id.toLong @@ -63,32 +72,34 @@ object XivApi { } shopName.replace("ItemReceive", "") -> shopId } - }.getOrElse(throw deserializationError(s"Could not parse $js")) - } + } + .getOrElse(throw deserializationError(s"Could not parse $js")) Future { js.fields("Results") match { case array: JsArray => - array.elements.map(_.asJsObject.getFields("ID", "GameContentLinks") match { - case Seq(JsNumber(id), shop: JsObject) => id.toLong -> extractTraderId(shop.asJsObject) - case other => throw deserializationError(s"Could not parse $other") - }).toMap + array.elements + .map(_.asJsObject.getFields("ID", "GameContentLinks") match { + 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") } } } - private def parseXivapiJsonToType(shops: Map[Long, (String, Long)])(js: JsObject) - (implicit executionContext: ExecutionContext): Future[Map[Long, PieceType.PieceType]] = + private def parseXivapiJsonToType( + shops: Map[Long, (String, Long)] + )(js: JsObject)(implicit executionContext: ExecutionContext): Future[Map[Long, PieceType.PieceType]] = Future { val shopMap = js.fields("Results") match { case array: JsArray => - 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") - } + array.elements.collect { case shop: JsObject => + shop.fields("ID") match { + case JsNumber(id) => id.toLong -> shop + case other => throw deserializationError(s"Could not parse $other") + } }.toMap case other => throw deserializationError(s"Could not parse $other") } @@ -96,9 +107,8 @@ object XivApi { shops.map { case (itemId, (index, shopId)) => val pieceType = if (index == "crafted" && shopId == -1L) PieceType.Crafted - else { - Try(shopMap(shopId).fields(s"ItemCost$index").asJsObject) - .toOption + else + Try(shopMap(shopId).fields(s"ItemCost$index").asJsObject).toOption .getOrElse(throw new Exception(s"${shopMap(shopId).fields(s"ItemCost$index")}, $index")) .getFields("IsUnique", "StackSize") match { case Seq(JsNumber(isUnique), JsNumber(stackSize)) => @@ -106,7 +116,6 @@ object XivApi { else PieceType.Savage case other => throw deserializationError(s"Could not parse $other") } - } itemId -> pieceType } } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/parser/Parser.scala similarity index 54% rename from src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala rename to src/main/scala/me/arcanis/ffxivbis/service/bis/parser/Parser.scala index af30586..3d2b192 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/bis/IdParser.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/parser/Parser.scala @@ -1,4 +1,4 @@ -package me.arcanis.ffxivbis.service.bis +package me.arcanis.ffxivbis.service.bis.parser import akka.http.scaladsl.model.Uri import com.typesafe.scalalogging.StrictLogging @@ -7,10 +7,9 @@ import spray.json.JsObject import scala.concurrent.{ExecutionContext, Future} -trait IdParser extends StrictLogging { +trait Parser extends StrictLogging { - def parse(job: Job.Job, js: JsObject) - (implicit executionContext: ExecutionContext): Future[Map[String, Long]] + 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/parser/impl/Ariyala.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/parser/impl/Ariyala.scala new file mode 100644 index 0000000..bbe2cda --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/parser/impl/Ariyala.scala @@ -0,0 +1,45 @@ +package me.arcanis.ffxivbis.service.bis.parser.impl + +import akka.http.scaladsl.model.Uri +import me.arcanis.ffxivbis.models.Job +import me.arcanis.ffxivbis.service.bis.BisProvider +import me.arcanis.ffxivbis.service.bis.parser.Parser +import spray.json.{deserializationError, JsNumber, JsObject, JsString} + +import scala.concurrent.{ExecutionContext, Future} + +object Ariyala extends Parser { + + 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 + case other => throw deserializationError(s"Invalid job name $other") + } + js.fields.get("datasets") match { + case Some(datasets: JsObject) => + val fields = datasets.fields + fields + .getOrElse(apiJob, fields(job.toString)) + .asJsObject + .fields("normal") + .asJsObject + .fields("items") + .asJsObject + .fields + .foldLeft(Map.empty[String, Long]) { + case (acc, (key, JsNumber(id))) => + BisProvider.remapKey(key).map(k => acc + (k -> id.toLong)).getOrElse(acc) + case (acc, _) => acc + } + case other => throw deserializationError(s"Invalid json $other") + } + } + + 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/parser/impl/Etro.scala b/src/main/scala/me/arcanis/ffxivbis/service/bis/parser/impl/Etro.scala new file mode 100644 index 0000000..304a879 --- /dev/null +++ b/src/main/scala/me/arcanis/ffxivbis/service/bis/parser/impl/Etro.scala @@ -0,0 +1,25 @@ +package me.arcanis.ffxivbis.service.bis.parser.impl + +import akka.http.scaladsl.model.Uri +import me.arcanis.ffxivbis.models.Job +import me.arcanis.ffxivbis.service.bis.BisProvider +import me.arcanis.ffxivbis.service.bis.parser.Parser +import spray.json.{JsNumber, JsObject} + +import scala.concurrent.{ExecutionContext, Future} + +object Etro extends Parser { + + 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) + case (acc, _) => acc + } + } + + 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/Database.scala b/src/main/scala/me/arcanis/ffxivbis/service/database/Database.scala similarity index 90% rename from src/main/scala/me/arcanis/ffxivbis/service/Database.scala rename to src/main/scala/me/arcanis/ffxivbis/service/database/Database.scala index 354e2a4..89d81ba 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/Database.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/database/Database.scala @@ -6,15 +6,15 @@ * * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause */ -package me.arcanis.ffxivbis.service +package me.arcanis.ffxivbis.service.database import akka.actor.typed.Behavior import akka.actor.typed.scaladsl.Behaviors import com.typesafe.config.Config import com.typesafe.scalalogging.StrictLogging -import me.arcanis.ffxivbis.messages.{DatabaseMessage} +import me.arcanis.ffxivbis.messages.DatabaseMessage import me.arcanis.ffxivbis.models.{Party, Player, PlayerId} -import me.arcanis.ffxivbis.service.impl.DatabaseImpl +import me.arcanis.ffxivbis.service.database.impl.DatabaseImpl import me.arcanis.ffxivbis.storage.DatabaseProfile import scala.concurrent.{ExecutionContext, Future} diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseBiSHandler.scala similarity index 85% rename from src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala rename to src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseBiSHandler.scala index d134f21..fc37c51 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseBiSHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseBiSHandler.scala @@ -6,13 +6,13 @@ * * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause */ -package me.arcanis.ffxivbis.service.impl +package me.arcanis.ffxivbis.service.database.impl import akka.actor.typed.scaladsl.Behaviors import me.arcanis.ffxivbis.messages.{AddPieceToBis, DatabaseMessage, GetBiS, RemovePieceFromBiS, RemovePiecesFromBiS} -import me.arcanis.ffxivbis.service.Database +import me.arcanis.ffxivbis.service.database.Database -trait DatabaseBiSHandler { this: Database => +trait DatabaseBiSHandler { this: Database => def bisHandler: DatabaseMessage.Handler = { case AddPieceToBis(playerId, piece, client) => @@ -23,7 +23,7 @@ trait DatabaseBiSHandler { this: Database => getParty(partyId, withBiS = true, withLoot = false) .map(filterParty(_, maybePlayerId)) .foreach(client ! _) - Behaviors.same + Behaviors.same case RemovePieceFromBiS(playerId, piece, client) => profile.deletePieceBiS(playerId, piece).foreach(_ => client ! ()) @@ -34,4 +34,3 @@ trait DatabaseBiSHandler { this: Database => Behaviors.same } } - diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseImpl.scala similarity index 70% rename from src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala rename to src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseImpl.scala index f7999b8..8d006c5 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseImpl.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseImpl.scala @@ -6,23 +6,26 @@ * * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause */ -package me.arcanis.ffxivbis.service.impl +package me.arcanis.ffxivbis.service.database.impl import akka.actor.typed.{Behavior, DispatcherSelector} import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext} import com.typesafe.config.Config import me.arcanis.ffxivbis.messages.DatabaseMessage -import me.arcanis.ffxivbis.service.Database +import me.arcanis.ffxivbis.service.database.Database import me.arcanis.ffxivbis.storage.DatabaseProfile import scala.concurrent.ExecutionContext class DatabaseImpl(context: ActorContext[DatabaseMessage]) - extends AbstractBehavior[DatabaseMessage](context) with Database - with DatabaseBiSHandler with DatabaseLootHandler - with DatabasePartyHandler with DatabaseUserHandler { + extends AbstractBehavior[DatabaseMessage](context) + with Database + with DatabaseBiSHandler + with DatabaseLootHandler + with DatabasePartyHandler + with DatabaseUserHandler { - override implicit val executionContext: ExecutionContext = { + implicit override val executionContext: ExecutionContext = { val selector = DispatcherSelector.fromConfig("me.arcanis.ffxivbis.default-dispatcher") context.system.dispatchers.lookup(selector) } @@ -32,5 +35,5 @@ class DatabaseImpl(context: ActorContext[DatabaseMessage]) override def onMessage(msg: DatabaseMessage): Behavior[DatabaseMessage] = handle(msg) private def handle: DatabaseMessage.Handler = - bisHandler orElse lootHandler orElse partyHandler orElse userHandler + bisHandler.orElse(lootHandler).orElse(partyHandler).orElse(userHandler) } diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseLootHandler.scala similarity index 92% rename from src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala rename to src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseLootHandler.scala index c90e0f4..755b21d 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseLootHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseLootHandler.scala @@ -6,14 +6,13 @@ * * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause */ -package me.arcanis.ffxivbis.service.impl +package me.arcanis.ffxivbis.service.database.impl import java.time.Instant - import akka.actor.typed.scaladsl.Behaviors import me.arcanis.ffxivbis.messages.{AddPieceTo, DatabaseMessage, GetLoot, RemovePieceFrom, SuggestLoot} import me.arcanis.ffxivbis.models.Loot -import me.arcanis.ffxivbis.service.Database +import me.arcanis.ffxivbis.service.database.Database trait DatabaseLootHandler { this: Database => diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabasePartyHandler.scala similarity index 63% rename from src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala rename to src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabasePartyHandler.scala index defc06b..49b147f 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabasePartyHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabasePartyHandler.scala @@ -6,16 +6,16 @@ * * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause */ -package me.arcanis.ffxivbis.service.impl +package me.arcanis.ffxivbis.service.database.impl import akka.actor.typed.scaladsl.Behaviors import me.arcanis.ffxivbis.messages.{AddPlayer, DatabaseMessage, GetParty, GetPartyDescription, GetPlayer, RemovePlayer, UpdateParty} import me.arcanis.ffxivbis.models.{BiS, Player} -import me.arcanis.ffxivbis.service.Database +import me.arcanis.ffxivbis.service.database.Database import scala.concurrent.Future -trait DatabasePartyHandler { this: Database => +trait DatabasePartyHandler { this: Database => def partyHandler: DatabaseMessage.Handler = { case AddPlayer(player, client) => @@ -31,16 +31,26 @@ trait DatabasePartyHandler { this: Database => Behaviors.same case GetPlayer(playerId, client) => - val player = profile.getPlayerFull(playerId).flatMap { maybePlayerData => - Future.traverse(maybePlayerData.toSeq) { playerData => - for { - bis <- profile.getPiecesBiS(playerId) - loot <- profile.getPieces(playerId) - } yield Player(playerData.id, playerId.partyId, playerId.job, - playerId.nick, BiS(bis.map(_.piece)), loot, - playerData.link, playerData.priority) + val player = profile + .getPlayerFull(playerId) + .flatMap { maybePlayerData => + Future.traverse(maybePlayerData.toSeq) { playerData => + for { + bis <- profile.getPiecesBiS(playerId) + loot <- profile.getPieces(playerId) + } yield Player( + playerData.id, + playerId.partyId, + playerId.job, + playerId.nick, + BiS(bis.map(_.piece)), + loot, + playerData.link, + playerData.priority + ) + } } - }.map(_.headOption) + .map(_.headOption) player.foreach(client ! _) Behaviors.same diff --git a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseUserHandler.scala similarity index 91% rename from src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala rename to src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseUserHandler.scala index d186225..5131034 100644 --- a/src/main/scala/me/arcanis/ffxivbis/service/impl/DatabaseUserHandler.scala +++ b/src/main/scala/me/arcanis/ffxivbis/service/database/impl/DatabaseUserHandler.scala @@ -6,11 +6,11 @@ * * License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause */ -package me.arcanis.ffxivbis.service.impl +package me.arcanis.ffxivbis.service.database.impl import akka.actor.typed.scaladsl.Behaviors import me.arcanis.ffxivbis.messages.{AddUser, DatabaseMessage, DeleteUser, Exists, GetUser, GetUsers} -import me.arcanis.ffxivbis.service.Database +import me.arcanis.ffxivbis.service.database.Database trait DatabaseUserHandler { this: Database => diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/BiSProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/BiSProfile.scala index cf3015b..e8f3e05 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/BiSProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/BiSProfile.scala @@ -18,16 +18,17 @@ import scala.concurrent.Future trait BiSProfile { this: DatabaseProfile => import dbConfig.profile.api._ - case class BiSRep(playerId: Long, created: Long, piece: String, - pieceType: String, job: String) { + case class BiSRep(playerId: Long, created: Long, piece: String, pieceType: String, job: String) { def toLoot: Loot = Loot( - playerId, Piece(piece, PieceType.withName(pieceType), Job.withName(job)), - Instant.ofEpochMilli(created), isFreeLoot = false) + playerId, + Piece(piece, PieceType.withName(pieceType), Job.withName(job)), + Instant.ofEpochMilli(created), + isFreeLoot = false + ) } object BiSRep { def fromPiece(playerId: Long, piece: Piece): BiSRep = - BiSRep(playerId, DatabaseProfile.now, piece.piece, - piece.pieceType.toString, piece.job.toString) + BiSRep(playerId, DatabaseProfile.now, piece.piece, piece.pieceType.toString, piece.job.toString) } class BiSPieces(tag: Tag) extends Table[BiSRep](tag, "bis") { diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala index 5b6a6a0..d287125 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/DatabaseProfile.scala @@ -18,7 +18,11 @@ import slick.jdbc.JdbcProfile import scala.concurrent.{ExecutionContext, Future} class DatabaseProfile(context: ExecutionContext, config: Config) - extends BiSProfile with LootProfile with PartyProfile with PlayersProfile with UsersProfile { + extends BiSProfile + with LootProfile + with PartyProfile + with PlayersProfile + with UsersProfile { implicit val executionContext: ExecutionContext = context diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/LootProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/LootProfile.scala index eeecb31..32c3165 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/LootProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/LootProfile.scala @@ -18,19 +18,33 @@ import scala.concurrent.Future trait LootProfile { this: DatabaseProfile => import dbConfig.profile.api._ - case class LootRep(lootId: Option[Long], playerId: Long, created: Long, - piece: String, pieceType: String, job: String, - isFreeLoot: Int) { + case class LootRep( + lootId: Option[Long], + playerId: Long, + created: Long, + piece: String, + pieceType: String, + job: String, + isFreeLoot: Int + ) { def toLoot: Loot = Loot( playerId, Piece(piece, PieceType.withName(pieceType), Job.withName(job)), - Instant.ofEpochMilli(created), isFreeLoot == 1) + Instant.ofEpochMilli(created), + isFreeLoot == 1 + ) } object LootRep { def fromLoot(playerId: Long, loot: Loot): LootRep = - LootRep(None, playerId, loot.timestamp.toEpochMilli, loot.piece.piece, - loot.piece.pieceType.toString, loot.piece.job.toString, - if (loot.isFreeLoot) 1 else 0) + LootRep( + None, + playerId, + loot.timestamp.toEpochMilli, + loot.piece.piece, + loot.piece.pieceType.toString, + loot.piece.job.toString, + if (loot.isFreeLoot) 1 else 0 + ) } class LootPieces(tag: Tag) extends Table[LootRep](tag, "loot") { @@ -48,7 +62,7 @@ trait LootProfile { this: DatabaseProfile => def fkPlayerId: ForeignKeyQuery[Players, PlayerRep] = foreignKey("player_id", playerId, playersTable)(_.playerId, onDelete = ForeignKeyAction.Cascade) def lootOwnerIdx: Index = - index("loot_owner_idx", (playerId), unique = false) + index("loot_owner_idx", playerId, unique = false) } def deletePieceById(loot: Loot)(playerId: Long): Future[Int] = diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala b/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala index 330cfda..c2ab08e 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/Migration.scala @@ -11,13 +11,13 @@ package me.arcanis.ffxivbis.storage import com.typesafe.config.Config import org.flywaydb.core.Flyway import org.flywaydb.core.api.configuration.ClassicConfiguration +import org.flywaydb.core.api.output.MigrateResult -import scala.concurrent.Future import scala.util.Try class Migration(config: Config) { - def performMigration(): Try[Boolean] = { + def performMigration(): Try[MigrateResult] = { val section = DatabaseProfile.getSection(config) val url = section.getString("db.url") @@ -34,11 +34,11 @@ class Migration(config: Config) { flywayConfiguration.setDataSource(url, username, password) val flyway = new Flyway(flywayConfiguration) - Try(flyway.migrate().success) + Try(flyway.migrate()) } } object Migration { - def apply(config: Config): Try[Boolean] = new Migration(config).performMigration() + def apply(config: Config): Try[MigrateResult] = new Migration(config).performMigration() } diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/PartyProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/PartyProfile.scala index 59676b3..d79bf48 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/PartyProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/PartyProfile.scala @@ -15,8 +15,7 @@ import scala.concurrent.Future trait PartyProfile { this: DatabaseProfile => import dbConfig.profile.api._ - case class PartyRep(partyId: Option[Long], partyName: String, - partyAlias: Option[String]) { + case class PartyRep(partyId: Option[Long], partyName: String, partyAlias: Option[String]) { def toDescription: PartyDescription = PartyDescription(partyName, partyAlias) } object PartyRep { @@ -34,7 +33,9 @@ trait PartyProfile { this: DatabaseProfile => } def getPartyDescription(partyId: String): Future[PartyDescription] = - db.run(partyDescription(partyId).result.headOption.map(_.map(_.toDescription).getOrElse(PartyDescription.empty(partyId)))) + db.run( + partyDescription(partyId).result.headOption.map(_.map(_.toDescription).getOrElse(PartyDescription.empty(partyId))) + ) def getUniquePartyId(partyId: String): Future[Option[Long]] = db.run(partyDescription(partyId).map(_.partyId).result.headOption) def insertPartyDescription(partyDescription: PartyDescription): Future[Int] = @@ -43,7 +44,6 @@ trait PartyProfile { this: DatabaseProfile => case _ => db.run(partiesTable.insertOrUpdate(PartyRep.fromDescription(partyDescription, None))) } - private def partyDescription(partyId: String) = partiesTable.filter(_.partyName === partyId) -} \ No newline at end of file +} diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/PlayersProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/PlayersProfile.scala index 568d305..607a3f3 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/PlayersProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/PlayersProfile.scala @@ -15,16 +15,21 @@ import scala.concurrent.Future trait PlayersProfile { this: DatabaseProfile => import dbConfig.profile.api._ - case class PlayerRep(partyId: String, playerId: Option[Long], created: Long, - nick: String, job: String, link: Option[String], priority: Int) { + case class PlayerRep( + partyId: String, + playerId: Option[Long], + created: Long, + nick: String, + job: String, + link: Option[String], + priority: Int + ) { def toPlayer: Player = - Player(playerId.getOrElse(-1), partyId, Job.withName(job), nick, - BiS.empty, Seq.empty, link, priority) + Player(playerId.getOrElse(-1), partyId, Job.withName(job), nick, BiS.empty, Seq.empty, link, priority) } object PlayerRep { def fromPlayer(player: Player, id: Option[Long]): PlayerRep = - PlayerRep(player.partyId, id, DatabaseProfile.now, player.nick, - player.job.toString, player.link, player.priority) + PlayerRep(player.partyId, id, DatabaseProfile.now, player.nick, player.job.toString, player.link, player.priority) } class Players(tag: Tag) extends Table[PlayerRep](tag, "players") { @@ -42,10 +47,11 @@ trait PlayersProfile { this: DatabaseProfile => def deletePlayer(playerId: PlayerId): Future[Int] = db.run(player(playerId).delete) def getParty(partyId: String): Future[Map[Long, Player]] = - db.run(players(partyId).result).map(_.foldLeft(Map.empty[Long, Player]) { - case (acc, p @ PlayerRep(_, Some(id), _, _, _, _, _)) => acc + (id -> p.toPlayer) - case (acc, _) => acc - }) + db.run(players(partyId).result) + .map(_.foldLeft(Map.empty[Long, Player]) { + case (acc, p @ PlayerRep(_, Some(id), _, _, _, _, _)) => acc + (id -> p.toPlayer) + case (acc, _) => acc + }) def getPlayer(playerId: PlayerId): Future[Option[Long]] = db.run(player(playerId).map(_.playerId).result.headOption) def getPlayerFull(playerId: PlayerId): Future[Option[Player]] = diff --git a/src/main/scala/me/arcanis/ffxivbis/storage/UsersProfile.scala b/src/main/scala/me/arcanis/ffxivbis/storage/UsersProfile.scala index 761b421..7a5a09a 100644 --- a/src/main/scala/me/arcanis/ffxivbis/storage/UsersProfile.scala +++ b/src/main/scala/me/arcanis/ffxivbis/storage/UsersProfile.scala @@ -16,8 +16,7 @@ import scala.concurrent.Future trait UsersProfile { this: DatabaseProfile => import dbConfig.profile.api._ - case class UserRep(partyId: String, userId: Option[Long], username: String, - password: String, permission: String) { + case class UserRep(partyId: String, userId: Option[Long], username: String, password: String, permission: String) { def toUser: User = User(partyId, username, password, Permission.withName(permission)) } object UserRep { diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala index 0af7e1e..9ca5a3b 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/BiSEndpointTest.scala @@ -12,7 +12,8 @@ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser} import me.arcanis.ffxivbis.models.{BiS, Job} import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.{Database, PartyService} +import me.arcanis.ffxivbis.service.database.Database +import me.arcanis.ffxivbis.service.PartyService import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala index 4cff35e..8a9e935 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/LootEndpointTest.scala @@ -12,7 +12,8 @@ import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser} -import me.arcanis.ffxivbis.service.{Database, PartyService} +import me.arcanis.ffxivbis.service.database.Database +import me.arcanis.ffxivbis.service.PartyService import me.arcanis.ffxivbis.storage.Migration import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala index 48d1d42..6cbb3d9 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PartyEndpointTest.scala @@ -12,7 +12,8 @@ import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.messages.AddUser import me.arcanis.ffxivbis.models.PartyDescription import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.{Database, PartyService} +import me.arcanis.ffxivbis.service.database.Database +import me.arcanis.ffxivbis.service.PartyService import me.arcanis.ffxivbis.storage.Migration import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala index aa9bd92..3dd649f 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/PlayerEndpointTest.scala @@ -11,7 +11,8 @@ import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser} import me.arcanis.ffxivbis.service.bis.BisProvider -import me.arcanis.ffxivbis.service.{Database, PartyService} +import me.arcanis.ffxivbis.service.database.Database +import me.arcanis.ffxivbis.service.PartyService import me.arcanis.ffxivbis.storage.Migration import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike diff --git a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala index 005b4b2..a965acf 100644 --- a/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/http/api/v1/UserEndpointTest.scala @@ -8,12 +8,12 @@ import akka.testkit.TestKit import com.typesafe.config.Config import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.http.api.v1.json._ -import me.arcanis.ffxivbis.service.{Database, PartyService} +import me.arcanis.ffxivbis.service.PartyService +import me.arcanis.ffxivbis.service.database.Database import me.arcanis.ffxivbis.storage.Migration import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpecLike -import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseBiSHandlerTest.scala similarity index 98% rename from src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala rename to src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseBiSHandlerTest.scala index c8360eb..35eef8b 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseBiSHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseBiSHandlerTest.scala @@ -1,12 +1,12 @@ -package me.arcanis.ffxivbis.service +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.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare +import me.arcanis.ffxivbis.{Fixtures, Settings} import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseLootHandlerTest.scala similarity index 98% rename from src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala rename to src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseLootHandlerTest.scala index 08f335d..2821e4d 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseLootHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseLootHandlerTest.scala @@ -1,12 +1,12 @@ -package me.arcanis.ffxivbis.service +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.{AddPieceTo, AddPlayer, GetLoot, RemovePieceFrom} -import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare +import me.arcanis.ffxivbis.{Fixtures, Settings} import org.scalatest.wordspec.AnyWordSpecLike import scala.concurrent.Await diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabasePartyHandlerTest.scala similarity index 97% rename from src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala rename to src/test/scala/me/arcanis/ffxivbis/service/database/DatabasePartyHandlerTest.scala index 03a2614..eea1f4a 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabasePartyHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabasePartyHandlerTest.scala @@ -1,14 +1,13 @@ -package me.arcanis.ffxivbis.service +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.{Fixtures, Settings} import me.arcanis.ffxivbis.models._ import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare +import me.arcanis.ffxivbis.{Fixtures, Settings} import org.scalatest.wordspec.AnyWordSpecLike -import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps diff --git a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseUserHandlerTest.scala similarity index 97% rename from src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala rename to src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseUserHandlerTest.scala index 6e90561..fb9d9ef 100644 --- a/src/test/scala/me/arcanis/ffxivbis/service/DatabaseUserHandlerTest.scala +++ b/src/test/scala/me/arcanis/ffxivbis/service/database/DatabaseUserHandlerTest.scala @@ -1,14 +1,13 @@ -package me.arcanis.ffxivbis.service +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.models.User -import me.arcanis.ffxivbis.{Fixtures, Settings} import me.arcanis.ffxivbis.storage.Migration import me.arcanis.ffxivbis.utils.Compare +import me.arcanis.ffxivbis.{Fixtures, Settings} import org.scalatest.wordspec.AnyWordSpecLike -import scala.concurrent.Await import scala.concurrent.duration._ import scala.language.postfixOps