Merge remote-tracking branch 'origin/privateBet'

master
Lucas EVARD 11 months ago
commit f6e13ebb06

@ -1,10 +1,11 @@
package allin.data package allin.data
import allin.dto.UserDTO
import allin.model.* import allin.model.*
import java.time.ZonedDateTime import java.time.ZonedDateTime
interface BetDataSource { interface BetDataSource {
fun getAllBets(filters: List<BetFilter>): List<Bet> fun getAllBets(filters: List<BetFilter>, userDTO: UserDTO): List<Bet>
fun getBetById(id: String): Bet? fun getBetById(id: String): Bet?
fun getBetDetailById(id: String, username: String): BetDetail? fun getBetDetailById(id: String, username: String): BetDetail?
fun getBetsNotFinished(): List<Bet> fun getBetsNotFinished(): List<Bet>
@ -19,4 +20,6 @@ interface BetDataSource {
fun getCurrent(username: String): List<BetDetail> fun getCurrent(username: String): List<BetDetail>
fun getMostPopularBet(): Bet? fun getMostPopularBet(): Bet?
fun updatePopularityScore(betId: String) fun updatePopularityScore(betId: String)
fun addPrivateBet(bet: Bet)
fun isInvited(betid: String, userId: String): Boolean
} }

@ -1,6 +1,7 @@
package allin.data.mock package allin.data.mock
import allin.data.BetDataSource import allin.data.BetDataSource
import allin.dto.UserDTO
import allin.model.* import allin.model.*
import allin.model.BetStatus.* import allin.model.BetStatus.*
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -15,7 +16,7 @@ class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetData
private val betInfos get() = mockData.betInfos private val betInfos get() = mockData.betInfos
private val answerInfos get() = mockData.answerInfos private val answerInfos get() = mockData.answerInfos
override fun getAllBets(filters: List<BetFilter>): List<Bet> { override fun getAllBets(filters: List<BetFilter>, userDTO: UserDTO): List<Bet> {
return when { return when {
filters.isEmpty() -> bets filters.isEmpty() -> bets
@ -233,4 +234,12 @@ class MockBetDataSource(private val mockData: MockDataSource.MockData) : BetData
bet.popularityscore = score bet.popularityscore = score
} }
override fun addPrivateBet(bet: Bet) {
TODO()
}
override fun isInvited(betid: String, userId: String): Boolean {
TODO("Not yet implemented")
}
} }

@ -2,6 +2,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.dto.UserDTO
import allin.model.* import allin.model.*
import org.ktorm.database.Database import org.ktorm.database.Database
import org.ktorm.dsl.* import org.ktorm.dsl.*
@ -13,9 +14,10 @@ import kotlin.math.roundToInt
class PostgresBetDataSource(private val database: Database) : BetDataSource { class PostgresBetDataSource(private val database: Database) : BetDataSource {
override fun getAllBets(filters: List<BetFilter>): List<Bet> { override fun getAllBets(filters: List<BetFilter>, userDTO: UserDTO): List<Bet> {
return when { return when {
filters.isEmpty() -> database.bets.map { it.toBet(database) } filters.isEmpty() -> database.bets.map { it.toBet(database) }
.filter { (!it.isPrivate) or (isInvited(it.id, userDTO.id)) or (it.createdBy == userDTO.id) }
filters.size == 1 -> { filters.size == 1 -> {
val filter = filters.first() val filter = filters.first()
@ -28,6 +30,7 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
it.status inList listOf(BetStatus.IN_PROGRESS, BetStatus.WAITING, BetStatus.CLOSING) it.status inList listOf(BetStatus.IN_PROGRESS, BetStatus.WAITING, BetStatus.CLOSING)
} }
}.map { it.toBet(database) } }.map { it.toBet(database) }
.filter { (!it.isPrivate) or (isInvited(it.id, userDTO.id)) or (it.createdBy == userDTO.id) }
} }
else -> { else -> {
@ -44,11 +47,11 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
(public or invitation) and (finished or inProgress) (public or invitation) and (finished or inProgress)
}.map { it.toBet(database) } }.map { it.toBet(database) }
.filter { (!it.isPrivate) or (isInvited(it.id, userDTO.id)) or (it.createdBy == userDTO.id) }
} }
} }
} }
override fun getBetById(id: String): Bet? = override fun getBetById(id: String): Bet? =
database.bets.find { it.id eq id }?.toBet(database) database.bets.find { it.id eq id }?.toBet(database)
@ -269,4 +272,17 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
} }
} }
override fun addPrivateBet(bet: Bet) {
addBet(bet)
bet.userInvited?.forEach {
database.privatebets.add(PrivateBetEntity {
betId = bet.id
userId = it
})
}
}
override fun isInvited(betid: String, userId: String): Boolean {
return database.privatebets.filter { (it.betid eq betid) and (it.userId eq userId) }.isNotEmpty()
}
} }

@ -144,6 +144,17 @@ class PostgresDataSource : AllInDataSource() {
) )
""".trimIndent() """.trimIndent()
) )
database.execute(
"""
CREATE TABLE IF NOT EXISTS privatebet
(
betid VARCHAR(255),
userid VARCHAR(255),
CONSTRAINT pk_privatebet PRIMARY KEY (betid,userid)
)
""".trimIndent()
)
} }
override val userDataSource: UserDataSource by lazy { PostgresUserDataSource(database) } override val userDataSource: UserDataSource by lazy { PostgresUserDataSource(database) }

@ -41,7 +41,7 @@ interface BetEntity : Entity<BetEntity> {
} else { } else {
database.responses.filter { it.betId eq id }.map { it.response } database.responses.filter { it.betId eq id }.map { it.response }
}, },
createdBy = createdBy, createdBy = database.users.first { it.id eq createdBy }.username,
popularityscore = popularityscore, popularityscore = popularityscore,
totalStakes = betInfo?.totalStakes ?: 0, totalStakes = betInfo?.totalStakes ?: 0,
totalParticipants = betInfo?.totalParticipants ?: 0 totalParticipants = betInfo?.totalParticipants ?: 0

@ -0,0 +1,21 @@
package allin.data.postgres.entities
import org.ktorm.database.Database
import org.ktorm.entity.Entity
import org.ktorm.entity.sequenceOf
import org.ktorm.schema.Table
import org.ktorm.schema.varchar
interface PrivateBetEntity : Entity<PrivateBetEntity> {
companion object : Entity.Factory<PrivateBetEntity>()
var betId: String
var userId: String
}
object PrivateBetsEntity : Table<PrivateBetEntity>("privatebet") {
val betid = varchar("betid").bindTo { it.betId }
val userId = varchar("userid").bindTo { it.userId }
}
val Database.privatebets get() = this.sequenceOf(PrivateBetsEntity)

@ -22,7 +22,8 @@ data class Bet(
val createdBy: String = "", val createdBy: String = "",
var popularityscore: Int = 0, var popularityscore: Int = 0,
val totalStakes: Int = 0, val totalStakes: Int = 0,
val totalParticipants: Int = 0 val totalParticipants: Int = 0,
val userInvited: List<String>? = null
) )
@Serializable @Serializable

@ -48,19 +48,22 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","POST /bets/add") logManager.log("Routing", "POST /bets/add")
hasToken { principal -> hasToken { principal ->
val bet = call.receive<Bet>() val bet = call.receive<Bet>()
val id = UUID.randomUUID().toString() val id = UUID.randomUUID().toString()
val username = tokenManagerBet.getUsernameFromToken(principal) val username = tokenManagerBet.getUsernameFromToken(principal)
val user = userDataSource.getUserByUsername(username) val user = userDataSource.getUserByUsername(username)
betDataSource.getBetById(id)?.let { betDataSource.getBetById(id)?.let {
logManager.log("Routing","${ApiMessage.BET_ALREADY_EXIST} /bets/add") logManager.log("Routing", "${ApiMessage.BET_ALREADY_EXIST} /bets/add")
call.respond(HttpStatusCode.Conflict, ApiMessage.BET_ALREADY_EXIST) call.respond(HttpStatusCode.Conflict, ApiMessage.BET_ALREADY_EXIST)
} ?: run { } ?: run {
val betWithId = bet.copy(id = id, createdBy = user.first?.username.toString()) val betWithId = bet.copy(id = id, createdBy = user.first?.id.toString())
betDataSource.addBet(betWithId)
logManager.log("Routing","CREATED /bets/add\t${betWithId}") if (bet.isPrivate && bet.userInvited?.isNotEmpty() == true) {
betDataSource.addPrivateBet(betWithId)
} else betDataSource.addBet(betWithId)
logManager.log("Routing", "CREATED /bets/add\t${betWithId}")
call.respond(HttpStatusCode.Created, betWithId) call.respond(HttpStatusCode.Created, betWithId)
} }
} }
@ -84,15 +87,15 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","POST /bets/gets") logManager.log("Routing", "POST /bets/gets")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { _, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
val filtersRequest = val filtersRequest =
kotlin.runCatching { call.receiveNullable<BetFiltersRequest>() }.getOrNull() kotlin.runCatching { call.receiveNullable<BetFiltersRequest>() }.getOrNull()
val filters = val filters =
filtersRequest?.filters ?: emptyList() // Use provided filters or empty list if null filtersRequest?.filters ?: emptyList() // Use provided filters or empty list if null
logManager.log("Routing","ACCEPTED /bets/gets\t${filters}") logManager.log("Routing", "ACCEPTED /bets/gets\t${filters}")
call.respond(HttpStatusCode.Accepted, betDataSource.getAllBets(filters)) call.respond(HttpStatusCode.Accepted, betDataSource.getAllBets(filters, user))
} }
} }
} }
@ -113,15 +116,15 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/popular") logManager.log("Routing", "GET /bets/popular")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { _, _ -> verifyUserFromToken(userDataSource, principal) { _, _ ->
val bet = betDataSource.getMostPopularBet() val bet = betDataSource.getMostPopularBet()
if (bet != null) { if (bet != null) {
logManager.log("Routing","ACCEPTED /bets/popular\t${bet}") logManager.log("Routing", "ACCEPTED /bets/popular\t${bet}")
call.respond(HttpStatusCode.Accepted, bet) call.respond(HttpStatusCode.Accepted, bet)
} }
logManager.log("Routing","${ApiMessage.BET_NOT_FOUND} /bets/popular") logManager.log("Routing", "${ApiMessage.BET_NOT_FOUND} /bets/popular")
call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND) call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND)
} }
} }
@ -146,13 +149,12 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/get/{id}") logManager.log("Routing", "GET /bets/get/{id}")
val id = call.parameters["id"] ?: "" val id = call.parameters["id"] ?: ""
betDataSource.getBetById(id)?.let { bet -> betDataSource.getBetById(id)?.let { bet ->
logManager.log("Routing","ACCEPTED /bets/get/{id}\t ${bet}") logManager.log("Routing", "ACCEPTED /bets/get/{id}\t ${bet}")
call.respond(HttpStatusCode.Accepted, bet) call.respond(HttpStatusCode.Accepted, bet)
} ?: } ?: logManager.log("Routing", "${ApiMessage.BET_NOT_FOUND} /bets/get/{id}")
logManager.log("Routing","${ApiMessage.BET_NOT_FOUND} /bets/get/{id}")
call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND) call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND)
} }
@ -173,13 +175,13 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","POST /bets/delete") logManager.log("Routing", "POST /bets/delete")
val id = call.receive<Map<String, String>>()["id"] ?: "" val id = call.receive<Map<String, String>>()["id"] ?: ""
if (betDataSource.removeBet(id)) { if (betDataSource.removeBet(id)) {
logManager.log("Routing","ACCEPTED /bets/delete") logManager.log("Routing", "ACCEPTED /bets/delete")
call.respond(HttpStatusCode.Accepted) call.respond(HttpStatusCode.Accepted)
} else { } else {
logManager.log("Routing","${ApiMessage.BET_NOT_FOUND} /bets/delete") logManager.log("Routing", "${ApiMessage.BET_NOT_FOUND} /bets/delete")
call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND) call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND)
} }
} }
@ -201,13 +203,13 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","POST /bets/update") logManager.log("Routing", "POST /bets/update")
val updatedBetData = call.receive<UpdatedBetData>() val updatedBetData = call.receive<UpdatedBetData>()
if (betDataSource.updateBet(updatedBetData)) { if (betDataSource.updateBet(updatedBetData)) {
logManager.log("Routing","ACCEPTED /bets/delete") logManager.log("Routing", "ACCEPTED /bets/delete")
call.respond(HttpStatusCode.Accepted) call.respond(HttpStatusCode.Accepted)
} else { } else {
logManager.log("Routing","${ApiMessage.BET_NOT_FOUND} /bets/delete") logManager.log("Routing", "${ApiMessage.BET_NOT_FOUND} /bets/delete")
call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND) call.respond(HttpStatusCode.NotFound, ApiMessage.BET_NOT_FOUND)
} }
} }
@ -227,11 +229,11 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/toConfirm") logManager.log("Routing", "GET /bets/toConfirm")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
val response = betDataSource.getToConfirm(user.username) val response = betDataSource.getToConfirm(user.username)
logManager.log("Routing","ACCEPTED /bets/toConfirm\t${response}") logManager.log("Routing", "ACCEPTED /bets/toConfirm\t${response}")
call.respond(HttpStatusCode.Accepted, response) call.respond(HttpStatusCode.Accepted, response)
} }
} }
@ -253,10 +255,10 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/getWon") logManager.log("Routing", "GET /bets/getWon")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
logManager.log("Routing","ACCEPTED /bets/getWon") logManager.log("Routing", "ACCEPTED /bets/getWon")
call.respond(HttpStatusCode.Accepted, betDataSource.getWonNotifications(user.username)) call.respond(HttpStatusCode.Accepted, betDataSource.getWonNotifications(user.username))
} }
} }
@ -278,10 +280,13 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/history") logManager.log("Routing", "GET /bets/history")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
logManager.log("Routing","ACCEPTED /bets/toConfirm\t${betDataSource.getHistory(user.username)}") logManager.log(
"Routing",
"ACCEPTED /bets/toConfirm\t${betDataSource.getHistory(user.username)}"
)
call.respond(HttpStatusCode.Accepted, betDataSource.getHistory(user.username)) call.respond(HttpStatusCode.Accepted, betDataSource.getHistory(user.username))
} }
} }
@ -303,10 +308,13 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/current") logManager.log("Routing", "GET /bets/current")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
logManager.log("Routing","ACCEPTED /bets/toConfirm\t${betDataSource.getCurrent(user.username)}") logManager.log(
"Routing",
"ACCEPTED /bets/toConfirm\t${betDataSource.getCurrent(user.username)}"
)
call.respond(HttpStatusCode.Accepted, betDataSource.getCurrent(user.username)) call.respond(HttpStatusCode.Accepted, betDataSource.getCurrent(user.username))
} }
} }
@ -332,7 +340,7 @@ fun Application.betRouter() {
} }
} }
}) { }) {
logManager.log("Routing","GET /bets/confirm/{id}") logManager.log("Routing", "GET /bets/confirm/{id}")
hasToken { principal -> hasToken { principal ->
verifyUserFromToken(userDataSource, principal) { user, _ -> verifyUserFromToken(userDataSource, principal) { user, _ ->
val betId = call.parameters["id"] ?: "" val betId = call.parameters["id"] ?: ""
@ -340,10 +348,10 @@ fun Application.betRouter() {
if (betDataSource.getBetById(betId)?.createdBy == user.username) { if (betDataSource.getBetById(betId)?.createdBy == user.username) {
betDataSource.confirmBet(betId, result) betDataSource.confirmBet(betId, result)
logManager.log("Routing","ACCEPTED /bets/confirm/{id}") logManager.log("Routing", "ACCEPTED /bets/confirm/{id}")
call.respond(HttpStatusCode.OK) call.respond(HttpStatusCode.OK)
} else { } else {
logManager.log("Routing","UNAUTHORIZED /bets/confirm/{id}") logManager.log("Routing", "UNAUTHORIZED /bets/confirm/{id}")
call.respond(HttpStatusCode.Unauthorized) call.respond(HttpStatusCode.Unauthorized)
} }
} }

Loading…
Cancel
Save