package fr.dcproject.component.constitution.routes
import fr.dcproject.application.http.badRequestIfNotValid
import fr.dcproject.common.response.toOutput
import fr.dcproject.common.security.assert
import fr.dcproject.common.utils.receiveOrBadRequest
import fr.dcproject.component.article.database.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.auth.mustBeAuth
import fr.dcproject.component.citizen.database.Citizen
import fr.dcproject.component.citizen.database.CitizenWithUserI
import fr.dcproject.component.constitution.ConstitutionAccessControl
import fr.dcproject.component.constitution.database.ConstitutionForUpdate
import fr.dcproject.component.constitution.database.ConstitutionForUpdate.TitleForUpdate
import fr.dcproject.component.constitution.database.ConstitutionRepository
import fr.dcproject.component.constitution.routes.CreateConstitution.PostConstitutionRequest.Input
import fr.dcproject.component.constitution.routes.CreateConstitution.PostConstitutionRequest.Input.Title
import io.konform.validation.Validation
import io.konform.validation.jsonschema.maxLength
import io.konform.validation.jsonschema.minLength
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.post
import io.ktor.response.respond
import io.ktor.routing.Route
import org.joda.time.DateTime
import java.util.UUID
@KtorExperimentalLocationsAPI
object CreateConstitution {
@Location("/constitutions")
class PostConstitutionRequest {
class Input(
val title: String,
val anonymous: Boolean = true,
val titles: MutableList
= mutableListOf(),
val draft: Boolean = false,
val versionId: UUID = UUID.randomUUID()
) {
class Title(
val id: UUID = UUID.randomUUID(),
val name: String,
val articles: List = listOf()
) {
class ArticleRef(val id: UUID)
}
fun validate() = Validation {
Input::title {
minLength(10)
maxLength(80)
}
Input::titles onEach {
Title::name {
minLength(10)
maxLength(80)
}
}
}.validate(this)
}
}
private fun getNewConstitution(input: Input, citizen: Citizen) = input.run {
validate().badRequestIfNotValid()
ConstitutionForUpdate>(
id = UUID.randomUUID(),
title = title,
titles = titles.create(),
createdBy = citizen,
versionId = versionId
)
}
private fun List.create(): MutableList> =
map { it.create() }.toMutableList()
private fun Title.create(): TitleForUpdate =
TitleForUpdate(
id,
name,
articles.map { ArticleRef(it.id) }
)
fun Route.createConstitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
post {
mustBeAuth()
getNewConstitution(call.receiveOrBadRequest(), citizen).let {
ac.assert { canCreate(it, citizenOrNull) }
val c = repo.upsert(it) ?: error("Unable to create Constitution")
call.respond(
HttpStatusCode.Created,
object {
val id: UUID = c.id
val title: String = c.title
val titles: List = c.titles.map { t ->
object {
val id: UUID = t.id
val name: String = t.name
val rank: Int = t.rank
val articles: List = t.articles.map { a ->
val id = a.id
val title = a.title
val createdBy = a.createdBy.toOutput()
val workgroup: Any? = a.workgroup?.let { w ->
object {
val id = w.id
val name = w.name
}
}
}
}
}
val anonymous: Boolean = c.anonymous
val draft: Boolean = c.draft
val versionId: UUID = c.versionId
val createdAt: DateTime = c.createdAt
val createdBy: Any = c.createdBy.toOutput()
}
)
}
}
}
}