mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-25 01:37:17 +00:00
add support of party alias
This commit is contained in:
parent
1e6064e081
commit
16ce0bf61c
@ -0,0 +1,5 @@
|
|||||||
|
create table parties (
|
||||||
|
player_id bigserial unique,
|
||||||
|
party_name text not null,
|
||||||
|
party_alias text);
|
||||||
|
create unique index parties_party_name_idx on parties(party_name);
|
@ -0,0 +1,5 @@
|
|||||||
|
create table parties (
|
||||||
|
player_id integer primary key autoincrement,
|
||||||
|
party_name text not null,
|
||||||
|
party_alias text);
|
||||||
|
create unique index parties_party_name_idx on parties(party_name);
|
@ -16,7 +16,9 @@ import me.arcanis.ffxivbis.service.Ariyala
|
|||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class AriyalaHelper(ariyala: ActorRef) {
|
trait AriyalaHelper {
|
||||||
|
|
||||||
|
def ariyala: ActorRef
|
||||||
|
|
||||||
def downloadBiS(link: String, job: Job.Job)
|
def downloadBiS(link: String, job: Job.Job)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[BiS] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[BiS] =
|
||||||
|
@ -17,7 +17,9 @@ import me.arcanis.ffxivbis.service.impl.DatabaseBiSHandler
|
|||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class BiSHelper(storage: ActorRef, ariyala: ActorRef) extends AriyalaHelper(ariyala) {
|
trait BiSHelper extends AriyalaHelper {
|
||||||
|
|
||||||
|
def storage: ActorRef
|
||||||
|
|
||||||
def addPieceBiS(playerId: PlayerId, piece: Piece)
|
def addPieceBiS(playerId: PlayerId, piece: Piece)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
|
@ -18,7 +18,9 @@ import me.arcanis.ffxivbis.service.impl.DatabaseLootHandler
|
|||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class LootHelper(storage: ActorRef) {
|
trait LootHelper {
|
||||||
|
|
||||||
|
def storage: ActorRef
|
||||||
|
|
||||||
def addPieceLoot(playerId: PlayerId, piece: Piece)
|
def addPieceLoot(playerId: PlayerId, piece: Piece)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
|
@ -12,12 +12,14 @@ import akka.actor.ActorRef
|
|||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
import me.arcanis.ffxivbis.http.api.v1.json.ApiAction
|
||||||
import me.arcanis.ffxivbis.models.{Party, Player, PlayerId}
|
import me.arcanis.ffxivbis.models.{Party, PartyDescription, Player, PlayerId}
|
||||||
import me.arcanis.ffxivbis.service.impl.{DatabaseBiSHandler, DatabasePartyHandler}
|
import me.arcanis.ffxivbis.service.impl.{DatabaseBiSHandler, DatabasePartyHandler}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class PlayerHelper(storage: ActorRef, ariyala: ActorRef) extends AriyalaHelper(ariyala) {
|
trait PlayerHelper extends AriyalaHelper {
|
||||||
|
|
||||||
|
def storage: ActorRef
|
||||||
|
|
||||||
def addPlayer(player: Player)
|
def addPlayer(player: Player)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
@ -38,6 +40,10 @@ class PlayerHelper(storage: ActorRef, ariyala: ActorRef) extends AriyalaHelper(a
|
|||||||
case ApiAction.remove => removePlayer(player.playerId)
|
case ApiAction.remove => removePlayer(player.playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getPartyDescription(partyId: String)
|
||||||
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[PartyDescription] =
|
||||||
|
(storage ? DatabasePartyHandler.GetPartyDescription(partyId)).mapTo[PartyDescription]
|
||||||
|
|
||||||
def getPlayers(partyId: String, maybePlayerId: Option[PlayerId])
|
def getPlayers(partyId: String, maybePlayerId: Option[PlayerId])
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] =
|
||||||
maybePlayerId match {
|
maybePlayerId match {
|
||||||
@ -50,4 +56,8 @@ class PlayerHelper(storage: ActorRef, ariyala: ActorRef) extends AriyalaHelper(a
|
|||||||
def removePlayer(playerId: PlayerId)
|
def removePlayer(playerId: PlayerId)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
(storage ? DatabasePartyHandler.RemovePlayer(playerId)).mapTo[Int]
|
(storage ? DatabasePartyHandler.RemovePlayer(playerId)).mapTo[Int]
|
||||||
|
|
||||||
|
def updateDescription(partyDescription: PartyDescription)
|
||||||
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
|
(storage ? DatabasePartyHandler.UpdateParty(partyDescription)).mapTo[Int]
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,8 @@ import scala.io.Source
|
|||||||
class Swagger(config: Config) extends SwaggerHttpService {
|
class Swagger(config: Config) extends SwaggerHttpService {
|
||||||
override val apiClasses: Set[Class[_]] = Set(
|
override val apiClasses: Set[Class[_]] = Set(
|
||||||
classOf[api.v1.BiSEndpoint], classOf[api.v1.LootEndpoint],
|
classOf[api.v1.BiSEndpoint], classOf[api.v1.LootEndpoint],
|
||||||
classOf[api.v1.PlayerEndpoint], classOf[api.v1.TypesEndpoint],
|
classOf[api.v1.PartyEndpoint], classOf[api.v1.PlayerEndpoint],
|
||||||
classOf[api.v1.UserEndpoint]
|
classOf[api.v1.TypesEndpoint], classOf[api.v1.UserEndpoint]
|
||||||
)
|
)
|
||||||
|
|
||||||
override val info: Info = Info(
|
override val info: Info = Info(
|
||||||
|
@ -17,7 +17,9 @@ import me.arcanis.ffxivbis.service.impl.DatabaseUserHandler
|
|||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class UserHelper(storage: ActorRef) {
|
trait UserHelper {
|
||||||
|
|
||||||
|
def storage: ActorRef
|
||||||
|
|
||||||
def addUser(user: User, isHashedPassword: Boolean)
|
def addUser(user: User, isHashedPassword: Boolean)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
|
@ -27,8 +27,8 @@ import me.arcanis.ffxivbis.models.PlayerId
|
|||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
@Path("api/v1")
|
@Path("api/v1")
|
||||||
class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout)
|
class BiSEndpoint(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
extends BiSHelper(storage, ariyala) with Authorization with JsonSupport {
|
extends BiSHelper with Authorization with JsonSupport {
|
||||||
|
|
||||||
def route: Route = createBiS ~ getBiS ~ modifyBiS
|
def route: Route = createBiS ~ getBiS ~ modifyBiS
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import scala.util.{Failure, Success}
|
|||||||
|
|
||||||
@Path("api/v1")
|
@Path("api/v1")
|
||||||
class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
||||||
extends LootHelper(storage) with Authorization with JsonSupport with HttpHandler {
|
extends LootHelper with Authorization with JsonSupport with HttpHandler {
|
||||||
|
|
||||||
def route: Route = getLoot ~ modifyLoot
|
def route: Route = getLoot ~ modifyLoot
|
||||||
|
|
||||||
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.http.api.v1
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
import akka.http.scaladsl.model.{HttpEntity, StatusCodes}
|
||||||
|
import akka.http.scaladsl.server.Directives._
|
||||||
|
import akka.http.scaladsl.server._
|
||||||
|
import akka.util.Timeout
|
||||||
|
import io.swagger.v3.oas.annotations.enums.ParameterIn
|
||||||
|
import io.swagger.v3.oas.annotations.media.{Content, Schema}
|
||||||
|
import io.swagger.v3.oas.annotations.parameters.RequestBody
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement
|
||||||
|
import io.swagger.v3.oas.annotations.{Operation, Parameter}
|
||||||
|
import javax.ws.rs._
|
||||||
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
|
import me.arcanis.ffxivbis.http.{Authorization, PlayerHelper}
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
|
@Path("api/v1")
|
||||||
|
class PartyEndpoint(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
|
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",
|
||||||
|
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])))),
|
||||||
|
),
|
||||||
|
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("get"))),
|
||||||
|
tags = Array("party"),
|
||||||
|
)
|
||||||
|
def getPartyDescription: Route =
|
||||||
|
path("party" / Segment / "description") { partyId =>
|
||||||
|
extractExecutionContext { implicit executionContext =>
|
||||||
|
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
||||||
|
get {
|
||||||
|
onComplete(getPartyDescription(partyId)) {
|
||||||
|
case Success(response) => complete(PartyDescriptionResponse.fromDescription(response))
|
||||||
|
case Failure(exception) => throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(value = Array("application/json"))
|
||||||
|
@Path("party/{partyId}/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])))),
|
||||||
|
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])))),
|
||||||
|
),
|
||||||
|
security = Array(new SecurityRequirement(name = "basic auth", scopes = Array("post"))),
|
||||||
|
tags = Array("party"),
|
||||||
|
)
|
||||||
|
def modifyPartyDescription: Route =
|
||||||
|
path("party" / Segment / "description") { partyId =>
|
||||||
|
extractExecutionContext { implicit executionContext =>
|
||||||
|
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
||||||
|
post {
|
||||||
|
entity(as[PartyDescriptionResponse]) { partyDescription =>
|
||||||
|
val description = partyDescription.copy(partyId = partyId)
|
||||||
|
onComplete(updateDescription(description.toDescription)) {
|
||||||
|
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
||||||
|
case Failure(exception) => throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,8 +27,8 @@ import me.arcanis.ffxivbis.models.PlayerId
|
|||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
@Path("api/v1")
|
@Path("api/v1")
|
||||||
class PlayerEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout)
|
class PlayerEndpoint(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
extends PlayerHelper(storage, ariyala) with Authorization with JsonSupport with HttpHandler {
|
extends PlayerHelper with Authorization with JsonSupport with HttpHandler {
|
||||||
|
|
||||||
def route: Route = getParty ~ modifyParty
|
def route: Route = getParty ~ modifyParty
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef, config: Config)
|
|||||||
|
|
||||||
private val biSEndpoint = new BiSEndpoint(storage, ariyala)
|
private val biSEndpoint = new BiSEndpoint(storage, ariyala)
|
||||||
private val lootEndpoint = new LootEndpoint(storage)
|
private val lootEndpoint = new LootEndpoint(storage)
|
||||||
|
private val partyEndpoint = new PartyEndpoint(storage, ariyala)
|
||||||
private val playerEndpoint = new PlayerEndpoint(storage, ariyala)
|
private val playerEndpoint = new PlayerEndpoint(storage, ariyala)
|
||||||
private val typesEndpoint = new TypesEndpoint(config)
|
private val typesEndpoint = new TypesEndpoint(config)
|
||||||
private val userEndpoint = new UserEndpoint(storage)
|
private val userEndpoint = new UserEndpoint(storage)
|
||||||
@ -28,8 +29,8 @@ class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef, config: Config)
|
|||||||
def route: Route =
|
def route: Route =
|
||||||
handleExceptions(exceptionHandler) {
|
handleExceptions(exceptionHandler) {
|
||||||
handleRejections(rejectionHandler) {
|
handleRejections(rejectionHandler) {
|
||||||
biSEndpoint.route ~ lootEndpoint.route ~ playerEndpoint.route ~
|
biSEndpoint.route ~ lootEndpoint.route ~ partyEndpoint.route ~
|
||||||
typesEndpoint.route ~ userEndpoint.route
|
playerEndpoint.route ~ typesEndpoint.route ~ userEndpoint.route
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ import scala.util.{Failure, Success}
|
|||||||
|
|
||||||
@Path("api/v1")
|
@Path("api/v1")
|
||||||
class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
||||||
extends UserHelper(storage) with Authorization with JsonSupport {
|
extends UserHelper with Authorization with JsonSupport {
|
||||||
|
|
||||||
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers
|
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
|
|||||||
implicit val partyIdFormat: RootJsonFormat[PartyIdResponse] = jsonFormat1(PartyIdResponse.apply)
|
implicit val partyIdFormat: RootJsonFormat[PartyIdResponse] = jsonFormat1(PartyIdResponse.apply)
|
||||||
implicit val pieceFormat: RootJsonFormat[PieceResponse] = jsonFormat3(PieceResponse.apply)
|
implicit val pieceFormat: RootJsonFormat[PieceResponse] = jsonFormat3(PieceResponse.apply)
|
||||||
implicit val lootFormat: RootJsonFormat[LootResponse] = jsonFormat2(LootResponse.apply)
|
implicit val lootFormat: RootJsonFormat[LootResponse] = jsonFormat2(LootResponse.apply)
|
||||||
|
implicit val partyDescriptionFormat: RootJsonFormat[PartyDescriptionResponse] = jsonFormat2(PartyDescriptionResponse.apply)
|
||||||
implicit val playerFormat: RootJsonFormat[PlayerResponse] = jsonFormat7(PlayerResponse.apply)
|
implicit val playerFormat: RootJsonFormat[PlayerResponse] = jsonFormat7(PlayerResponse.apply)
|
||||||
implicit val playerActionFormat: RootJsonFormat[PlayerActionResponse] = jsonFormat2(PlayerActionResponse.apply)
|
implicit val playerActionFormat: RootJsonFormat[PlayerActionResponse] = jsonFormat2(PlayerActionResponse.apply)
|
||||||
implicit val playerIdFormat: RootJsonFormat[PlayerIdResponse] = jsonFormat3(PlayerIdResponse.apply)
|
implicit val playerIdFormat: RootJsonFormat[PlayerIdResponse] = jsonFormat3(PlayerIdResponse.apply)
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.http.api.v1.json
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema
|
||||||
|
import me.arcanis.ffxivbis.models.PartyDescription
|
||||||
|
|
||||||
|
case class PartyDescriptionResponse(
|
||||||
|
@Schema(description = "party id", required = true) partyId: String,
|
||||||
|
@Schema(description = "party name") partyAlias: Option[String]) {
|
||||||
|
def toDescription: PartyDescription = PartyDescription(partyId, partyAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
object PartyDescriptionResponse {
|
||||||
|
def fromDescription(description: PartyDescription): PartyDescriptionResponse =
|
||||||
|
PartyDescriptionResponse(description.partyId, description.partyAlias)
|
||||||
|
}
|
@ -13,10 +13,12 @@ import akka.http.scaladsl.model.StatusCodes
|
|||||||
import akka.http.scaladsl.server.Directives._
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.http.scaladsl.server._
|
import akka.http.scaladsl.server._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.http.Authorization
|
import me.arcanis.ffxivbis.http.{Authorization, PlayerHelper}
|
||||||
|
|
||||||
class BasePartyView(override val storage: ActorRef)(implicit timeout: Timeout)
|
import scala.util.{Failure, Success}
|
||||||
extends Authorization {
|
|
||||||
|
class BasePartyView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
|
extends PlayerHelper with Authorization {
|
||||||
|
|
||||||
def route: Route = getIndex
|
def route: Route = getIndex
|
||||||
|
|
||||||
@ -25,8 +27,10 @@ class BasePartyView(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
extractExecutionContext { implicit executionContext =>
|
extractExecutionContext { implicit executionContext =>
|
||||||
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
||||||
get {
|
get {
|
||||||
complete {
|
onComplete(getPartyDescription(partyId)) {
|
||||||
(StatusCodes.OK, RootView.toHtml(BasePartyView.template(partyId)))
|
case Success(description) =>
|
||||||
|
complete(StatusCodes.OK, RootView.toHtml(BasePartyView.template(partyId, description.alias)))
|
||||||
|
case Failure(exception) => throw exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,16 +46,16 @@ object BasePartyView {
|
|||||||
def root(partyId: String): Text.TypedTag[String] =
|
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): String =
|
def template(partyId: String, alias: String): String =
|
||||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" +
|
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" +
|
||||||
html(lang:="en",
|
html(lang:="en",
|
||||||
head(
|
head(
|
||||||
titleTag(s"Party $partyId"),
|
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(
|
body(
|
||||||
h2(s"Party $partyId"),
|
h2(s"Party $alias"),
|
||||||
br,
|
br,
|
||||||
h2(a(href:=s"/party/$partyId/players", title:="party")("party")),
|
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/bis", title:="bis management")("best in slot")),
|
||||||
|
@ -19,8 +19,8 @@ import me.arcanis.ffxivbis.models.{Piece, Player, PlayerId}
|
|||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
class BiSView(override val storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout)
|
class BiSView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
extends BiSHelper(storage, ariyala) with Authorization {
|
extends BiSHelper with Authorization {
|
||||||
|
|
||||||
def route: Route = getBiS ~ modifyBiS
|
def route: Route = getBiS ~ modifyBiS
|
||||||
|
|
||||||
|
@ -13,13 +13,14 @@ import akka.http.scaladsl.model.StatusCodes
|
|||||||
import akka.http.scaladsl.server.Directives._
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.http.scaladsl.server._
|
import akka.http.scaladsl.server._
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.http.UserHelper
|
import me.arcanis.ffxivbis.http.{PlayerHelper, UserHelper}
|
||||||
import me.arcanis.ffxivbis.models.{Permission, User}
|
import me.arcanis.ffxivbis.models.{PartyDescription, Permission, User}
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
class IndexView(storage: ActorRef)(implicit timeout: Timeout)
|
class IndexView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
extends UserHelper(storage) {
|
extends PlayerHelper with UserHelper {
|
||||||
|
|
||||||
def route: Route = createParty ~ getIndex
|
def route: Route = createParty ~ getIndex
|
||||||
|
|
||||||
@ -27,13 +28,17 @@ class IndexView(storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
path("party") {
|
path("party") {
|
||||||
extractExecutionContext { implicit executionContext =>
|
extractExecutionContext { implicit executionContext =>
|
||||||
post {
|
post {
|
||||||
formFields("username".as[String], "password".as[String]) { (username, password) =>
|
formFields("username".as[String], "password".as[String], "alias".as[String].?) { (username, password, maybeAlias) =>
|
||||||
onComplete(newPartyId) {
|
onComplete {
|
||||||
case Success(partyId) =>
|
newPartyId.flatMap { partyId =>
|
||||||
val user = User(partyId, username, password, Permission.admin)
|
val user = User(partyId, username, password, Permission.admin)
|
||||||
onComplete(addUser(user, isHashedPassword = false)) {
|
addUser(user, isHashedPassword = false).flatMap { _ =>
|
||||||
case _ => redirect(s"/party/$partyId", StatusCodes.Found)
|
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 Failure(exception) => throw exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,6 +72,7 @@ object IndexView {
|
|||||||
body(
|
body(
|
||||||
form(action:=s"party", method:="post")(
|
form(action:=s"party", method:="post")(
|
||||||
label("create a new party"),
|
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:="username", id:="username", placeholder:="username", title:="username", `type`:="text"),
|
||||||
input(name:="password", id:="password", placeholder:="password", title:="password", `type`:="password"),
|
input(name:="password", id:="password", placeholder:="password", title:="password", `type`:="password"),
|
||||||
input(name:="add", id:="add", `type`:="submit", value:="add")
|
input(name:="add", id:="add", `type`:="submit", value:="add")
|
||||||
|
@ -20,7 +20,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
|
|
||||||
class LootSuggestView(override val storage: ActorRef)(implicit timeout: Timeout)
|
class LootSuggestView(override val storage: ActorRef)(implicit timeout: Timeout)
|
||||||
extends LootHelper(storage) with Authorization {
|
extends LootHelper with Authorization {
|
||||||
|
|
||||||
def route: Route = getIndex ~ suggestLoot
|
def route: Route = getIndex ~ suggestLoot
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
class LootView (override val storage: ActorRef)(implicit timeout: Timeout)
|
class LootView (override val storage: ActorRef)(implicit timeout: Timeout)
|
||||||
extends LootHelper(storage) with Authorization {
|
extends LootHelper with Authorization {
|
||||||
|
|
||||||
def route: Route = getLoot ~ modifyLoot
|
def route: Route = getLoot ~ modifyLoot
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ import me.arcanis.ffxivbis.models._
|
|||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class PlayerView(override val storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout)
|
class PlayerView(override val storage: ActorRef, override val ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
extends PlayerHelper(storage, ariyala) with Authorization {
|
extends PlayerHelper with Authorization {
|
||||||
|
|
||||||
def route: Route = getParty ~ modifyParty
|
def route: Route = getParty ~ modifyParty
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ import akka.util.Timeout
|
|||||||
|
|
||||||
class RootView(storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout) {
|
class RootView(storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout) {
|
||||||
|
|
||||||
private val basePartyView = new BasePartyView(storage)
|
private val basePartyView = new BasePartyView(storage, ariyala)
|
||||||
private val indexView = new IndexView(storage)
|
private val indexView = new IndexView(storage, ariyala)
|
||||||
|
|
||||||
private val biSView = new BiSView(storage, ariyala)
|
private val biSView = new BiSView(storage, ariyala)
|
||||||
private val lootView = new LootView(storage)
|
private val lootView = new LootView(storage)
|
||||||
|
@ -20,7 +20,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
class UserView(override val storage: ActorRef)(implicit timeout: Timeout)
|
class UserView(override val storage: ActorRef)(implicit timeout: Timeout)
|
||||||
extends UserHelper(storage) with Authorization {
|
extends UserHelper with Authorization {
|
||||||
|
|
||||||
def route: Route = getUsers ~ modifyUsers
|
def route: Route = getUsers ~ modifyUsers
|
||||||
|
|
||||||
|
@ -15,15 +15,15 @@ import me.arcanis.ffxivbis.service.LootSelector
|
|||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
case class Party(partyId: String, rules: Seq[String], players: Map[PlayerId, Player])
|
case class Party(partyDescription: PartyDescription, rules: Seq[String], players: Map[PlayerId, Player])
|
||||||
extends StrictLogging {
|
extends StrictLogging {
|
||||||
require(players.keys.forall(_.partyId == partyId), "party id must be same")
|
require(players.keys.forall(_.partyId == partyDescription.partyId), "party id must be same")
|
||||||
|
|
||||||
def getPlayers: Seq[Player] = players.values.toSeq
|
def getPlayers: Seq[Player] = players.values.toSeq
|
||||||
def player(playerId: PlayerId): Option[Player] = players.get(playerId)
|
def player(playerId: PlayerId): Option[Player] = players.get(playerId)
|
||||||
def withPlayer(player: Player): Party =
|
def withPlayer(player: Player): Party =
|
||||||
try {
|
try {
|
||||||
require(player.partyId == partyId, "player must belong to this party")
|
require(player.partyId == partyDescription.partyId, "player must belong to this party")
|
||||||
copy(players = players + (player.playerId -> player))
|
copy(players = players + (player.playerId -> player))
|
||||||
} catch {
|
} catch {
|
||||||
case exception: Exception =>
|
case exception: Exception =>
|
||||||
@ -36,10 +36,7 @@ case class Party(partyId: String, rules: Seq[String], players: Map[PlayerId, Pla
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Party {
|
object Party {
|
||||||
def apply(partyId: Option[String], config: Config): Party =
|
def apply(party: PartyDescription, config: Config,
|
||||||
new Party(partyId.getOrElse(randomPartyId), getRules(config), Map.empty)
|
|
||||||
|
|
||||||
def apply(partyId: String, config: Config,
|
|
||||||
players: Map[Long, Player], bis: Seq[Loot], loot: Seq[Loot]): Party = {
|
players: Map[Long, Player], bis: Seq[Loot], loot: Seq[Loot]): Party = {
|
||||||
val bisByPlayer = bis.groupBy(_.playerId).view.mapValues(piece => BiS(piece.map(_.piece)))
|
val bisByPlayer = bis.groupBy(_.playerId).view.mapValues(piece => BiS(piece.map(_.piece)))
|
||||||
val lootByPlayer = loot.groupBy(_.playerId).view
|
val lootByPlayer = loot.groupBy(_.playerId).view
|
||||||
@ -49,7 +46,7 @@ object Party {
|
|||||||
.withBiS(bisByPlayer.get(playerId))
|
.withBiS(bisByPlayer.get(playerId))
|
||||||
.withLoot(lootByPlayer.getOrElse(playerId, Seq.empty)))
|
.withLoot(lootByPlayer.getOrElse(playerId, Seq.empty)))
|
||||||
}
|
}
|
||||||
Party(partyId, getRules(config), playersWithItems)
|
Party(party, getRules(config), playersWithItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getRules(config: Config): Seq[String] =
|
def getRules(config: Config): Seq[String] =
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.models
|
||||||
|
|
||||||
|
case class PartyDescription(partyId: String, partyAlias: Option[String]) {
|
||||||
|
def alias: String = partyAlias.getOrElse(partyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
object PartyDescription {
|
||||||
|
def empty(partyId: String): PartyDescription = PartyDescription(partyId, None)
|
||||||
|
}
|
@ -32,10 +32,11 @@ trait Database extends Actor with StrictLogging {
|
|||||||
|
|
||||||
def getParty(partyId: String, withBiS: Boolean, withLoot: Boolean): Future[Party] =
|
def getParty(partyId: String, withBiS: Boolean, withLoot: Boolean): Future[Party] =
|
||||||
for {
|
for {
|
||||||
|
partyDescription <- profile.getPartyDescription(partyId)
|
||||||
players <- profile.getParty(partyId)
|
players <- profile.getParty(partyId)
|
||||||
bis <- if (withBiS) profile.getPiecesBiS(partyId) else Future(Seq.empty)
|
bis <- if (withBiS) profile.getPiecesBiS(partyId) else Future(Seq.empty)
|
||||||
loot <- if (withLoot) profile.getPieces(partyId) else Future(Seq.empty)
|
loot <- if (withLoot) profile.getPieces(partyId) else Future(Seq.empty)
|
||||||
} yield Party(partyId, context.system.settings.config, players, bis, loot)
|
} yield Party(partyDescription, context.system.settings.config, players, bis, loot)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Database {
|
object Database {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
package me.arcanis.ffxivbis.service.impl
|
package me.arcanis.ffxivbis.service.impl
|
||||||
|
|
||||||
import akka.pattern.pipe
|
import akka.pattern.pipe
|
||||||
import me.arcanis.ffxivbis.models.{BiS, Player, PlayerId}
|
import me.arcanis.ffxivbis.models.{BiS, PartyDescription, Player, PlayerId}
|
||||||
import me.arcanis.ffxivbis.service.Database
|
import me.arcanis.ffxivbis.service.Database
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
@ -26,6 +26,10 @@ trait DatabasePartyHandler { this: Database =>
|
|||||||
val client = sender()
|
val client = sender()
|
||||||
getParty(partyId, withBiS = true, withLoot = true).pipeTo(client)
|
getParty(partyId, withBiS = true, withLoot = true).pipeTo(client)
|
||||||
|
|
||||||
|
case GetPartyDescription(partyId) =>
|
||||||
|
val client = sender()
|
||||||
|
profile.getPartyDescription(partyId).pipeTo(client)
|
||||||
|
|
||||||
case GetPlayer(playerId) =>
|
case GetPlayer(playerId) =>
|
||||||
val client = sender()
|
val client = sender()
|
||||||
val player = profile.getPlayerFull(playerId).flatMap { maybePlayerData =>
|
val player = profile.getPlayerFull(playerId).flatMap { maybePlayerData =>
|
||||||
@ -43,6 +47,10 @@ trait DatabasePartyHandler { this: Database =>
|
|||||||
case RemovePlayer(playerId) =>
|
case RemovePlayer(playerId) =>
|
||||||
val client = sender()
|
val client = sender()
|
||||||
profile.deletePlayer(playerId).pipeTo(client)
|
profile.deletePlayer(playerId).pipeTo(client)
|
||||||
|
|
||||||
|
case UpdateParty(description) =>
|
||||||
|
val client = sender()
|
||||||
|
profile.insertPartyDescription(description).pipeTo(client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +59,14 @@ object DatabasePartyHandler {
|
|||||||
override def partyId: String = player.partyId
|
override def partyId: String = player.partyId
|
||||||
}
|
}
|
||||||
case class GetParty(partyId: String) extends Database.DatabaseRequest
|
case class GetParty(partyId: String) extends Database.DatabaseRequest
|
||||||
|
case class GetPartyDescription(partyId: String) extends Database.DatabaseRequest
|
||||||
case class GetPlayer(playerId: PlayerId) extends Database.DatabaseRequest {
|
case class GetPlayer(playerId: PlayerId) extends Database.DatabaseRequest {
|
||||||
override def partyId: String = playerId.partyId
|
override def partyId: String = playerId.partyId
|
||||||
}
|
}
|
||||||
case class RemovePlayer(playerId: PlayerId) extends Database.DatabaseRequest {
|
case class RemovePlayer(playerId: PlayerId) extends Database.DatabaseRequest {
|
||||||
override def partyId: String = playerId.partyId
|
override def partyId: String = playerId.partyId
|
||||||
}
|
}
|
||||||
|
case class UpdateParty(partyDescription: PartyDescription) extends Database.DatabaseRequest {
|
||||||
|
override def partyId: String = partyDescription.partyId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import slick.jdbc.JdbcProfile
|
|||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class DatabaseProfile(context: ExecutionContext, config: Config)
|
class DatabaseProfile(context: ExecutionContext, config: Config)
|
||||||
extends BiSProfile with LootProfile with PlayersProfile with UsersProfile {
|
extends BiSProfile with LootProfile with PartyProfile with PlayersProfile with UsersProfile {
|
||||||
|
|
||||||
implicit val executionContext: ExecutionContext = context
|
implicit val executionContext: ExecutionContext = context
|
||||||
|
|
||||||
@ -29,6 +29,7 @@ class DatabaseProfile(context: ExecutionContext, config: Config)
|
|||||||
|
|
||||||
val bisTable: TableQuery[BiSPieces] = TableQuery[BiSPieces]
|
val bisTable: TableQuery[BiSPieces] = TableQuery[BiSPieces]
|
||||||
val lootTable: TableQuery[LootPieces] = TableQuery[LootPieces]
|
val lootTable: TableQuery[LootPieces] = TableQuery[LootPieces]
|
||||||
|
val partiesTable: TableQuery[Parties] = TableQuery[Parties]
|
||||||
val playersTable: TableQuery[Players] = TableQuery[Players]
|
val playersTable: TableQuery[Players] = TableQuery[Players]
|
||||||
val usersTable: TableQuery[Users] = TableQuery[Users]
|
val usersTable: TableQuery[Users] = TableQuery[Users]
|
||||||
|
|
||||||
@ -55,10 +56,10 @@ class DatabaseProfile(context: ExecutionContext, config: Config)
|
|||||||
private def byPartyId[T](partyId: String, callback: Seq[Long] => Future[T]): Future[T] =
|
private def byPartyId[T](partyId: String, callback: Seq[Long] => Future[T]): Future[T] =
|
||||||
getPlayers(partyId).map(callback).flatten
|
getPlayers(partyId).map(callback).flatten
|
||||||
private def byPlayerId[T](playerId: PlayerId, callback: Long => Future[T]): Future[T] =
|
private def byPlayerId[T](playerId: PlayerId, callback: Long => Future[T]): Future[T] =
|
||||||
getPlayer(playerId).map {
|
getPlayer(playerId).flatMap {
|
||||||
case Some(id) => callback(id)
|
case Some(id) => callback(id)
|
||||||
case None => Future.failed(new Error(s"Could not find player $playerId"))
|
case None => Future.failed(new Error(s"Could not find player $playerId"))
|
||||||
}.flatten
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object DatabaseProfile {
|
object DatabaseProfile {
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.storage
|
||||||
|
|
||||||
|
import me.arcanis.ffxivbis.models.PartyDescription
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
trait PartyProfile { this: DatabaseProfile =>
|
||||||
|
import dbConfig.profile.api._
|
||||||
|
|
||||||
|
case class PartyRep(partyId: Option[Long], partyName: String, partyAlias: Option[String]) {
|
||||||
|
def toDescription: PartyDescription = PartyDescription(partyName, partyAlias)
|
||||||
|
}
|
||||||
|
object PartyRep {
|
||||||
|
def fromDescription(party: PartyDescription, id: Option[Long]): PartyRep =
|
||||||
|
PartyRep(id, party.partyId, party.partyAlias)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parties(tag: Tag) extends Table[PartyRep](tag, "parties") {
|
||||||
|
def partyId: Rep[Long] = column[Long]("party_id", O.AutoInc, O.PrimaryKey)
|
||||||
|
def partyName: Rep[String] = column[String]("party_name")
|
||||||
|
def partyAlias: Rep[Option[String]] = column[Option[String]]("party_alias")
|
||||||
|
|
||||||
|
def * =
|
||||||
|
(partyId.?, partyName, partyAlias) <> ((PartyRep.apply _).tupled, PartyRep.unapply)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getPartyDescription(partyId: String): Future[PartyDescription] =
|
||||||
|
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] =
|
||||||
|
getUniquePartyId(partyDescription.partyId).flatMap {
|
||||||
|
case Some(id) => db.run(partiesTable.update(PartyRep.fromDescription(partyDescription, Some(id))))
|
||||||
|
case _ => db.run(partiesTable.insertOrUpdate(PartyRep.fromDescription(partyDescription, None)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private def partyDescription(partyId: String) =
|
||||||
|
partiesTable.filter(_.partyName === partyId)
|
||||||
|
}
|
@ -39,7 +39,6 @@ trait PlayersProfile { this: DatabaseProfile =>
|
|||||||
(partyId, playerId.?, created, nick, job, bisLink, priority) <> ((PlayerRep.apply _).tupled, PlayerRep.unapply)
|
(partyId, playerId.?, created, nick, job, bisLink, priority) <> ((PlayerRep.apply _).tupled, PlayerRep.unapply)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def deletePlayer(playerId: PlayerId): Future[Int] = db.run(player(playerId).delete)
|
def deletePlayer(playerId: PlayerId): Future[Int] = db.run(player(playerId).delete)
|
||||||
def getParty(partyId: String): Future[Map[Long, Player]] =
|
def getParty(partyId: String): Future[Map[Long, Player]] =
|
||||||
db.run(players(partyId).result).map(_.foldLeft(Map.empty[Long, Player]) {
|
db.run(players(partyId).result).map(_.foldLeft(Map.empty[Long, Player]) {
|
||||||
@ -53,10 +52,10 @@ trait PlayersProfile { this: DatabaseProfile =>
|
|||||||
def getPlayers(partyId: String): Future[Seq[Long]] =
|
def getPlayers(partyId: String): Future[Seq[Long]] =
|
||||||
db.run(players(partyId).map(_.playerId).result)
|
db.run(players(partyId).map(_.playerId).result)
|
||||||
def insertPlayer(playerObj: Player): Future[Int] =
|
def insertPlayer(playerObj: Player): Future[Int] =
|
||||||
getPlayer(playerObj.playerId).map {
|
getPlayer(playerObj.playerId).flatMap {
|
||||||
case Some(id) => db.run(playersTable.update(PlayerRep.fromPlayer(playerObj, Some(id))))
|
case Some(id) => db.run(playersTable.update(PlayerRep.fromPlayer(playerObj, Some(id))))
|
||||||
case _ => db.run(playersTable.insertOrUpdate(PlayerRep.fromPlayer(playerObj, None)))
|
case _ => db.run(playersTable.insertOrUpdate(PlayerRep.fromPlayer(playerObj, None)))
|
||||||
}.flatten
|
}
|
||||||
|
|
||||||
private def player(playerId: PlayerId) =
|
private def player(playerId: PlayerId) =
|
||||||
playersTable
|
playersTable
|
||||||
|
@ -49,10 +49,10 @@ trait UsersProfile { this: DatabaseProfile =>
|
|||||||
def getUsers(partyId: String): Future[Seq[User]] =
|
def getUsers(partyId: String): Future[Seq[User]] =
|
||||||
db.run(user(partyId, None).result).map(_.map(_.toUser))
|
db.run(user(partyId, None).result).map(_.map(_.toUser))
|
||||||
def insertUser(userObj: User): Future[Int] =
|
def insertUser(userObj: User): Future[Int] =
|
||||||
db.run(user(userObj.partyId, Some(userObj.username)).map(_.userId).result.headOption).map {
|
db.run(user(userObj.partyId, Some(userObj.username)).map(_.userId).result.headOption).flatMap {
|
||||||
case Some(id) => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, Some(id))))
|
case Some(id) => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, Some(id))))
|
||||||
case _ => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, None)))
|
case _ => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, None)))
|
||||||
}.flatten
|
}
|
||||||
|
|
||||||
private def user(partyId: String, username: Option[String]) =
|
private def user(partyId: String, username: Option[String]) =
|
||||||
usersTable
|
usersTable
|
||||||
|
@ -5,12 +5,13 @@ import akka.http.scaladsl.model.{StatusCodes, Uri}
|
|||||||
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||||
import akka.http.scaladsl.server._
|
import akka.http.scaladsl.server._
|
||||||
import akka.pattern.ask
|
|
||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
|
import akka.pattern.ask
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.service.{Ariyala, PartyService, impl}
|
import me.arcanis.ffxivbis.models.PartyDescription
|
||||||
|
import me.arcanis.ffxivbis.service.{Ariyala, impl}
|
||||||
import me.arcanis.ffxivbis.storage.Migration
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
import org.scalatest.{Matchers, WordSpec}
|
import org.scalatest.{Matchers, WordSpec}
|
||||||
|
|
||||||
@ -23,22 +24,19 @@ class PartyEndpointTest extends WordSpec
|
|||||||
|
|
||||||
private val auth: Authorization =
|
private val auth: Authorization =
|
||||||
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
||||||
private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}")
|
private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}/description")
|
||||||
private val playerId = PlayerIdResponse.fromPlayerId(Fixtures.playerEmpty.playerId)
|
|
||||||
private val timeout: FiniteDuration = 60 seconds
|
private val timeout: FiniteDuration = 60 seconds
|
||||||
implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout)
|
implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout)
|
||||||
|
|
||||||
private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props)
|
private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props)
|
||||||
private val ariyala: ActorRef = system.actorOf(Ariyala.props)
|
private val ariyala: ActorRef = system.actorOf(Ariyala.props)
|
||||||
private val party: ActorRef = system.actorOf(PartyService.props(storage))
|
private val route: Route = new PartyEndpoint(storage, ariyala)(timeout).route
|
||||||
private val route: Route = new PlayerEndpoint(party, ariyala)(timeout).route
|
|
||||||
|
|
||||||
override def testConfig: Config = Settings.withRandomDatabase
|
override def testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
override def beforeAll: Unit = {
|
override def beforeAll: Unit = {
|
||||||
Await.result(Migration(system.settings.config), timeout)
|
Await.result(Migration(system.settings.config), timeout)
|
||||||
Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout)
|
Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout)
|
||||||
Await.result((storage ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def afterAll: Unit = {
|
override def afterAll: Unit = {
|
||||||
@ -48,13 +46,23 @@ class PartyEndpointTest extends WordSpec
|
|||||||
|
|
||||||
"api v1 party endpoint" must {
|
"api v1 party endpoint" must {
|
||||||
|
|
||||||
"get users" in {
|
"get empty party description" in {
|
||||||
val uri = endpoint.withQuery(Uri.Query(Map("nick" -> playerId.nick, "job" -> playerId.job)))
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
val response = Seq(PlayerResponse.fromPlayer(Fixtures.playerEmpty))
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[PartyDescriptionResponse].toDescription shouldEqual PartyDescription.empty(Fixtures.partyId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"update party description" in {
|
||||||
|
val entity = PartyDescriptionResponse(Fixtures.partyId, Some("random party name"))
|
||||||
|
|
||||||
|
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.Accepted
|
||||||
|
}
|
||||||
|
|
||||||
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
status shouldEqual StatusCodes.OK
|
status shouldEqual StatusCodes.OK
|
||||||
responseAs[Seq[PlayerResponse]] shouldEqual response
|
responseAs[PartyDescriptionResponse].toDescription shouldEqual entity.toDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package me.arcanis.ffxivbis.http.api.v1
|
||||||
|
|
||||||
|
import akka.actor.ActorRef
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
|
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||||
|
import akka.http.scaladsl.server._
|
||||||
|
import akka.pattern.ask
|
||||||
|
import akka.testkit.TestKit
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||||
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
|
import me.arcanis.ffxivbis.service.{Ariyala, PartyService, impl}
|
||||||
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
|
import org.scalatest.{Matchers, WordSpec}
|
||||||
|
|
||||||
|
import scala.concurrent.Await
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import scala.language.postfixOps
|
||||||
|
|
||||||
|
class PlayerEndpointTest extends WordSpec
|
||||||
|
with Matchers with ScalatestRouteTest with JsonSupport {
|
||||||
|
|
||||||
|
private val auth: Authorization =
|
||||||
|
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
||||||
|
private val endpoint: Uri = Uri(s"/party/${Fixtures.partyId}")
|
||||||
|
private val playerId = PlayerIdResponse.fromPlayerId(Fixtures.playerEmpty.playerId)
|
||||||
|
private val timeout: FiniteDuration = 60 seconds
|
||||||
|
implicit private val routeTimeout: RouteTestTimeout = RouteTestTimeout(timeout)
|
||||||
|
|
||||||
|
private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props)
|
||||||
|
private val ariyala: ActorRef = system.actorOf(Ariyala.props)
|
||||||
|
private val party: ActorRef = system.actorOf(PartyService.props(storage))
|
||||||
|
private val route: Route = new PlayerEndpoint(party, ariyala)(timeout).route
|
||||||
|
|
||||||
|
override def testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
|
override def beforeAll: Unit = {
|
||||||
|
Await.result(Migration(system.settings.config), timeout)
|
||||||
|
Await.result((storage ? impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true))(timeout).mapTo[Int], timeout)
|
||||||
|
Await.result((storage ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def afterAll: Unit = {
|
||||||
|
TestKit.shutdownActorSystem(system)
|
||||||
|
Settings.clearDatabase(system.settings.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
"api v1 player endpoint" must {
|
||||||
|
|
||||||
|
"get users" in {
|
||||||
|
val uri = endpoint.withQuery(Uri.Query(Map("nick" -> playerId.nick, "job" -> playerId.job)))
|
||||||
|
val response = Seq(PlayerResponse.fromPlayer(Fixtures.playerEmpty))
|
||||||
|
|
||||||
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerResponse]] shouldEqual response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -6,23 +6,24 @@ import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
|||||||
|
|
||||||
class PartyTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
class PartyTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||||
|
|
||||||
|
private val partyDescription = PartyDescription.empty(Fixtures.partyId)
|
||||||
private val party =
|
private val party =
|
||||||
Party(Fixtures.partyId, Seq.empty, Map(Fixtures.playerEmpty.playerId -> Fixtures.playerEmpty))
|
Party(partyDescription, Seq.empty, Map(Fixtures.playerEmpty.playerId -> Fixtures.playerEmpty))
|
||||||
|
|
||||||
"party model" must {
|
"party model" must {
|
||||||
|
|
||||||
"accept player with same party id" in {
|
"accept player with same party id" in {
|
||||||
noException should be thrownBy
|
noException should be thrownBy
|
||||||
Party(Fixtures.partyId, Seq.empty, Map(Fixtures.playerEmpty.playerId -> Fixtures.playerEmpty))
|
Party(partyDescription, Seq.empty, Map(Fixtures.playerEmpty.playerId -> Fixtures.playerEmpty))
|
||||||
}
|
}
|
||||||
|
|
||||||
"fail on multiple party ids" in {
|
"fail on multiple party ids" in {
|
||||||
val anotherPlayer = Fixtures.playerEmpty.copy(partyId = Fixtures.partyId2)
|
val anotherPlayer = Fixtures.playerEmpty.copy(partyId = Fixtures.partyId2)
|
||||||
|
|
||||||
an [IllegalArgumentException] should be thrownBy
|
an [IllegalArgumentException] should be thrownBy
|
||||||
Party(Fixtures.partyId, Seq.empty, Map(anotherPlayer.playerId -> anotherPlayer))
|
Party(partyDescription, Seq.empty, Map(anotherPlayer.playerId -> anotherPlayer))
|
||||||
an [IllegalArgumentException] should be thrownBy
|
an [IllegalArgumentException] should be thrownBy
|
||||||
Party(Fixtures.partyId, Seq.empty, Map(Fixtures.playerEmpty.playerId -> Fixtures.playerEmpty, anotherPlayer.playerId -> anotherPlayer))
|
Party(partyDescription, Seq.empty, Map(Fixtures.playerEmpty.playerId -> Fixtures.playerEmpty, anotherPlayer.playerId -> anotherPlayer))
|
||||||
}
|
}
|
||||||
|
|
||||||
"return player list" in {
|
"return player list" in {
|
||||||
@ -38,7 +39,7 @@ class PartyTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"add new player" in {
|
"add new player" in {
|
||||||
val newParty = Party(Fixtures.partyId, Seq.empty, Map.empty)
|
val newParty = Party(partyDescription, Seq.empty, Map.empty)
|
||||||
newParty.withPlayer(Fixtures.playerEmpty) shouldEqual party
|
newParty.withPlayer(Fixtures.playerEmpty) shouldEqual party
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class LootSelectorTest extends TestKit(ActorSystem("lootselector"))
|
|||||||
|
|
||||||
import me.arcanis.ffxivbis.utils.Converters._
|
import me.arcanis.ffxivbis.utils.Converters._
|
||||||
|
|
||||||
private var default: Party = Party(Some(Fixtures.partyId), Settings.config(Map.empty))
|
private var default: Party = Party(PartyDescription.empty(Fixtures.partyId), Settings.config(Map.empty), Map.empty, Seq.empty, Seq.empty)
|
||||||
private var dnc: Player = Player(-1, Fixtures.partyId, Job.DNC, "a nick", BiS(), Seq.empty, Some(Fixtures.link))
|
private var dnc: Player = Player(-1, Fixtures.partyId, Job.DNC, "a nick", BiS(), Seq.empty, Some(Fixtures.link))
|
||||||
private var drg: Player = Player(-1, Fixtures.partyId, Job.DRG, "another nick", BiS(), Seq.empty, Some(Fixtures.link2))
|
private var drg: Player = Player(-1, Fixtures.partyId, Job.DRG, "another nick", BiS(), Seq.empty, Some(Fixtures.link2))
|
||||||
private val timeout: FiniteDuration = 60 seconds
|
private val timeout: FiniteDuration = 60 seconds
|
||||||
|
Loading…
Reference in New Issue
Block a user