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<ArticleRef> = listOf() ) { class ArticleRef(val id: UUID) } fun validate() = Validation<Input> { 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<CitizenWithUserI, TitleForUpdate<ArticleRef>>( id = UUID.randomUUID(), title = title, titles = titles.create(), createdBy = citizen, versionId = versionId ) } private fun List<Title>.create(): MutableList<TitleForUpdate<ArticleRef>> = map { it.create() }.toMutableList() private fun Title.create(): TitleForUpdate<ArticleRef> = TitleForUpdate( id, name, articles.map { ArticleRef(it.id) } ) fun Route.createConstitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) { post<PostConstitutionRequest> { 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<Any> = c.titles.map { t -> object { val id: UUID = t.id val name: String = t.name val rank: Int = t.rank val articles: List<Any> = 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() } ) } } } }