some tests

This commit is contained in:
2019-10-24 08:20:03 +03:00
parent d1001ffb8e
commit b228595a1b
28 changed files with 551 additions and 47 deletions

View File

@ -0,0 +1,36 @@
create table players (
party_id text not null,
player_id bigserial,
created bigint not null,
nick text not null,
job text not null,
bis_link text,
priority integer not null default 1);
create unique index players_nick_job_idx on players(party_id, nick, job);
create table loot (
loot_id bigserial,
player_id bigint not null,
created bigint not null,
piece text not null,
is_tome integer not null,
job text not null,
foreign key (player_id) references players(player_id) on delete cascade);
create index loot_owner_idx on loot(player_id);
create table bis (
player_id bigint not null,
created bigint not null,
piece text not null,
is_tome integer not null,
job text not null,
foreign key (player_id) references players(player_id) on delete cascade);
create unique index bis_piece_player_id_idx on bis(player_id, piece);
create table users (
party_id text not null,
user_id bigserial,
username text not null,
password text not null,
permission text not null);
create unique index users_username_idx on users(party_id, username);

View File

@ -1,6 +1,6 @@
create table players (
party_id text not null,
player_id integer primary key,
player_id integer primary key autoincrement,
created integer not null,
nick text not null,
job text not null,
@ -9,7 +9,7 @@ create table players (
create unique index players_nick_job_idx on players(party_id, nick, job);
create table loot (
loot_id integer primary key,
loot_id integer primary key autoincrement,
player_id integer not null,
created integer not null,
piece text not null,
@ -29,7 +29,7 @@ create unique index bis_piece_player_id_idx on bis(player_id, piece);
create table users (
party_id text not null,
user_id integer primary key,
user_id integer primary key autoincrement,
username text not null,
password text not null,
permission text not null);

View File

@ -0,0 +1,5 @@
<configuration>
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />
</configuration>

View File

@ -22,6 +22,16 @@ me.arcanis.ffxivbis {
}
numThreads = 10
}
postgresql {
profile = "slick.jdbc.PostgresProfile$"
db {
url = "jdbc:postgresql://localhost/ffxivbis"
user = "user"
password = "password"
}
numThreads = 10
}
}
settings {

View File

@ -20,5 +20,5 @@ class AriyalaHelper(ariyala: ActorRef) {
def downloadBiS(link: String, job: Job.Job)
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[BiS] =
(ariyala ? Ariyala.GetBiS(link, job)).mapTo[Seq[Piece]].map(BiS(_))
(ariyala ? Ariyala.GetBiS(link, job)).mapTo[BiS]
}

View File

@ -36,7 +36,7 @@ class PlayerHelper(storage: ActorRef, ariyala: ActorRef) extends AriyalaHelper(a
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] =
maybePlayerId match {
case Some(playerId) =>
(storage ? DatabasePartyHandler.GetPlayer(playerId)).mapTo[Player].map(Seq(_))
(storage ? DatabasePartyHandler.GetPlayer(playerId)).mapTo[Option[Player]].map(_.toSeq)
case None =>
(storage ? DatabasePartyHandler.GetParty(partyId)).mapTo[Party].map(_.players.values.toSeq)
}

View File

@ -20,7 +20,7 @@ class UserHelper(storage: ActorRef) {
def addUser(user: User, isHashedPassword: Boolean)
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
(storage ? DatabaseUserHandler.InsertUser(user, isHashedPassword)).mapTo[Int]
(storage ? DatabaseUserHandler.AddUser(user, isHashedPassword)).mapTo[Int]
def user(partyId: String, username: String)
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[User]] =

View File

@ -22,4 +22,5 @@ case class User(partyId: String,
def hash: String = BCrypt.hashpw(password, BCrypt.gensalt)
def verify(plain: String): Boolean = BCrypt.checkpw(plain, password)
def verityScope(scope: Permission.Value): Boolean = permission >= scope
def withHashedPassword: User = copy(password = hash)
}

View File

@ -18,7 +18,7 @@ import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Keep, Sink}
import akka.util.ByteString
import com.typesafe.scalalogging.StrictLogging
import me.arcanis.ffxivbis.models.{Job, Piece}
import me.arcanis.ffxivbis.models.{BiS, Job, Piece}
import spray.json._
import scala.concurrent.{ExecutionContext, Future}
@ -39,7 +39,7 @@ class Ariyala extends Actor with StrictLogging {
override def receive: Receive = {
case GetBiS(link, job) =>
val client = sender()
get(link, job).pipeTo(client)
get(link, job).map(BiS(_)).pipeTo(client)
}
private def get(link: String, job: Job.Job): Future[Seq[Piece]] = {

View File

@ -19,6 +19,11 @@ trait Database extends Actor with StrictLogging {
implicit def executionContext: ExecutionContext
def profile: DatabaseProfile
override def postStop(): Unit = {
profile.db.close()
super.postStop()
}
def filterParty(party: Party, maybePlayerId: Option[PlayerId]): Seq[Player] =
(party, maybePlayerId) match {
case (_, Some(playerId)) => party.player(playerId).map(Seq(_)).getOrElse(Seq.empty)

View File

@ -12,6 +12,8 @@ import akka.pattern.pipe
import me.arcanis.ffxivbis.models.{BiS, Player, PlayerId}
import me.arcanis.ffxivbis.service.Database
import scala.concurrent.Future
trait DatabasePartyHandler { this: Database =>
import DatabasePartyHandler._
@ -26,11 +28,16 @@ trait DatabasePartyHandler { this: Database =>
case GetPlayer(playerId) =>
val client = sender()
val player = for {
bis <- profile.getPiecesBiS(playerId)
loot <- profile.getPieces(playerId)
} yield Player(playerId.partyId, playerId.job, playerId.nick,
BiS(bis.map(_.piece)), loot.map(_.piece))
val player = profile.getPlayerFull(playerId).flatMap { maybePlayerData =>
Future.traverse(maybePlayerData.toSeq) { playerData =>
for {
bis <- profile.getPiecesBiS(playerId)
loot <- profile.getPieces(playerId)
} yield Player(playerId.partyId, playerId.job, playerId.nick,
BiS(bis.map(_.piece)), loot.map(_.piece),
playerData.link, playerData.priority)
}
}.map(_.headOption)
player.pipeTo(client)
case RemovePlayer(playerId) =>

View File

@ -16,6 +16,11 @@ trait DatabaseUserHandler { this: Database =>
import DatabaseUserHandler._
def userHandler: Receive = {
case AddUser(user, isHashedPassword) =>
val client = sender()
val toInsert = if (isHashedPassword) user else user.withHashedPassword
profile.insertUser(toInsert).pipeTo(client)
case DeleteUser(partyId, username) =>
val client = sender()
profile.deleteUser(partyId, username).pipeTo(client)
@ -27,17 +32,12 @@ trait DatabaseUserHandler { this: Database =>
case GetUsers(partyId) =>
val client = sender()
profile.getUsers(partyId).pipeTo(client)
case InsertUser(user, isHashedPassword) =>
val client = sender()
val toInsert = if (isHashedPassword) user else user.copy(password = user.hash)
profile.insertUser(toInsert).pipeTo(client)
}
}
object DatabaseUserHandler {
case class AddUser(user: User, isHashedPassword: Boolean)
case class DeleteUser(partyId: String, username: String)
case class GetUser(partyId: String, username: String)
case class GetUsers(partyId: String)
case class InsertUser(user: User, isHashedPassword: Boolean)
}

View File

@ -44,7 +44,10 @@ trait LootProfile { this: DatabaseProfile =>
}
def deletePieceById(piece: Piece)(playerId: Long): Future[Int] =
db.run(pieceLoot(LootRep.fromPiece(playerId, piece)).take(1).delete)
db.run(pieceLoot(LootRep.fromPiece(playerId, piece)).map(_.lootId).max.result).flatMap {
case Some(id) => db.run(lootTable.filter(_.lootId === id).delete)
case _ => throw new IllegalArgumentException(s"Could not find piece $piece belong to $playerId")
}
def getPiecesById(playerId: Long): Future[Seq[Loot]] = getPiecesById(Seq(playerId))
def getPiecesById(playerIds: Seq[Long]): Future[Seq[Loot]] =
db.run(piecesLoot(playerIds).result).map(_.map(_.toLoot))

View File

@ -10,6 +10,7 @@ package me.arcanis.ffxivbis.storage
import com.typesafe.config.Config
import org.flywaydb.core.Flyway
import org.flywaydb.core.api.configuration.ClassicConfiguration
import scala.concurrent.Future
@ -21,7 +22,16 @@ class Migration(config: Config) {
val username = section.getString("db.user")
val password = section.getString("db.password")
val flyway = Flyway.configure().dataSource(url, username, password).load()
val provider = url match {
case s"jdbc:$p:$_" => p
case other => throw new NotImplementedError(s"unknown could not parse jdbc url from $other")
}
val flywayConfiguration = new ClassicConfiguration
flywayConfiguration.setLocationsAsStrings(s"db/migration/$provider")
flywayConfiguration.setDataSource(url, username, password)
val flyway = new Flyway(flywayConfiguration)
Future.successful(flyway.migrate())
}
}

View File

@ -49,6 +49,8 @@ trait PlayersProfile { this: DatabaseProfile =>
})
def getPlayer(playerId: PlayerId): Future[Option[Long]] =
db.run(player(playerId).map(_.playerId).result.headOption)
def getPlayerFull(playerId: PlayerId): Future[Option[Player]] =
db.run(player(playerId).result.headOption.map(_.map(_.toPlayer)))
def getPlayers(partyId: String): Future[Seq[Long]] =
db.run(players(partyId).map(_.playerId).result)
def insertPlayer(playerObj: Player): Future[Int] =

View File

@ -22,7 +22,7 @@ trait UsersProfile { this: DatabaseProfile =>
}
object UserRep {
def fromUser(user: User, id: Option[Long]): UserRep =
UserRep(user.partyId, None, user.username, user.password, user.permission.toString)
UserRep(user.partyId, id, user.username, user.password, user.permission.toString)
}
class Users(tag: Tag) extends Table[UserRep](tag, "users") {
@ -47,8 +47,8 @@ trait UsersProfile { this: DatabaseProfile =>
def getUsers(partyId: String): Future[Seq[User]] =
db.run(user(partyId, None).result).map(_.map(_.toUser))
def insertUser(userObj: User): Future[Int] =
db.run(user(userObj.partyId, Some(userObj.username)).result.headOption).map {
case Some(user) => db.run(usersTable.update(UserRep.fromUser(userObj, user.userId)))
db.run(user(userObj.partyId, Some(userObj.username)).map(_.userId).result.headOption).map {
case Some(id) => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, Some(id))))
case _ => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, None)))
}.flatten