Bet status, type and update status

pull/8/head
Arthur VALIN 1 year ago
parent addc40f4bf
commit 65cc0d8fde

@ -5,8 +5,8 @@ import allin.data.mock.MockDataSource
import allin.data.postgres.PostgresDataSource
import allin.routing.*
import allin.utils.TokenManager
import allin.utils.kronJob
import com.typesafe.config.ConfigFactory
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
@ -14,6 +14,13 @@ import io.ktor.server.config.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import kotlinx.serialization.json.Json
import java.time.ZonedDateTime
import kotlin.time.ExperimentalTime
import kotlin.time.minutes
@ExperimentalTime
val BET_VERIFY_DELAY = 5.minutes
val data_source = System.getenv()["DATA_SOURCE"]
@ -25,12 +32,21 @@ private val allInDataSource: AllInDataSource = when (data_source) {
val Application.dataSource: AllInDataSource
get() = allInDataSource
val json by lazy {
Json {
ignoreUnknownKeys = true
encodeDefaults = true
}
}
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
extracted()
}.start(wait = true)
}
@OptIn(ExperimentalTime::class)
private fun Application.extracted() {
val config = HoconApplicationConfig(ConfigFactory.load())
val tokenManager = TokenManager.getInstance(config)
@ -46,11 +62,16 @@ private fun Application.extracted() {
}
}
install(ContentNegotiation) {
json()
json
}
BasicRouting()
UserRouter()
BetRouter()
ParticipationRouter()
BetDetailRouter()
kronJob(BET_VERIFY_DELAY) {
dataSource.betDataSource.updateBetStatuses(ZonedDateTime.now())
}
}

@ -1,7 +1,12 @@
package allin.data
abstract class AllInDataSource {
abstract val userDataSource: UserDataSource
abstract val betDataSource: BetDataSource
abstract val participationDataSource: ParticipationDataSource
}

@ -2,6 +2,7 @@ package allin.data
import allin.model.Bet
import allin.model.UpdatedBetData
import java.time.ZonedDateTime
interface BetDataSource {
fun getAllBets(): List<Bet>
@ -10,4 +11,5 @@ interface BetDataSource {
fun addBet(bet: Bet)
fun removeBet(id: String): Boolean
fun updateBet(data: UpdatedBetData): Boolean
fun updateBetStatuses(date: ZonedDateTime)
}

@ -4,8 +4,15 @@ import allin.model.Participation
interface ParticipationDataSource {
fun addParticipation(participation: Participation)
fun getParticipationFromBetId(betid: String): List<Participation>
fun getParticipationFromUserId(username: String, betid: String): List<Participation>
fun deleteParticipation(id: String): Boolean
}

@ -4,9 +4,18 @@ import allin.dto.UserDTO
import allin.model.User
interface UserDataSource {
fun getUserByUsername(username: String): Pair<UserDTO?, String?>
fun addUser(user: User)
fun deleteUser(username: String): Boolean
fun modifyUserCoins(username: String, amount: Int)
fun userExists(username: String, email: String): Boolean
}

@ -2,6 +2,7 @@ package allin.data.mock
import allin.data.BetDataSource
import allin.model.Bet
import allin.model.BetStatus
import allin.model.UpdatedBetData
import java.time.ZonedDateTime
@ -26,6 +27,18 @@ class MockBetDataSource : BetDataSource {
bets += bet
}
override fun updateBetStatuses(date: ZonedDateTime) {
bets.forEachIndexed { idx, bet ->
if (bet.endRegistration >= date) {
if (bet.endBet >= date) {
bets[idx] = bet.copy(status = BetStatus.WAITING)
} else {
bets[idx] = bet.copy(status = BetStatus.CLOSING)
}
}
}
}
private val bets by lazy { mutableListOf<Bet>() }
}

@ -2,9 +2,14 @@ package allin.data.postgres
import allin.data.BetDataSource
import allin.entities.BetsEntity
import allin.entities.NO_VALUE
import allin.entities.ResponsesEntity
import allin.entities.ResponsesEntity.response
import allin.entities.YES_VALUE
import allin.model.Bet
import allin.model.BetStatus
import allin.model.BetType
import allin.model.UpdatedBetData
import allin.utils.Execute
import org.ktorm.database.Database
import org.ktorm.dsl.*
import java.time.ZoneId
@ -21,8 +26,21 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
endRegistration = this[BetsEntity.endRegistration]!!.atZone(ZoneId.of("Europe/Paris")),
endBet = this[BetsEntity.endBet]!!.atZone(ZoneId.of("Europe/Paris")),
isPrivate = this[BetsEntity.isPrivate] ?: false,
response = mutableListOf(), // ResponsesEntity.getResponse(UUID.fromString(this[BetsEntity.id].toString())),
createdBy = this[BetsEntity.createdBy].toString()
status = this[BetsEntity.status] ?: BetStatus.IN_PROGRESS,
type = this[BetsEntity.type] ?: BetType.CUSTOM,
createdBy = this[BetsEntity.createdBy].toString(),
response = let {
val idBet = this[BetsEntity.id].toString()
val type = this[BetsEntity.type] ?: BetType.CUSTOM
if (type == BetType.CUSTOM) {
database.from(ResponsesEntity)
.select(response)
.where { ResponsesEntity.id eq UUID.fromString(idBet) }
.map { it[response].toString() }
} else {
listOf(YES_VALUE, NO_VALUE)
}
}
)
private fun Query.mapToBet() = this.map { it.toBet() }
@ -53,7 +71,15 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
set(it.isPrivate, bet.isPrivate)
set(it.createdBy, bet.createdBy)
}
// ResponsesEntity.addResponse(bet.response, UUID.fromString(bet.id))
if (bet.type == BetType.CUSTOM) {
bet.response.forEach { selected ->
database.insert(ResponsesEntity) {
set(it.id, UUID.fromString(bet.id))
set(it.response, selected)
}
}
}
}
override fun removeBet(id: String): Boolean {
@ -67,9 +93,21 @@ class PostgresBetDataSource(private val database: Database) : BetDataSource {
} > 0
}
fun createBetsTable() {
val request =
"CREATE TABLE IF not exists bet ( id uuid PRIMARY KEY, theme VARCHAR(255), endregistration timestamp,endbet timestamp,sentencebet varchar(500),isprivate boolean, createdby varchar(250))"
database.Execute(request)
override fun updateBetStatuses(date: ZonedDateTime) {
database.update(BetsEntity) {
set(BetsEntity.status, BetStatus.WAITING)
where {
(BetsEntity.endRegistration greaterEq date.toInstant()) and
(BetsEntity.endBet less date.toInstant())
}
}
database.update(BetsEntity) {
set(BetsEntity.status, BetStatus.CLOSING)
where {
(BetsEntity.endRegistration greaterEq date.toInstant()) and
(BetsEntity.endBet greaterEq date.toInstant())
}
}
}
}

@ -4,6 +4,7 @@ import allin.data.AllInDataSource
import allin.data.BetDataSource
import allin.data.ParticipationDataSource
import allin.data.UserDataSource
import allin.utils.Execute
import org.ktorm.database.Database
class PostgresDataSource : AllInDataSource() {
@ -21,14 +22,14 @@ class PostgresDataSource : AllInDataSource() {
user = dbUser,
password = dbPassword
)
database.Execute("CREATE TABLE IF not exists utilisateur ( id uuid PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins double precision,email VARCHAR(255))")
database.Execute("CREATE TABLE IF not exists bet ( id uuid PRIMARY KEY, theme VARCHAR(255), endregistration timestamp,endbet timestamp,sentencebet varchar(500),isprivate boolean, createdby varchar(250))")
database.Execute("CREATE TABLE IF NOT EXISTS participation (id uuid PRIMARY KEY,bet uuid,username varchar(250),answer varchar(250),stake int);")
database.Execute("CREATE TABLE IF NOT EXISTS response (id UUID,response VARCHAR(250),CONSTRAINT pk_response_id PRIMARY KEY (id,response));")
}
override val userDataSource: UserDataSource = PostgresUserDataSource(database)
.also { it.createUserTable() }
override val betDataSource: BetDataSource = PostgresBetDataSource(database)
.also { it.createBetsTable() }
override val participationDataSource: ParticipationDataSource = PostgresParticipationDataSource(database)
.also { it.createParticipationTable() }
}

@ -3,7 +3,6 @@ package allin.data.postgres
import allin.data.ParticipationDataSource
import allin.entities.ParticipationsEntity
import allin.model.Participation
import allin.utils.Execute
import org.ktorm.database.Database
import org.ktorm.dsl.*
import java.util.*
@ -21,12 +20,6 @@ class PostgresParticipationDataSource(private val database: Database) : Particip
private fun Query.mapToParticipation() = this.map { it.toParticipation() }
fun createParticipationTable() {
val request =
"CREATE TABLE IF NOT EXISTS participation (id uuid PRIMARY KEY,bet uuid,username varchar(250),answer varchar(250),stake int);"
database.Execute(request)
}
override fun addParticipation(participation: Participation) {
database.insert(ParticipationsEntity) {
set(it.id, UUID.fromString(participation.id))

@ -55,10 +55,4 @@ class PostgresUserDataSource(private val database: Database) : UserDataSource {
val request = "UPDATE utilisateur SET coins = coins - $amount WHERE username = '$username';"
database.Execute(request)
}
fun createUserTable() {
val request =
"CREATE TABLE IF not exists utilisateur ( id uuid PRIMARY KEY, username VARCHAR(255), password VARCHAR(255),coins double precision,email VARCHAR(255))"
database.Execute(request)
}
}

@ -1,5 +1,7 @@
package allin.entities
import allin.model.BetStatus
import allin.model.BetType
import org.ktorm.entity.Entity
import org.ktorm.schema.*
import java.time.ZonedDateTime
@ -11,6 +13,8 @@ interface BetEntity : Entity<BetEntity> {
val endRegistration: ZonedDateTime
val endBet: ZonedDateTime
val isPrivate: Boolean
val status: BetStatus
val type: BetType
val createdBy: String
}
@ -21,5 +25,7 @@ object BetsEntity : Table<BetEntity>("bet") {
val endRegistration = timestamp("endregistration")
val endBet = timestamp("endbet")
val isPrivate = boolean("isprivate")
val status = enum<BetStatus>("status")
val type = enum<BetType>("type")
val createdBy = varchar("createdby")
}

@ -7,6 +7,9 @@ import org.ktorm.schema.varchar
import java.util.*
const val YES_VALUE = "Yes"
const val NO_VALUE = "No"
interface ResponseEntity : Entity<ResponseEntity> {
val betId: UUID
val response: String
@ -15,27 +18,4 @@ interface ResponseEntity : Entity<ResponseEntity> {
object ResponsesEntity : Table<ResponseEntity>("response") {
val id = uuid("id").primaryKey()
val response = varchar("response").primaryKey()
/*
fun createResponseTable(){
val request="CREATE TABLE IF NOT EXISTS response (id UUID,response VARCHAR(250),CONSTRAINT pk_response_id PRIMARY KEY (id,response));"
database.Execute(request)
}
fun getResponse(idBet: UUID): MutableList<String> {
return database.from(ResponsesEntity)
.select(response)
.where { id eq idBet }
.map { it[response].toString() }.toMutableList()
}
fun addResponse(responses : MutableList<String>, idBet : UUID ) {
responses.forEach {selected ->
database.insert(ResponsesEntity) {
set(it.id, idBet)
set(it.response,selected)
}
}
}
*/
}

@ -10,9 +10,11 @@ import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*
import io.ktor.util.pipeline.*
suspend fun PipelineContext<*, ApplicationCall>.hasToken(content: suspend (principal: JWTPrincipal) -> Unit) =
call.principal<JWTPrincipal>()?.let { content(it) } ?: call.respond(HttpStatusCode.Unauthorized)
suspend fun PipelineContext<*, ApplicationCall>.verifyUserFromToken(
userDataSource: UserDataSource,
principal: JWTPrincipal,

@ -1,20 +1,21 @@
package allin.model
import allin.serializer.UUIDSerializer
import allin.model.BetStatus.IN_PROGRESS
import allin.serializer.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable
import java.time.ZonedDateTime
import java.util.*
@Serializable
data class Bet(
val id: String = "",
val theme: String,
val sentenceBet: String,
val status: BetStatus = IN_PROGRESS,
val type: BetType,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
var response: MutableList<String>,
var response: List<String>,
val createdBy: String = ""
)
@ -23,5 +24,5 @@ data class UpdatedBetData(
val id: String,
@Serializable(ZonedDateTimeSerializer::class) val endBet: ZonedDateTime,
val isPrivate: Boolean,
val response: MutableList<String>
val response: List<String>
)

@ -0,0 +1,9 @@
package allin.model
enum class BetStatus {
IN_PROGRESS,
WAITING,
CLOSING,
FINISHED,
CANCELLED
}

@ -0,0 +1,7 @@
package allin.model
enum class BetType {
MATCH,
BINARY,
CUSTOM
}

@ -4,7 +4,8 @@ import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.BasicRouting(){
fun Application.BasicRouting() {
routing {
get("/") {
call.respond("Bienvenue sur l'API de AlLin!")

@ -11,6 +11,7 @@ import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Application.BetDetailRouter() {
val userDataSource = this.dataSource.userDataSource
val betDataSource = this.dataSource.betDataSource

@ -17,6 +17,7 @@ import java.util.*
val tokenManagerBet = AppConfig.tokenManager
fun Application.BetRouter() {
val userDataSource = this.dataSource.userDataSource
val betDataSource = this.dataSource.betDataSource
@ -34,14 +35,16 @@ fun Application.BetRouter() {
call.respond(HttpStatusCode.Conflict, ApiMessage.BetAlreadyExist)
} ?: run {
val betWithId = Bet(
id,
bet.theme,
bet.sentenceBet,
bet.endRegistration,
bet.endBet,
bet.isPrivate,
bet.response,
username
id = id,
theme = bet.theme,
sentenceBet = bet.sentenceBet,
status = bet.status,
type = bet.type,
endRegistration = bet.endRegistration,
endBet = bet.endBet,
isPrivate = bet.isPrivate,
response = bet.response,
createdBy = username
)
betDataSource.addBet(betWithId)
call.respond(HttpStatusCode.Created, betWithId)

@ -14,6 +14,7 @@ import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*
fun Application.ParticipationRouter() {
val userDataSource = this.dataSource.userDataSource

@ -20,6 +20,8 @@ val RegexCheckerUser = AppConfig.regexChecker
val CryptManagerUser = AppConfig.cryptManager
val tokenManagerUser = AppConfig.tokenManager
const val DEFAULT_COINS = 500
fun Application.UserRouter() {
val userDataSource = this.dataSource.userDataSource

@ -0,0 +1,16 @@
package allin.utils
import kotlinx.coroutines.*
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@OptIn(DelicateCoroutinesApi::class, ExperimentalTime::class)
fun kronJob(duration: Duration, action: () -> Unit) =
GlobalScope.launch {
withContext(Dispatchers.IO) {
while (true) {
runCatching { action() }
delay(duration.toLongMilliseconds())
}
}
}
Loading…
Cancel
Save