Bet result and history
continuous-integration/drone/push Build is passing Details

pull/10/head
Arthur VALIN 1 year ago
parent 0aa192efaf
commit cd7e20a4b8

@ -1,6 +1,8 @@
package allin.data package allin.data
import allin.model.Bet import allin.model.Bet
import allin.model.BetDetail
import allin.model.BetResultDetail
import allin.model.UpdatedBetData import allin.model.UpdatedBetData
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -14,4 +16,7 @@ interface BetDataSource {
fun updateBetStatuses(date: ZonedDateTime) fun updateBetStatuses(date: ZonedDateTime)
fun getToConfirm(username: String): List<Bet> fun getToConfirm(username: String): List<Bet>
fun confirmBet(betId: String, result: String) fun confirmBet(betId: String, result: String)
fun getWonNotifications(username: String): List<BetResultDetail>
fun getHistory(username: String): List<BetResultDetail>
fun getCurrent(username: String): List<BetDetail>
} }

@ -1,13 +1,16 @@
package allin.data.mock package allin.data.mock
import allin.data.BetDataSource import allin.data.BetDataSource
import allin.model.Bet import allin.model.*
import allin.model.BetResult import allin.model.BetStatus.*
import allin.model.BetStatus
import allin.model.UpdatedBetData
import java.time.ZonedDateTime import java.time.ZonedDateTime
class MockBetDataSource : BetDataSource { class MockBetDataSource(mockData: MockDataSource.MockData) : BetDataSource {
private val bets = mockData.bets
private val results = mockData.results
private val participations = mockData.participations
private val resultNotifications = mockData.resultNotifications
override fun getAllBets(): List<Bet> = bets override fun getAllBets(): List<Bet> = bets
override fun getBetById(id: String): Bet? = override fun getBetById(id: String): Bet? =
bets.find { it.id == id } bets.find { it.id == id }
@ -32,16 +35,16 @@ class MockBetDataSource : BetDataSource {
bets.forEachIndexed { idx, bet -> bets.forEachIndexed { idx, bet ->
if (date >= bet.endRegistration) { if (date >= bet.endRegistration) {
if (date >= bet.endBet) { if (date >= bet.endBet) {
bets[idx] = bet.copy(status = BetStatus.WAITING) bets[idx] = bet.copy(status = WAITING)
} else { } else {
bets[idx] = bet.copy(status = BetStatus.CLOSING) bets[idx] = bet.copy(status = CLOSING)
} }
} }
} }
} }
override fun getToConfirm(username: String): List<Bet> = override fun getToConfirm(username: String): List<Bet> =
bets.filter { it.createdBy == username && it.status == BetStatus.CLOSING } bets.filter { it.createdBy == username && it.status == CLOSING }
override fun confirmBet(betId: String, result: String) { override fun confirmBet(betId: String, result: String) {
results.add( results.add(
@ -52,12 +55,71 @@ class MockBetDataSource : BetDataSource {
) )
bets.replaceAll { bets.replaceAll {
if (it.id == betId) { if (it.id == betId) {
it.copy(status = BetStatus.FINISHED) it.copy(status = FINISHED)
} else it } else it
} }
participations.filter { it.betId == betId && it.answer == result }
.forEach {
resultNotifications.add(Pair(betId, it.username))
}
} }
private val bets by lazy { mutableListOf<Bet>() } override fun getWonNotifications(username: String): List<BetResultDetail> {
private val results by lazy { mutableListOf<BetResult>() } return bets.map { bet ->
val notification = resultNotifications.find { it.first == bet.id } ?: return@map null
val result = results.find { it.betId == bet.id } ?: return@map null
val participation = participations.find { it.username == username && it.betId == bet.id }
?: return@map null
if (participation.answer == result.result) {
resultNotifications.remove(notification)
BetResultDetail(
betResult = result,
bet = bet,
participation = participation,
amount = participation.stake,
won = true
)
} else null
}.mapNotNull { it }
}
override fun getHistory(username: String): List<BetResultDetail> {
return bets.map { bet ->
val result = results.find { it.betId == bet.id } ?: return@map null
val participation = participations.find { it.username == username && it.betId == bet.id }
?: return@map null
BetResultDetail(
betResult = result,
bet = bet,
participation = participation,
amount = participation.stake,
won = participation.answer == result.result
)
}.mapNotNull { it }
}
override fun getCurrent(username: String): List<BetDetail> {
return bets.map { bet ->
when (bet.status) {
CANCELLED, FINISHED -> return@map null
else -> {
val participation = participations.find { it.username == username && it.betId == bet.id }
?: return@map null
val participations = participations.filter { it.betId == bet.id }
BetDetail(
bet = bet,
answers = getBetAnswerDetail(bet, participations),
participations = participations,
userParticipation = participation
)
}
}
}.mapNotNull { it }
}
} }

@ -4,9 +4,26 @@ import allin.data.AllInDataSource
import allin.data.BetDataSource import allin.data.BetDataSource
import allin.data.ParticipationDataSource import allin.data.ParticipationDataSource
import allin.data.UserDataSource import allin.data.UserDataSource
import allin.model.Bet
import allin.model.BetResult
import allin.model.Participation
import allin.model.User
import java.time.ZonedDateTime
class MockDataSource : AllInDataSource() { class MockDataSource : AllInDataSource() {
override val userDataSource: UserDataSource = MockUserDataSource()
override val betDataSource: BetDataSource = MockBetDataSource() class MockData {
override val participationDataSource: ParticipationDataSource = MockParticipationDataSource() val bets by lazy { mutableListOf<Bet>() }
val results by lazy { mutableListOf<BetResult>() }
val resultNotifications by lazy { mutableListOf<Pair<String, String>>() }
val users by lazy { mutableListOf<User>() }
val lastGifts by lazy { mutableMapOf<String, ZonedDateTime>() }
val participations by lazy { mutableListOf<Participation>() }
}
private val mockData by lazy { MockData() }
override val userDataSource: UserDataSource = MockUserDataSource(mockData)
override val betDataSource: BetDataSource = MockBetDataSource(mockData)
override val participationDataSource: ParticipationDataSource = MockParticipationDataSource(mockData)
} }

@ -3,7 +3,9 @@ package allin.data.mock
import allin.data.ParticipationDataSource import allin.data.ParticipationDataSource
import allin.model.Participation import allin.model.Participation
class MockParticipationDataSource : ParticipationDataSource { class MockParticipationDataSource(mockData: MockDataSource.MockData) : ParticipationDataSource {
private val participations = mockData.participations
override fun addParticipation(participation: Participation) { override fun addParticipation(participation: Participation) {
participations += participations participations += participations
} }
@ -16,7 +18,4 @@ class MockParticipationDataSource : ParticipationDataSource {
override fun deleteParticipation(id: String): Boolean = override fun deleteParticipation(id: String): Boolean =
participations.removeIf { it.id == id } participations.removeIf { it.id == id }
private val participations by lazy { mutableListOf<Participation>() }
} }

@ -5,7 +5,11 @@ import allin.dto.UserDTO
import allin.model.User import allin.model.User
import java.time.ZonedDateTime import java.time.ZonedDateTime
class MockUserDataSource : UserDataSource { class MockUserDataSource(mockData: MockDataSource.MockData) : UserDataSource {
private val users = mockData.users
private val lastGifts = mockData.lastGifts
override fun getUserByUsername(username: String): Pair<UserDTO?, String?> = override fun getUserByUsername(username: String): Pair<UserDTO?, String?> =
users.find { it.username == username }?.let { users.find { it.username == username }?.let {
Pair( Pair(
@ -49,12 +53,4 @@ class MockUserDataSource : UserDataSource {
lastGifts[username] = ZonedDateTime.now() lastGifts[username] = ZonedDateTime.now()
return value return value
} }
private val users by lazy {
mutableListOf<User>()
}
private val lastGifts by lazy {
mutableMapOf<String, ZonedDateTime>()
}
} }

@ -3,10 +3,7 @@ package allin.data.postgres
import allin.data.BetDataSource import allin.data.BetDataSource
import allin.data.postgres.entities.* import allin.data.postgres.entities.*
import allin.data.postgres.entities.ResponsesEntity.response import allin.data.postgres.entities.ResponsesEntity.response
import allin.model.Bet import allin.model.*
import allin.model.BetStatus
import allin.model.BetType
import allin.model.UpdatedBetData
import org.ktorm.database.Database import org.ktorm.database.Database
import org.ktorm.dsl.* import org.ktorm.dsl.*
import java.time.ZoneId import java.time.ZoneId
@ -40,7 +37,29 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
} }
) )
private fun QueryRowSet.toParticipation() =
Participation(
id = this[ParticipationsEntity.id]?.toString() ?: "",
betId = this[ParticipationsEntity.betId]?.toString() ?: "",
username = this[ParticipationsEntity.username] ?: "",
answer = this[ParticipationsEntity.answer] ?: "",
stake = this[ParticipationsEntity.stake] ?: 0
)
private fun QueryRowSet.toBetResultDetail() =
BetResultDetail(
betResult = BetResult(
betId = this[BetResultsEntity.betId]?.toString() ?: "",
result = this[BetResultsEntity.result] ?: ""
),
bet = this.toBet(),
participation = this.toParticipation(),
amount = this[ParticipationsEntity.stake] ?: 0,
won = this[ParticipationsEntity.answer] == this[BetResultsEntity.result]
)
private fun Query.mapToBet() = this.map { it.toBet() } private fun Query.mapToBet() = this.map { it.toBet() }
private fun Query.mapToBetResultDetail() = this.map { it.toBetResultDetail() }
override fun getAllBets(): List<Bet> = override fun getAllBets(): List<Bet> =
database.from(BetsEntity).select().mapToBet() database.from(BetsEntity).select().mapToBet()
@ -77,6 +96,73 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
where { BetsEntity.id eq UUID.fromString(betId) } where { BetsEntity.id eq UUID.fromString(betId) }
set(BetsEntity.status, BetStatus.FINISHED) set(BetsEntity.status, BetStatus.FINISHED)
} }
database.from(ParticipationsEntity)
.select()
.where {
(ParticipationsEntity.betId eq UUID.fromString(betId)) and
(ParticipationsEntity.answer eq result)
}
.forEach { participation ->
database.insert(BetResultNotificationsEntity) {
set(it.betId, betId)
set(it.username, participation[ParticipationsEntity.username])
}
}
}
override fun getWonNotifications(username: String): List<BetResultDetail> {
return database.from(BetsEntity)
.innerJoin(ParticipationsEntity, on = BetsEntity.id eq ParticipationsEntity.betId)
.innerJoin(BetResultsEntity, on = BetsEntity.id eq BetResultsEntity.betId)
.innerJoin(BetResultNotificationsEntity, on = BetsEntity.id eq BetResultNotificationsEntity.betId)
.select()
.where {
(BetResultsEntity.result eq ParticipationsEntity.answer) and
(ParticipationsEntity.username eq username)
}.let {
it.forEach { row ->
row[BetsEntity.id]?.let { betId ->
database.delete(BetResultNotificationsEntity) {
(it.betId eq betId) and (it.username eq username)
}
}
}
it
}.mapToBetResultDetail()
}
override fun getHistory(username: String): List<BetResultDetail> {
return database.from(BetsEntity)
.innerJoin(ParticipationsEntity, on = BetsEntity.id eq ParticipationsEntity.betId)
.innerJoin(BetResultsEntity, on = BetsEntity.id eq BetResultsEntity.betId)
.select()
.where { ParticipationsEntity.username eq username }.mapToBetResultDetail()
}
override fun getCurrent(username: String): List<BetDetail> {
return database.from(BetsEntity)
.innerJoin(ParticipationsEntity, on = BetsEntity.id eq ParticipationsEntity.betId)
.select()
.where {
(BetsEntity.status notEq BetStatus.FINISHED) and
(BetsEntity.status notEq BetStatus.CANCELLED) and
(ParticipationsEntity.username eq username)
}.map {
val participations = it[BetsEntity.id]?.let { betId ->
database.from(ParticipationsEntity)
.select().where { ParticipationsEntity.betId eq betId }.map { it.toParticipation() }
} ?: emptyList()
val bet = it.toBet()
BetDetail(
bet = bet,
answers = getBetAnswerDetail(bet, participations),
participations = participations,
userParticipation = it.toParticipation()
)
}
} }
override fun addBet(bet: Bet) { override fun addBet(bet: Bet) {

@ -58,6 +58,11 @@ class PostgresDataSource : AllInDataSource() {
betid uuid PRIMARY KEY REFERENCES bet, betid uuid PRIMARY KEY REFERENCES bet,
result varchar(250) result varchar(250)
) )
CREATE TABLE IF NOT EXISTS betresultnotification (
betid uuid,
username varchar(250),
CONSTRAINT pk_id_username PRIMARY KEY (betid, username)
)
""".trimIndent() """.trimIndent()
) )

@ -4,6 +4,7 @@ import org.ktorm.entity.Entity
import org.ktorm.schema.Table import org.ktorm.schema.Table
import org.ktorm.schema.uuid import org.ktorm.schema.uuid
import org.ktorm.schema.varchar import org.ktorm.schema.varchar
import java.util.*
interface BetResultEntity : Entity<BetResultEntity> { interface BetResultEntity : Entity<BetResultEntity> {
@ -15,3 +16,13 @@ object BetResultsEntity : Table<BetResultEntity>("betresult") {
val betId = uuid("betid").primaryKey().references(BetsEntity) { it.bet } val betId = uuid("betid").primaryKey().references(BetsEntity) { it.bet }
val result = varchar("result").bindTo { it.result } val result = varchar("result").bindTo { it.result }
} }
interface BetResultNotificationEntity : Entity<BetResultNotificationEntity> {
val betId: UUID
val username: String
}
object BetResultNotificationsEntity : Table<BetResultNotificationEntity>("betresult") {
val betId = uuid("betid").primaryKey()
val username = varchar("username").primaryKey()
}

@ -0,0 +1,12 @@
package allin.model
import kotlinx.serialization.Serializable
@Serializable
data class BetResultDetail(
val betResult: BetResult,
val bet: Bet,
val participation: Participation,
val amount: Int,
val won: Boolean
)

@ -1,12 +0,0 @@
package allin.model
import kotlinx.serialization.Serializable
@Serializable
data class ParticipationDetail(
val id: String,
val bet: Bet,
val username: String,
val answer: String,
val stake: Int
)

@ -52,25 +52,6 @@ fun Application.BetRouter() {
} }
} }
authenticate {
get("/bets/toConfirm") {
hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
val response = betDataSource.getToConfirm(user.username).map {
val participations = participationDataSource.getParticipationFromBetId(it.id)
BetDetail(
it,
getBetAnswerDetail(it, participations),
participations.toList(),
participationDataSource.getParticipationFromUserId(user.username, it.id).lastOrNull()
)
}
call.respond(HttpStatusCode.Accepted, response)
}
}
}
}
route("/bets/get/{id}") { route("/bets/get/{id}") {
get { get {
val id = call.parameters["id"] ?: "" val id = call.parameters["id"] ?: ""
@ -103,17 +84,49 @@ fun Application.BetRouter() {
} }
authenticate { authenticate {
get("/bets/current") { get("/bets/toConfirm") {
hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
val response = betDataSource.getToConfirm(user.username).map {
val participations = participationDataSource.getParticipationFromBetId(it.id)
BetDetail(
it,
getBetAnswerDetail(it, participations),
participations.toList(),
participationDataSource.getParticipationFromUserId(user.username, it.id).lastOrNull()
)
}
call.respond(HttpStatusCode.Accepted, response)
}
}
}
}
authenticate {
get("/bets/getWon") {
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
val currentBets = betDataSource.getBetsNotFinished() call.respond(HttpStatusCode.Accepted, betDataSource.getWonNotifications(user.username))
.filter { bet -> }
val userParticipation = }
participationDataSource.getParticipationFromUserId(user.username, bet.id) }
userParticipation.isNotEmpty()
} }
call.respond(HttpStatusCode.OK, currentBets) authenticate {
get("/bets/history") {
hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
call.respond(HttpStatusCode.Accepted, betDataSource.getHistory(user.username))
}
}
}
}
authenticate {
get("/bets/current") {
hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ ->
call.respond(HttpStatusCode.Accepted, betDataSource.getCurrent(user.username))
} }
} }
} }

@ -1,4 +1,4 @@
secret="secret" secret="secret"
issuer="http://0.0.0.0:8080/" issuer="http://0.0.0.0:8080/"
audience="http://0.0.0.0:8080/" audience="http://0.0.0.0:8080/"
realm="Access to main page" realm="allin"
Loading…
Cancel
Save