mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-25 01:37:17 +00:00
party cache impl, fix reject handling, correct party creation api
This commit is contained in:
parent
e03f8987b0
commit
50acecd97e
@ -11,6 +11,6 @@
|
|||||||
<logger name="http" level="DEBUG">
|
<logger name="http" level="DEBUG">
|
||||||
<appender-ref ref="http" />
|
<appender-ref ref="http" />
|
||||||
</logger>
|
</logger>
|
||||||
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />
|
<logger name="slick" level="INFO" />
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -42,6 +42,8 @@ me.arcanis.ffxivbis {
|
|||||||
]
|
]
|
||||||
# general request timeout, duratin, required
|
# general request timeout, duratin, required
|
||||||
request-timeout = 10s
|
request-timeout = 10s
|
||||||
|
# party in-memory storage lifetime
|
||||||
|
cache-timeout = 1m
|
||||||
}
|
}
|
||||||
|
|
||||||
web {
|
web {
|
||||||
|
@ -13,7 +13,7 @@ import akka.http.scaladsl.Http
|
|||||||
import akka.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
import com.typesafe.scalalogging.StrictLogging
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
import me.arcanis.ffxivbis.http.RootEndpoint
|
import me.arcanis.ffxivbis.http.RootEndpoint
|
||||||
import me.arcanis.ffxivbis.service.Ariyala
|
import me.arcanis.ffxivbis.service.{Ariyala, PartyService}
|
||||||
import me.arcanis.ffxivbis.service.impl.DatabaseImpl
|
import me.arcanis.ffxivbis.service.impl.DatabaseImpl
|
||||||
import me.arcanis.ffxivbis.storage.Migration
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
|
|
||||||
@ -35,7 +35,8 @@ class Application extends Actor with StrictLogging {
|
|||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
val ariyala = context.system.actorOf(Ariyala.props, "ariyala")
|
val ariyala = context.system.actorOf(Ariyala.props, "ariyala")
|
||||||
val storage = context.system.actorOf(DatabaseImpl.props, "storage")
|
val storage = context.system.actorOf(DatabaseImpl.props, "storage")
|
||||||
val http = new RootEndpoint(context.system, storage, ariyala)
|
val party = context.system.actorOf(PartyService.props(storage), "party")
|
||||||
|
val http = new RootEndpoint(context.system, party, ariyala)
|
||||||
|
|
||||||
logger.info(s"start server at $host:$port")
|
logger.info(s"start server at $host:$port")
|
||||||
val bind = Http()(context.system).bindAndHandle(http.route, host, port)
|
val bind = Http()(context.system).bindAndHandle(http.route, host, port)
|
||||||
|
@ -12,6 +12,7 @@ import akka.actor.ActorRef
|
|||||||
import akka.pattern.ask
|
import akka.pattern.ask
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import me.arcanis.ffxivbis.models.User
|
import me.arcanis.ffxivbis.models.User
|
||||||
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
import me.arcanis.ffxivbis.service.impl.DatabaseUserHandler
|
import me.arcanis.ffxivbis.service.impl.DatabaseUserHandler
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
@ -22,6 +23,9 @@ class UserHelper(storage: ActorRef) {
|
|||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
|
||||||
(storage ? DatabaseUserHandler.AddUser(user, isHashedPassword)).mapTo[Int]
|
(storage ? DatabaseUserHandler.AddUser(user, isHashedPassword)).mapTo[Int]
|
||||||
|
|
||||||
|
def newPartyId(implicit executionContext: ExecutionContext, timeout: Timeout): Future[String] =
|
||||||
|
(storage ? PartyService.GetNewPartyId).mapTo[String]
|
||||||
|
|
||||||
def user(partyId: String, username: String)
|
def user(partyId: String, username: String)
|
||||||
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[User]] =
|
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[User]] =
|
||||||
(storage ? DatabaseUserHandler.GetUser(partyId, username)).mapTo[Option[User]]
|
(storage ? DatabaseUserHandler.GetUser(partyId, username)).mapTo[Option[User]]
|
||||||
|
@ -29,7 +29,7 @@ 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, ariyala: ActorRef)(implicit timeout: Timeout)
|
||||||
extends BiSHelper(storage, ariyala) with Authorization with JsonSupport with HttpHandler {
|
extends BiSHelper(storage, ariyala) with Authorization with JsonSupport {
|
||||||
|
|
||||||
def route: Route = createBiS ~ getBiS ~ modifyBiS
|
def route: Route = createBiS ~ getBiS ~ modifyBiS
|
||||||
|
|
||||||
@ -54,18 +54,14 @@ class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit ti
|
|||||||
)
|
)
|
||||||
def createBiS: Route =
|
def createBiS: Route =
|
||||||
path("party" / Segment / "bis") { partyId =>
|
path("party" / Segment / "bis") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
put {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
entity(as[PlayerBiSLinkResponse]) { bisLink =>
|
||||||
put {
|
val playerId = bisLink.playerId.withPartyId(partyId)
|
||||||
entity(as[PlayerBiSLinkResponse]) { bisLink =>
|
onComplete(putBiS(playerId, bisLink.link)) {
|
||||||
val playerId = bisLink.playerId.withPartyId(partyId)
|
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
|
||||||
onComplete(putBiS(playerId, bisLink.link)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,23 +92,19 @@ class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit ti
|
|||||||
)
|
)
|
||||||
def getBiS: Route =
|
def getBiS: Route =
|
||||||
path("party" / Segment / "bis") { partyId =>
|
path("party" / Segment / "bis") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
get {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
|
||||||
get {
|
val playerId = PlayerId(partyId, maybeNick, maybeJob)
|
||||||
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
|
onComplete(bis(partyId, playerId)) {
|
||||||
val playerId = PlayerId(partyId, maybeNick, maybeJob)
|
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
|
||||||
onComplete(bis(partyId, playerId)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,21 +129,18 @@ class BiSEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit ti
|
|||||||
)
|
)
|
||||||
def modifyBiS: Route =
|
def modifyBiS: Route =
|
||||||
path("party" / Segment / "bis") { partyId =>
|
path("party" / Segment / "bis") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
post {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
entity(as[PieceActionResponse]) { action =>
|
||||||
post {
|
val playerId = action.playerIdResponse.withPartyId(partyId)
|
||||||
entity(as[PieceActionResponse]) { action =>
|
onComplete(doModifyBiS(action.action, playerId, action.piece.toPiece)) {
|
||||||
val playerId = action.playerIdResponse.withPartyId(partyId)
|
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
||||||
onComplete(doModifyBiS(action.action, playerId, action.piece.toPiece)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,18 +55,14 @@ class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
)
|
)
|
||||||
def getLoot: Route =
|
def getLoot: Route =
|
||||||
path("party" / Segment / "loot") { partyId =>
|
path("party" / Segment / "loot") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
get {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
|
||||||
get {
|
val playerId = PlayerId(partyId, maybeNick, maybeJob)
|
||||||
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
|
onComplete(loot(partyId, playerId)) {
|
||||||
val playerId = PlayerId(partyId, maybeNick, maybeJob)
|
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
|
||||||
onComplete(loot(partyId, playerId)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,18 +91,14 @@ class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
)
|
)
|
||||||
def modifyLoot: Route =
|
def modifyLoot: Route =
|
||||||
path("party" / Segment / "loot") { partyId =>
|
path("party" / Segment / "loot") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
post {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
entity(as[PieceActionResponse]) { action =>
|
||||||
post {
|
val playerId = action.playerIdResponse.withPartyId(partyId)
|
||||||
entity(as[PieceActionResponse]) { action =>
|
onComplete(doModifyLoot(action.action, playerId, action.piece.toPiece)) {
|
||||||
val playerId = action.playerIdResponse.withPartyId(partyId)
|
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
||||||
onComplete(doModifyLoot(action.action, playerId, action.piece.toPiece)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,17 +131,13 @@ class LootEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
)
|
)
|
||||||
def suggestLoot: Route =
|
def suggestLoot: Route =
|
||||||
path("party" / Segment / "loot") { partyId =>
|
path("party" / Segment / "loot") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
put {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
entity(as[PieceResponse]) { piece =>
|
||||||
put {
|
onComplete(suggestPiece(partyId, piece.toPiece)) {
|
||||||
entity(as[PieceResponse]) { piece =>
|
case Success(response) => complete(response.map(PlayerIdWithCountersResponse.fromPlayerId))
|
||||||
onComplete(suggestPiece(partyId, piece.toPiece)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(response) => complete(response.map(PlayerIdWithCountersResponse.fromPlayerId))
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,18 +55,14 @@ class PlayerEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit
|
|||||||
)
|
)
|
||||||
def getParty: Route =
|
def getParty: Route =
|
||||||
path("party" / Segment) { partyId =>
|
path("party" / Segment) { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
get {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authGet(partyId)) { _ =>
|
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
|
||||||
get {
|
val playerId = PlayerId(partyId, maybeNick, maybeJob)
|
||||||
parameters("nick".as[String].?, "job".as[String].?) { (maybeNick, maybeJob) =>
|
onComplete(getPlayers(partyId, playerId)) {
|
||||||
val playerId = PlayerId(partyId, maybeNick, maybeJob)
|
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
|
||||||
onComplete(getPlayers(partyId, playerId)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(response) => complete(response.map(PlayerResponse.fromPlayer))
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,17 +91,13 @@ class PlayerEndpoint(override val storage: ActorRef, ariyala: ActorRef)(implicit
|
|||||||
)
|
)
|
||||||
def modifyParty: Route =
|
def modifyParty: Route =
|
||||||
path("party" / Segment) { partyId =>
|
path("party" / Segment) { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
entity(as[PlayerActionResponse]) { action =>
|
||||||
authenticateBasicBCrypt(s"party $partyId", authPost(partyId)) { _ =>
|
val player = action.playerIdResponse.toPlayer.copy(partyId = partyId)
|
||||||
entity(as[PlayerActionResponse]) { action =>
|
onComplete(doModifyPlayer(action.action, player)) {
|
||||||
val player = action.playerIdResponse.toPlayer.copy(partyId = partyId)
|
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
||||||
onComplete(doModifyPlayer(action.action, player)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,11 @@ import akka.actor.ActorRef
|
|||||||
import akka.http.scaladsl.server.Directives._
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.http.scaladsl.server.Route
|
import akka.http.scaladsl.server.Route
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
|
import me.arcanis.ffxivbis.http.api.v1.json.JsonSupport
|
||||||
|
|
||||||
class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef)(implicit timeout: Timeout) {
|
class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef)
|
||||||
|
(implicit timeout: Timeout)
|
||||||
|
extends JsonSupport with HttpHandler {
|
||||||
|
|
||||||
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)
|
||||||
@ -21,5 +24,9 @@ class RootApiV1Endpoint(storage: ActorRef, ariyala: ActorRef)(implicit timeout:
|
|||||||
private val userEndpoint = new UserEndpoint(storage)
|
private val userEndpoint = new UserEndpoint(storage)
|
||||||
|
|
||||||
def route: Route =
|
def route: Route =
|
||||||
biSEndpoint.route ~ lootEndpoint.route ~ playerEndpoint.route ~ userEndpoint.route
|
handleExceptions(exceptionHandler) {
|
||||||
|
handleRejections(rejectionHandler) {
|
||||||
|
biSEndpoint.route ~ lootEndpoint.route ~ playerEndpoint.route ~ userEndpoint.route
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,18 @@ 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 with HttpHandler {
|
extends UserHelper(storage) with Authorization with JsonSupport {
|
||||||
|
|
||||||
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers
|
def route: Route = createParty ~ createUser ~ deleteUser ~ getUsers
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("party/{partyId}/create")
|
@Path("party")
|
||||||
@Consumes(value = Array("application/json"))
|
@Consumes(value = Array("application/json"))
|
||||||
@Operation(summary = "create new party", description = "Create new party with specified ID",
|
@Operation(summary = "create new party", description = "Create new party with specified ID",
|
||||||
parameters = Array(
|
|
||||||
new Parameter(name = "partyId", in = ParameterIn.PATH, description = "unique party ID", example = "abcdefgh"),
|
|
||||||
),
|
|
||||||
requestBody = new RequestBody(description = "party administrator description", required = true,
|
requestBody = new RequestBody(description = "party administrator description", required = true,
|
||||||
content = Array(new Content(schema = new Schema(implementation = classOf[UserResponse])))),
|
content = Array(new Content(schema = new Schema(implementation = classOf[UserResponse])))),
|
||||||
responses = Array(
|
responses = Array(
|
||||||
new ApiResponse(responseCode = "201", description = "Party has been created"),
|
new ApiResponse(responseCode = "200", description = "Party has been created"),
|
||||||
new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied"),
|
new ApiResponse(responseCode = "400", description = "Invalid parameters were supplied"),
|
||||||
new ApiResponse(responseCode = "406", description = "Party with the specified ID already exists"),
|
new ApiResponse(responseCode = "406", description = "Party with the specified ID already exists"),
|
||||||
new ApiResponse(responseCode = "500", description = "Internal server error"),
|
new ApiResponse(responseCode = "500", description = "Internal server error"),
|
||||||
@ -50,18 +47,18 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
tags = Array("party"),
|
tags = Array("party"),
|
||||||
)
|
)
|
||||||
def createParty: Route =
|
def createParty: Route =
|
||||||
path("party" / Segment / "create") { partyId =>
|
path("party") {
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
put {
|
||||||
extractExecutionContext { implicit executionContext =>
|
entity(as[UserResponse]) { user =>
|
||||||
put {
|
onComplete(newPartyId) {
|
||||||
entity(as[UserResponse]) { user =>
|
case Success(partyId) =>
|
||||||
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
|
val admin = user.toUser.copy(partyId = partyId, permission = Permission.admin)
|
||||||
onComplete(addUser(admin, isHashedPassword = false)) {
|
onComplete(addUser(admin, isHashedPassword = false)) {
|
||||||
case Success(_) => complete(StatusCodes.Created, HttpEntity.Empty)
|
case Success(_) => complete(PartyIdResponse(partyId))
|
||||||
case Failure(exception) => throw exception
|
case Failure(exception) => throw exception
|
||||||
}
|
}
|
||||||
}
|
case Failure(exception) => throw exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,18 +86,14 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
)
|
)
|
||||||
def createUser: Route =
|
def createUser: Route =
|
||||||
path("party" / Segment / "users") { partyId =>
|
path("party" / Segment / "users") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
post {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
|
entity(as[UserResponse]) { user =>
|
||||||
post {
|
val withPartyId = user.toUser.copy(partyId = partyId)
|
||||||
entity(as[UserResponse]) { user =>
|
onComplete(addUser(withPartyId, isHashedPassword = false)) {
|
||||||
val withPartyId = user.toUser.copy(partyId = partyId)
|
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
||||||
onComplete(addUser(withPartyId, isHashedPassword = false)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,16 +119,12 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
)
|
)
|
||||||
def deleteUser: Route =
|
def deleteUser: Route =
|
||||||
path("party" / Segment / "users" / Segment) { (partyId, username) =>
|
path("party" / Segment / "users" / Segment) { (partyId, username) =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
delete {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
|
onComplete(removeUser(partyId, username)) {
|
||||||
delete {
|
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
||||||
onComplete(removeUser(partyId, username)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(_) => complete(StatusCodes.Accepted, HttpEntity.Empty)
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,16 +152,12 @@ class UserEndpoint(override val storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
)
|
)
|
||||||
def getUsers: Route =
|
def getUsers: Route =
|
||||||
path("party" / Segment / "users") { partyId =>
|
path("party" / Segment / "users") { partyId =>
|
||||||
handleExceptions(exceptionHandler) {
|
extractExecutionContext { implicit executionContext =>
|
||||||
handleRejections(rejectionHandler) {
|
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
|
||||||
extractExecutionContext { implicit executionContext =>
|
get {
|
||||||
authenticateBasicBCrypt(s"party $partyId", authAdmin(partyId)) { _ =>
|
onComplete(users(partyId)) {
|
||||||
get {
|
case Success(response) => complete(response.map(UserResponse.fromUser))
|
||||||
onComplete(users(partyId)) {
|
case Failure(exception) => throw exception
|
||||||
case Success(response) => complete(response.map(UserResponse.fromUser))
|
|
||||||
case Failure(exception) => throw exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
|
|||||||
implicit val permissionFormat: RootJsonFormat[Permission.Value] = enumFormat(Permission)
|
implicit val permissionFormat: RootJsonFormat[Permission.Value] = enumFormat(Permission)
|
||||||
|
|
||||||
implicit val errorFormat: RootJsonFormat[ErrorResponse] = jsonFormat1(ErrorResponse.apply)
|
implicit val errorFormat: RootJsonFormat[ErrorResponse] = jsonFormat1(ErrorResponse.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 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)
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
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)
|
@ -16,19 +16,25 @@ import akka.util.Timeout
|
|||||||
import me.arcanis.ffxivbis.http.UserHelper
|
import me.arcanis.ffxivbis.http.UserHelper
|
||||||
import me.arcanis.ffxivbis.models.{Party, Permission, User}
|
import me.arcanis.ffxivbis.models.{Party, Permission, User}
|
||||||
|
|
||||||
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
class IndexView(storage: ActorRef)(implicit timeout: Timeout)
|
class IndexView(storage: ActorRef)(implicit timeout: Timeout)
|
||||||
extends UserHelper(storage) {
|
extends UserHelper(storage) {
|
||||||
|
|
||||||
def route: Route = createParty ~ getIndex
|
def route: Route = createParty ~ getIndex
|
||||||
|
|
||||||
def createParty: Route =
|
def createParty: Route =
|
||||||
path("party" / Segment / "create") { partyId =>
|
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]) { (username, password) =>
|
||||||
val user = User(partyId, username, password, Permission.admin)
|
onComplete(newPartyId) {
|
||||||
onComplete(addUser(user, isHashedPassword = false)) {
|
case Success(partyId) =>
|
||||||
case _ => redirect(s"/party/$partyId", StatusCodes.Found)
|
val user = User(partyId, username, password, Permission.admin)
|
||||||
|
onComplete(addUser(user, isHashedPassword = false)) {
|
||||||
|
case _ => redirect(s"/party/$partyId", StatusCodes.Found)
|
||||||
|
}
|
||||||
|
case Failure(exception) => throw exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +46,7 @@ class IndexView(storage: ActorRef)(implicit timeout: Timeout)
|
|||||||
get {
|
get {
|
||||||
parameters("partyId".as[String].?) {
|
parameters("partyId".as[String].?) {
|
||||||
case Some(partyId) => redirect(s"/party/$partyId", StatusCodes.Found)
|
case Some(partyId) => redirect(s"/party/$partyId", StatusCodes.Found)
|
||||||
case _ => complete((StatusCodes.OK, RootView.toHtml(IndexView.template)))
|
case _ => complete(StatusCodes.OK, RootView.toHtml(IndexView.template))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +64,7 @@ object IndexView {
|
|||||||
),
|
),
|
||||||
|
|
||||||
body(
|
body(
|
||||||
form(action:=s"party/${Party.randomPartyId}/create", method:="post")(
|
form(action:=s"party", method:="post")(
|
||||||
label("create a new party"),
|
label("create a new party"),
|
||||||
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"),
|
||||||
|
@ -22,7 +22,7 @@ import me.arcanis.ffxivbis.models.{BiS, Job, Piece}
|
|||||||
import spray.json._
|
import spray.json._
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.Try
|
||||||
|
|
||||||
class Ariyala extends Actor with StrictLogging {
|
class Ariyala extends Actor with StrictLogging {
|
||||||
import Ariyala._
|
import Ariyala._
|
||||||
|
@ -37,3 +37,9 @@ trait Database extends Actor with StrictLogging {
|
|||||||
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(partyId, context.system.settings.config, players, bis, loot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Database {
|
||||||
|
trait DatabaseRequest {
|
||||||
|
def partyId: String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import akka.actor.{Actor, ActorRef, Props}
|
||||||
|
import akka.pattern.{ask, pipe}
|
||||||
|
import akka.util.Timeout
|
||||||
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
|
import me.arcanis.ffxivbis.models.Party
|
||||||
|
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
|
class PartyService(storage: ActorRef) extends Actor with StrictLogging {
|
||||||
|
import PartyService._
|
||||||
|
import me.arcanis.ffxivbis.utils.Implicits._
|
||||||
|
|
||||||
|
private val cacheTimeout: FiniteDuration =
|
||||||
|
context.system.settings.config.getDuration("me.arcanis.ffxivbis.settings.cache-timeout")
|
||||||
|
implicit private val executionContext: ExecutionContext = context.dispatcher
|
||||||
|
implicit private val timeout: Timeout =
|
||||||
|
context.system.settings.config.getDuration("me.arcanis.ffxivbis.settings.request-timeout")
|
||||||
|
|
||||||
|
override def receive: Receive = handle(Map.empty)
|
||||||
|
|
||||||
|
private def handle(cache: Map[String, Party]): Receive = {
|
||||||
|
case ForgetParty(partyId) =>
|
||||||
|
context become handle(cache - partyId)
|
||||||
|
|
||||||
|
case GetNewPartyId =>
|
||||||
|
val client = sender()
|
||||||
|
getPartyId.pipeTo(client)
|
||||||
|
|
||||||
|
case req @ impl.DatabasePartyHandler.GetParty(partyId) =>
|
||||||
|
val client = sender()
|
||||||
|
val party = cache.get(partyId) match {
|
||||||
|
case Some(party) => Future.successful(party)
|
||||||
|
case None =>
|
||||||
|
(storage ? req).mapTo[Party].map { party =>
|
||||||
|
context become handle(cache + (partyId -> party))
|
||||||
|
context.system.scheduler.scheduleOnce(cacheTimeout, self, ForgetParty(partyId))
|
||||||
|
party
|
||||||
|
}
|
||||||
|
}
|
||||||
|
party.pipeTo(client)
|
||||||
|
|
||||||
|
case req: Database.DatabaseRequest =>
|
||||||
|
self ! ForgetParty(req.partyId)
|
||||||
|
storage.forward(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getPartyId: Future[String] = {
|
||||||
|
val partyId = Party.randomPartyId
|
||||||
|
(storage ? impl.DatabaseUserHandler.Exists(partyId)).mapTo[Boolean].flatMap {
|
||||||
|
case true => getPartyId
|
||||||
|
case false => Future.successful(partyId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object PartyService {
|
||||||
|
def props(storage: ActorRef): Props = Props(new PartyService(storage))
|
||||||
|
|
||||||
|
case class ForgetParty(partyId: String)
|
||||||
|
case object GetNewPartyId
|
||||||
|
}
|
@ -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, Piece, PlayerId}
|
import me.arcanis.ffxivbis.models.{Piece, PlayerId}
|
||||||
import me.arcanis.ffxivbis.service.Database
|
import me.arcanis.ffxivbis.service.Database
|
||||||
|
|
||||||
trait DatabaseBiSHandler { this: Database =>
|
trait DatabaseBiSHandler { this: Database =>
|
||||||
@ -33,7 +33,11 @@ trait DatabaseBiSHandler { this: Database =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DatabaseBiSHandler {
|
object DatabaseBiSHandler {
|
||||||
case class AddPieceToBis(playerId: PlayerId, piece: Piece)
|
case class AddPieceToBis(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest {
|
||||||
case class GetBiS(partyId: String, playerId: Option[PlayerId])
|
override def partyId: String = playerId.partyId
|
||||||
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece)
|
}
|
||||||
|
case class GetBiS(partyId: String, playerId: Option[PlayerId]) extends Database.DatabaseRequest
|
||||||
|
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest {
|
||||||
|
override def partyId: String = playerId.partyId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,12 @@ trait DatabaseLootHandler { this: Database =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DatabaseLootHandler {
|
object DatabaseLootHandler {
|
||||||
case class AddPieceTo(playerId: PlayerId, piece: Piece)
|
case class AddPieceTo(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest {
|
||||||
case class GetLoot(partyId: String, playerId: Option[PlayerId])
|
override def partyId: String = playerId.partyId
|
||||||
case class RemovePieceFrom(playerId: PlayerId, piece: Piece)
|
}
|
||||||
case class SuggestLoot(partyId: String, piece: Piece)
|
case class GetLoot(partyId: String, playerId: Option[PlayerId]) extends Database.DatabaseRequest
|
||||||
|
case class RemovePieceFrom(playerId: PlayerId, piece: Piece) extends Database.DatabaseRequest {
|
||||||
|
override def partyId: String = playerId.partyId
|
||||||
|
}
|
||||||
|
case class SuggestLoot(partyId: String, piece: Piece) extends Database.DatabaseRequest
|
||||||
}
|
}
|
||||||
|
@ -47,8 +47,14 @@ trait DatabasePartyHandler { this: Database =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DatabasePartyHandler {
|
object DatabasePartyHandler {
|
||||||
case class AddPlayer(player: Player)
|
case class AddPlayer(player: Player) extends Database.DatabaseRequest {
|
||||||
case class GetParty(partyId: String)
|
override def partyId: String = player.partyId
|
||||||
case class GetPlayer(playerId: PlayerId)
|
}
|
||||||
case class RemovePlayer(playerId: PlayerId)
|
case class GetParty(partyId: String) extends Database.DatabaseRequest
|
||||||
|
case class GetPlayer(playerId: PlayerId) extends Database.DatabaseRequest {
|
||||||
|
override def partyId: String = playerId.partyId
|
||||||
|
}
|
||||||
|
case class RemovePlayer(playerId: PlayerId) extends Database.DatabaseRequest {
|
||||||
|
override def partyId: String = playerId.partyId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,10 @@ trait DatabaseUserHandler { this: Database =>
|
|||||||
val client = sender()
|
val client = sender()
|
||||||
profile.deleteUser(partyId, username).pipeTo(client)
|
profile.deleteUser(partyId, username).pipeTo(client)
|
||||||
|
|
||||||
|
case Exists(partyId) =>
|
||||||
|
val client = sender()
|
||||||
|
profile.exists(partyId).pipeTo(client)
|
||||||
|
|
||||||
case GetUser(partyId, username) =>
|
case GetUser(partyId, username) =>
|
||||||
val client = sender()
|
val client = sender()
|
||||||
profile.getUser(partyId, username).pipeTo(client)
|
profile.getUser(partyId, username).pipeTo(client)
|
||||||
@ -36,8 +40,11 @@ trait DatabaseUserHandler { this: Database =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DatabaseUserHandler {
|
object DatabaseUserHandler {
|
||||||
case class AddUser(user: User, isHashedPassword: Boolean)
|
case class AddUser(user: User, isHashedPassword: Boolean) extends Database.DatabaseRequest {
|
||||||
case class DeleteUser(partyId: String, username: String)
|
override def partyId: String = user.partyId
|
||||||
case class GetUser(partyId: String, username: String)
|
}
|
||||||
case class GetUsers(partyId: String)
|
case class DeleteUser(partyId: String, username: String) extends Database.DatabaseRequest
|
||||||
|
case class Exists(partyId: String) extends Database.DatabaseRequest
|
||||||
|
case class GetUser(partyId: String, username: String) extends Database.DatabaseRequest
|
||||||
|
case class GetUsers(partyId: String) extends Database.DatabaseRequest
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ trait UsersProfile { this: DatabaseProfile =>
|
|||||||
|
|
||||||
def deleteUser(partyId: String, username: String): Future[Int] =
|
def deleteUser(partyId: String, username: String): Future[Int] =
|
||||||
db.run(user(partyId, Some(username)).delete)
|
db.run(user(partyId, Some(username)).delete)
|
||||||
|
def exists(partyId: String): Future[Boolean] =
|
||||||
|
db.run(user(partyId, None).exists.result)
|
||||||
def getUser(partyId: String, username: String): Future[Option[User]] =
|
def getUser(partyId: String, username: String): Future[Option[User]] =
|
||||||
db.run(user(partyId, Some(username)).result.headOption).map(_.map(_.toUser))
|
db.run(user(partyId, Some(username)).result.headOption).map(_.map(_.toUser))
|
||||||
def getUsers(partyId: String): Future[Seq[User]] =
|
def getUsers(partyId: String): Future[Seq[User]] =
|
||||||
|
@ -17,11 +17,14 @@ import scala.concurrent.duration.FiniteDuration
|
|||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
object Implicits {
|
object Implicits {
|
||||||
implicit def getBooleanFromOptionString(maybeYes: Option[String]): Boolean = maybeYes match {
|
implicit def getBooleanFromOptionString(maybeYes: Option[String]): Boolean = maybeYes.map(_.toLowerCase) match {
|
||||||
case Some("yes" | "on") => true
|
case Some("yes" | "on") => true
|
||||||
case _ => false
|
case _ => false
|
||||||
}
|
}
|
||||||
|
|
||||||
implicit def getFiniteDuration(duration: Duration): Timeout =
|
implicit def getFiniteDuration(duration: Duration): FiniteDuration =
|
||||||
|
FiniteDuration(duration.toNanos, TimeUnit.NANOSECONDS)
|
||||||
|
|
||||||
|
implicit def getTimeout(duration: Duration): Timeout =
|
||||||
FiniteDuration(duration.toNanos, TimeUnit.NANOSECONDS)
|
FiniteDuration(duration.toNanos, TimeUnit.NANOSECONDS)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ 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.models.BiS
|
import me.arcanis.ffxivbis.models.BiS
|
||||||
import me.arcanis.ffxivbis.service.{Ariyala, impl}
|
import me.arcanis.ffxivbis.service.{Ariyala, PartyService, impl}
|
||||||
import me.arcanis.ffxivbis.storage.Migration
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
import org.scalatest.{Matchers, WordSpec}
|
import org.scalatest.{Matchers, WordSpec}
|
||||||
|
|
||||||
@ -31,7 +31,8 @@ class BiSEndpointTest extends WordSpec
|
|||||||
|
|
||||||
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 route: Route = new BiSEndpoint(storage, ariyala)(timeout).route
|
private val party: ActorRef = system.actorOf(PartyService.props(storage))
|
||||||
|
private val route: Route = new BiSEndpoint(party, ariyala)(timeout).route
|
||||||
|
|
||||||
override def testConfig: Config = Settings.withRandomDatabase
|
override def testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import akka.testkit.TestKit
|
|||||||
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.impl
|
import me.arcanis.ffxivbis.service.{PartyService, impl}
|
||||||
import me.arcanis.ffxivbis.storage.Migration
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
import org.scalatest.{Matchers, WordSpec}
|
import org.scalatest.{Matchers, WordSpec}
|
||||||
|
|
||||||
@ -29,7 +29,8 @@ class LootEndpointTest extends WordSpec
|
|||||||
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 route: Route = new LootEndpoint(storage)(timeout).route
|
private val party: ActorRef = system.actorOf(PartyService.props(storage))
|
||||||
|
private val route: Route = new LootEndpoint(party)(timeout).route
|
||||||
|
|
||||||
override def testConfig: Config = Settings.withRandomDatabase
|
override def testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import akka.testkit.TestKit
|
|||||||
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, impl}
|
import me.arcanis.ffxivbis.service.{Ariyala, PartyService, impl}
|
||||||
import me.arcanis.ffxivbis.storage.Migration
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
import org.scalatest.{Matchers, WordSpec}
|
import org.scalatest.{Matchers, WordSpec}
|
||||||
|
|
||||||
@ -30,7 +30,8 @@ class PartyEndpointTest extends WordSpec
|
|||||||
|
|
||||||
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 route: Route = new PlayerEndpoint(storage, ariyala)(timeout).route
|
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 testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import akka.testkit.TestKit
|
|||||||
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.impl
|
import me.arcanis.ffxivbis.service.{PartyService, impl}
|
||||||
import me.arcanis.ffxivbis.storage.Migration
|
import me.arcanis.ffxivbis.storage.Migration
|
||||||
import org.scalatest.{Matchers, WordSpec}
|
import org.scalatest.{Matchers, WordSpec}
|
||||||
|
|
||||||
@ -22,12 +22,14 @@ class UserEndpointTest 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}/users")
|
private def endpoint: Uri = Uri(s"/party/$partyId/users")
|
||||||
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 var partyId: String = Fixtures.partyId
|
||||||
private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props)
|
private val storage: ActorRef = system.actorOf(impl.DatabaseImpl.props)
|
||||||
private val route: Route = new UserEndpoint(storage)(timeout).route
|
private val party: ActorRef = system.actorOf(PartyService.props(storage))
|
||||||
|
private val route: Route = new UserEndpoint(party)(timeout).route
|
||||||
|
|
||||||
override def testConfig: Config = Settings.withRandomDatabase
|
override def testConfig: Config = Settings.withRandomDatabase
|
||||||
|
|
||||||
@ -43,18 +45,17 @@ class UserEndpointTest extends WordSpec
|
|||||||
"api v1 users endpoint" must {
|
"api v1 users endpoint" must {
|
||||||
|
|
||||||
"create a party" in {
|
"create a party" in {
|
||||||
val uri = Uri(s"/party/${Fixtures.partyId}/create")
|
val uri = Uri(s"/party")
|
||||||
val entity = UserResponse.fromUser(Fixtures.userAdmin).copy(password = Fixtures.userPassword)
|
val entity = UserResponse.fromUser(Fixtures.userAdmin).copy(password = Fixtures.userPassword)
|
||||||
println(entity)
|
|
||||||
|
|
||||||
Put(uri, entity) ~> route ~> check {
|
Put(uri, entity) ~> route ~> check {
|
||||||
status shouldEqual StatusCodes.Created
|
status shouldEqual StatusCodes.OK
|
||||||
responseAs[String] shouldEqual ""
|
partyId = responseAs[PartyIdResponse].partyId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"add user" in {
|
"add user" in {
|
||||||
val entity = UserResponse.fromUser(Fixtures.userGet).copy(password = Fixtures.userPassword2)
|
val entity = UserResponse.fromUser(Fixtures.userGet).copy(partyId = partyId, password = Fixtures.userPassword2)
|
||||||
|
|
||||||
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
status shouldEqual StatusCodes.Accepted
|
status shouldEqual StatusCodes.Accepted
|
||||||
@ -70,13 +71,28 @@ class UserEndpointTest extends WordSpec
|
|||||||
status shouldEqual StatusCodes.OK
|
status shouldEqual StatusCodes.OK
|
||||||
|
|
||||||
val users = responseAs[Seq[UserResponse]]
|
val users = responseAs[Seq[UserResponse]]
|
||||||
users.map(_.partyId).distinct shouldEqual Seq(Fixtures.partyId)
|
users.map(_.partyId).distinct shouldEqual Seq(partyId)
|
||||||
users.map(user => user.username -> user.permission).toMap shouldEqual party
|
users.map(user => user.username -> user.permission).toMap shouldEqual party
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"remove user" in {
|
"remove user" in {
|
||||||
|
val entity = UserResponse.fromUser(Fixtures.userGet).copy(partyId = partyId)
|
||||||
|
|
||||||
|
Delete(endpoint.toString + s"/${entity.username}").withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.Accepted
|
||||||
|
}
|
||||||
|
|
||||||
|
val party = Seq(Fixtures.userAdmin)
|
||||||
|
.map(user => user.username -> Some(user.permission)).toMap
|
||||||
|
|
||||||
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
|
||||||
|
val users = responseAs[Seq[UserResponse]]
|
||||||
|
users.map(_.partyId).distinct shouldEqual Seq(partyId)
|
||||||
|
users.map(user => user.username -> user.permission).toMap shouldEqual party
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user