Big refactoring #77
@@ -7,28 +7,15 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import com.fasterxml.jackson.datatype.joda.JodaModule
|
||||
import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
|
||||
import component.auth.jwt.jwtInstallation
|
||||
import fr.dcproject.application.Env.PROD
|
||||
import fr.dcproject.component.article.routes.findArticleVersions
|
||||
import fr.dcproject.component.article.routes.findArticles
|
||||
import fr.dcproject.component.article.routes.getOneArticle
|
||||
import fr.dcproject.component.article.routes.upsertArticle
|
||||
import fr.dcproject.component.article.routes.installArticleRoutes
|
||||
import fr.dcproject.component.auth.ForbiddenException
|
||||
import fr.dcproject.component.auth.routes.authLogin
|
||||
import fr.dcproject.component.auth.routes.authPasswordless
|
||||
import fr.dcproject.component.auth.routes.authRegister
|
||||
import fr.dcproject.component.auth.jwt.jwtInstallation
|
||||
import fr.dcproject.component.auth.routes.installAuthRoutes
|
||||
import fr.dcproject.component.auth.user
|
||||
import fr.dcproject.component.citizen.routes.changeMyPassword
|
||||
import fr.dcproject.component.citizen.routes.findCitizen
|
||||
import fr.dcproject.component.citizen.routes.getCurrentCitizen
|
||||
import fr.dcproject.component.citizen.routes.getOneCitizen
|
||||
import fr.dcproject.component.comment.article.routes.createCommentArticle
|
||||
import fr.dcproject.component.comment.article.routes.getArticleComments
|
||||
import fr.dcproject.component.comment.article.routes.getCitizenArticleComments
|
||||
import fr.dcproject.component.comment.generic.routes.createCommentChildren
|
||||
import fr.dcproject.component.comment.generic.routes.editComment
|
||||
import fr.dcproject.component.comment.generic.routes.getChildrenComments
|
||||
import fr.dcproject.component.comment.generic.routes.getOneComment
|
||||
import fr.dcproject.component.citizen.routes.installCitizenRoutes
|
||||
import fr.dcproject.component.comment.article.routes.installCommentArticleRoutes
|
||||
import fr.dcproject.component.comment.generic.routes.installCommentRoutes
|
||||
import fr.dcproject.component.follow.routes.article.FollowArticle.followArticle
|
||||
import fr.dcproject.component.follow.routes.article.GetFollowArticle.getFollowArticle
|
||||
import fr.dcproject.component.follow.routes.article.GetMyFollowsArticle.getMyFollowsArticle
|
||||
@@ -86,14 +73,14 @@ import io.ktor.routing.Routing
|
||||
import io.ktor.server.jetty.EngineMain
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import io.ktor.websocket.WebSockets
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.CompletionException
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import org.eclipse.jetty.util.log.Slf4jLog
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.ktor.ext.Koin
|
||||
import org.koin.ktor.ext.get
|
||||
import org.slf4j.event.Level
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.CompletionException
|
||||
|
||||
fun main(args: Array<String>): Unit = EngineMain.main(args)
|
||||
|
||||
@@ -158,30 +145,13 @@ fun Application.module(env: Env = PROD) {
|
||||
|
||||
install(Routing.Feature) {
|
||||
// trace { application.log.trace(it.buildText()) }
|
||||
installArticleRoutes()
|
||||
installAuthRoutes()
|
||||
installCitizenRoutes()
|
||||
installCommentArticleRoutes()
|
||||
installCommentRoutes()
|
||||
|
||||
authenticate(optional = true) {
|
||||
/* Article */
|
||||
findArticles(get(), get())
|
||||
getOneArticle(get(), get())
|
||||
upsertArticle(get(), get(), get())
|
||||
findArticleVersions(get(), get())
|
||||
/* Citizen */
|
||||
findCitizen(get(), get())
|
||||
getOneCitizen(get())
|
||||
getCurrentCitizen(get())
|
||||
changeMyPassword(get(), get())
|
||||
/* Comment */
|
||||
editComment(get(), get())
|
||||
getOneComment(get(), get())
|
||||
createCommentChildren(get(), get())
|
||||
getChildrenComments(get(), get())
|
||||
/* Comment Article */
|
||||
getArticleComments(get(), get())
|
||||
createCommentArticle(get(), get())
|
||||
getCitizenArticleComments(get(), get())
|
||||
/* Auth */
|
||||
authLogin(get())
|
||||
authRegister(get())
|
||||
authPasswordless(get())
|
||||
/* Workgroup */
|
||||
getWorkgroups(get(), get())
|
||||
getWorkgroup(get(), get())
|
||||
|
||||
@@ -14,28 +14,28 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/articles/{article}/versions")
|
||||
class ArticleVersionsRequest(
|
||||
object FindArticleVersions {
|
||||
@Location("/articles/{article}/versions")
|
||||
class ArticleVersionsRequest(
|
||||
val article: ArticleForView,
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val sort: String? = null,
|
||||
val direction: RepositoryI.Direction? = null,
|
||||
val search: String? = null
|
||||
) {
|
||||
) {
|
||||
val page: Int = if (page < 1) 1 else page
|
||||
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
private fun ArticleRepository.findVersions(request: ArticleVersionsRequest) =
|
||||
private fun ArticleRepository.findVersions(request: ArticleVersionsRequest) =
|
||||
findVersionsByVersionId(request.page, request.limit, request.article.versionId)
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.findArticleVersions(repo: ArticleRepository, voter: ArticleVoter) {
|
||||
fun Route.findArticleVersions(repo: ArticleRepository, voter: ArticleVoter) {
|
||||
get<ArticleVersionsRequest> {
|
||||
repo.findVersions(it)
|
||||
.apply { voter.assert { canView(it.article, citizenOrNull) } }
|
||||
.let { call.respond(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,16 @@ import fr.dcproject.voter.assert
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.repository.RepositoryI
|
||||
import io.ktor.application.call
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@Location("/articles")
|
||||
class ArticlesRequest(
|
||||
@KtorExperimentalLocationsAPI
|
||||
object FindArticles {
|
||||
@Location("/articles")
|
||||
class ArticlesRequest(
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val sort: String? = null,
|
||||
@@ -22,12 +25,12 @@ class ArticlesRequest(
|
||||
val search: String? = null,
|
||||
val createdBy: String? = null,
|
||||
val workgroup: String? = null
|
||||
) {
|
||||
) {
|
||||
val page: Int = if (page < 1) 1 else page
|
||||
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
}
|
||||
|
||||
private fun ArticleRepository.findArticles(request: ArticlesRequest): Paginated<ArticleForListing> {
|
||||
private fun ArticleRepository.findArticles(request: ArticlesRequest): Paginated<ArticleForListing> {
|
||||
return find(
|
||||
request.page,
|
||||
request.limit,
|
||||
@@ -36,12 +39,13 @@ private fun ArticleRepository.findArticles(request: ArticlesRequest): Paginated<
|
||||
request.search,
|
||||
ArticleRepository.Filter(createdById = request.createdBy, workgroupId = request.workgroup)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Route.findArticles(repo: ArticleRepository, voter: ArticleVoter) {
|
||||
fun Route.findArticles(repo: ArticleRepository, voter: ArticleVoter) {
|
||||
get<ArticlesRequest> {
|
||||
repo.findArticles(it)
|
||||
.apply { voter.assert { canView(result, citizenOrNull) } }
|
||||
.let { call.respond(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.article.ArticleViewManager
|
||||
import fr.dcproject.component.article.ArticleVoter
|
||||
import fr.dcproject.component.article.routes.ArticleRequest.Output
|
||||
import fr.dcproject.component.article.routes.GetOneArticle.ArticleRequest.Output
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.dto.CreatedAt
|
||||
import fr.dcproject.dto.Opinionable
|
||||
@@ -19,18 +19,17 @@ import io.ktor.locations.Location
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/articles/{articleId}")
|
||||
class ArticleRequest(val articleId: UUID) : KoinComponent {
|
||||
object GetOneArticle {
|
||||
@Location("/articles/{articleId}")
|
||||
class ArticleRequest(val articleId: UUID) : KoinComponent {
|
||||
val repo: ArticleRepository by inject()
|
||||
|
||||
@KtorExperimentalAPI
|
||||
val article: ArticleForView = repo.findById(articleId) ?: throw NotFoundException("Article $articleId not found")
|
||||
|
||||
class Output(
|
||||
@@ -52,11 +51,9 @@ class ArticleRequest(val articleId: UUID) : KoinComponent {
|
||||
val createdBy = article.createdBy
|
||||
val workgroup = article.workgroup // TODO change to workgroup DTO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getOneArticle(viewManager: ArticleViewManager, voter: ArticleVoter) {
|
||||
fun Route.getOneArticle(viewManager: ArticleViewManager, voter: ArticleVoter) {
|
||||
get<ArticleRequest> {
|
||||
voter.assert { canView(it.article, citizenOrNull) }
|
||||
|
||||
@@ -71,4 +68,5 @@ fun Route.getOneArticle(viewManager: ArticleViewManager, voter: ArticleVoter) {
|
||||
viewManager.addView(call.request.local.remoteHost, it.article, citizenOrNull)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import fr.dcproject.component.article.ArticleForUpdate
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.article.ArticleVoter
|
||||
import fr.dcproject.component.article.routes.PostArticleRequest.Input
|
||||
import fr.dcproject.component.article.routes.UpsertArticle.UpsertArticleRequest.Input
|
||||
import fr.dcproject.component.auth.citizen
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.component.workgroup.WorkgroupRef
|
||||
@@ -23,8 +23,9 @@ import io.ktor.routing.Route
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/articles")
|
||||
class PostArticleRequest {
|
||||
object UpsertArticle {
|
||||
@Location("/articles")
|
||||
class UpsertArticleRequest {
|
||||
class Input(
|
||||
val id: UUID?,
|
||||
val title: String,
|
||||
@@ -36,10 +37,9 @@ class PostArticleRequest {
|
||||
val versionId: UUID?,
|
||||
val workgroup: WorkgroupRef? = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.upsertArticle(repo: ArticleRepository, workgroupRepository: WorkgroupRepository, voter: ArticleVoter) {
|
||||
fun Route.upsertArticle(repo: ArticleRepository, workgroupRepository: WorkgroupRepository, voter: ArticleVoter) {
|
||||
suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receive<Input>().run {
|
||||
ArticleForUpdate(
|
||||
id = id ?: UUID.randomUUID(),
|
||||
@@ -55,15 +55,12 @@ fun Route.upsertArticle(repo: ArticleRepository, workgroupRepository: WorkgroupR
|
||||
)
|
||||
}
|
||||
|
||||
post<PostArticleRequest> {
|
||||
post<UpsertArticleRequest> {
|
||||
val article = call.convertRequestToEntity()
|
||||
|
||||
voter.assert { canUpsert(article, citizenOrNull) }
|
||||
|
||||
val newArticle: ArticleForView = repo.upsert(article) ?: error("Article not updated")
|
||||
|
||||
call.respond(newArticle)
|
||||
|
||||
raiseEvent(ArticleUpdate.event, ArticleUpdate(newArticle))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
src/main/kotlin/component/article/routes/install.kt
Normal file
21
src/main/kotlin/component/article/routes/install.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package fr.dcproject.component.article.routes
|
||||
|
||||
import fr.dcproject.component.article.routes.FindArticleVersions.findArticleVersions
|
||||
import fr.dcproject.component.article.routes.FindArticles.findArticles
|
||||
import fr.dcproject.component.article.routes.GetOneArticle.getOneArticle
|
||||
import fr.dcproject.component.article.routes.UpsertArticle.upsertArticle
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installArticleRoutes() {
|
||||
authenticate(optional = true) {
|
||||
findArticles(get(), get())
|
||||
findArticleVersions(get(), get())
|
||||
getOneArticle(get(), get())
|
||||
upsertArticle(get(), get(), get())
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import com.sendgrid.helpers.mail.objects.Email
|
||||
import fr.dcproject.component.citizen.CitizenRepository
|
||||
import fr.dcproject.component.citizen.CitizenWithEmail
|
||||
import fr.dcproject.component.citizen.CitizenWithUserI
|
||||
import fr.dcproject.makeToken
|
||||
import fr.dcproject.component.auth.jwt.makeToken
|
||||
import fr.dcproject.messages.Mailer
|
||||
import io.ktor.http.URLBuilder
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package fr.dcproject
|
||||
package fr.dcproject.component.auth.jwt
|
||||
|
||||
import com.auth0.jwt.JWT
|
||||
import fr.dcproject.component.auth.UserI
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package component.auth.jwt
|
||||
package fr.dcproject.component.auth.jwt
|
||||
|
||||
import fr.dcproject.component.auth.User
|
||||
import fr.dcproject.component.auth.UserRepository
|
||||
import fr.dcproject.component.auth.jwt.JwtConfig
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.auth.Authentication
|
||||
import io.ktor.auth.jwt.jwt
|
||||
|
||||
@@ -2,7 +2,7 @@ package fr.dcproject.component.auth.routes
|
||||
|
||||
import com.fasterxml.jackson.databind.exc.MismatchedInputException
|
||||
import fr.dcproject.component.auth.UserRepository
|
||||
import fr.dcproject.makeToken
|
||||
import fr.dcproject.component.auth.jwt.makeToken
|
||||
import io.ktor.application.call
|
||||
import io.ktor.auth.UserPasswordCredential
|
||||
import io.ktor.http.HttpStatusCode
|
||||
@@ -13,15 +13,13 @@ import io.ktor.request.receive
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/login")
|
||||
class LoginRequest
|
||||
object Login {
|
||||
@Location("/login")
|
||||
class LoginRequest
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
fun Route.authLogin(userRepo: UserRepository) {
|
||||
fun Route.authLogin(userRepo: UserRepository) {
|
||||
post<LoginRequest> {
|
||||
try {
|
||||
val credentials = call.receive<UserPasswordCredential>()
|
||||
@@ -32,4 +30,5 @@ fun Route.authLogin(userRepo: UserRepository) {
|
||||
call.respond(HttpStatusCode.BadRequest, "You must be send name and password to the request")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package fr.dcproject.component.auth.routes
|
||||
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
|
||||
import fr.dcproject.component.auth.User
|
||||
import fr.dcproject.component.auth.UserI
|
||||
import fr.dcproject.component.auth.routes.RegisterRequest.Input
|
||||
import fr.dcproject.component.auth.routes.Register.RegisterRequest.Input
|
||||
import fr.dcproject.component.citizen.Citizen
|
||||
import fr.dcproject.component.citizen.CitizenI
|
||||
import fr.dcproject.component.citizen.CitizenRepository
|
||||
import fr.dcproject.makeToken
|
||||
import fr.dcproject.component.auth.jwt.makeToken
|
||||
import io.ktor.application.call
|
||||
import io.ktor.features.BadRequestException
|
||||
import io.ktor.http.HttpStatusCode
|
||||
@@ -18,12 +18,12 @@ import io.ktor.request.receive
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.joda.time.DateTime
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/register")
|
||||
class RegisterRequest {
|
||||
object Register {
|
||||
@Location("/register")
|
||||
class RegisterRequest {
|
||||
data class Input(
|
||||
val name: Name,
|
||||
val email: String,
|
||||
@@ -42,11 +42,9 @@ class RegisterRequest {
|
||||
val plainPassword: String? = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
fun Route.authRegister(citizenRepo: CitizenRepository) {
|
||||
fun Route.authRegister(citizenRepo: CitizenRepository) {
|
||||
fun Input.toCitizen(): Citizen = Citizen(
|
||||
name = CitizenI.Name(name.firstName, name.lastName, name.civility),
|
||||
birthday = birthday,
|
||||
@@ -69,4 +67,5 @@ fun Route.authRegister(citizenRepo: CitizenRepository) {
|
||||
call.respond(HttpStatusCode.BadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.component.auth.routes
|
||||
|
||||
import fr.dcproject.component.auth.PasswordlessAuth
|
||||
import fr.dcproject.component.auth.routes.PasswordlessRequest.Input
|
||||
import fr.dcproject.component.auth.routes.Sso.PasswordlessRequest.Input
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
@@ -10,20 +10,18 @@ import io.ktor.locations.post
|
||||
import io.ktor.request.receive
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/auth/passwordless")
|
||||
class PasswordlessRequest {
|
||||
object Sso {
|
||||
@Location("/auth/passwordless")
|
||||
class PasswordlessRequest {
|
||||
data class Input(val email: String, val url: String)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Send an email to the citizen with a link to automatically connect
|
||||
*/
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
fun Route.authPasswordless(passwordlessAuth: PasswordlessAuth) {
|
||||
fun Route.authPasswordless(passwordlessAuth: PasswordlessAuth) {
|
||||
post<PasswordlessRequest> {
|
||||
call.receive<Input>().run {
|
||||
try {
|
||||
@@ -34,4 +32,5 @@ fun Route.authPasswordless(passwordlessAuth: PasswordlessAuth) {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main/kotlin/component/auth/routes/install.kt
Normal file
19
src/main/kotlin/component/auth/routes/install.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
package fr.dcproject.component.auth.routes
|
||||
|
||||
import fr.dcproject.component.auth.routes.Login.authLogin
|
||||
import fr.dcproject.component.auth.routes.Register.authRegister
|
||||
import fr.dcproject.component.auth.routes.Sso.authPasswordless
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installAuthRoutes() {
|
||||
authenticate(optional = true) {
|
||||
authLogin(get())
|
||||
authRegister(get())
|
||||
authPasswordless(get())
|
||||
}
|
||||
}
|
||||
@@ -18,14 +18,13 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
|
||||
@Location("/citizens/{citizen}/password/change")
|
||||
class ChangePasswordCitizenRequest(val citizen: Citizen) {
|
||||
object ChangeMyPassword {
|
||||
@Location("/citizens/{citizen}/password/change")
|
||||
class ChangePasswordCitizenRequest(val citizen: Citizen) {
|
||||
data class Input(val oldPassword: String, val newPassword: String)
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.changeMyPassword(voter: CitizenVoter, userRepository: UserRepository) {
|
||||
fun Route.changeMyPassword(voter: CitizenVoter, userRepository: UserRepository) {
|
||||
put<ChangePasswordCitizenRequest> {
|
||||
voter.assert { canChangePassword(it.citizen, citizenOrNull) }
|
||||
try {
|
||||
@@ -44,4 +43,5 @@ fun Route.changeMyPassword(voter: CitizenVoter, userRepository: UserRepository)
|
||||
call.respond(HttpStatusCode.BadRequest, "Request format is not correct")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,24 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/citizens")
|
||||
class CitizensRequest(
|
||||
object FindCitizens {
|
||||
@Location("/citizens")
|
||||
class CitizensRequest(
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val sort: String? = null,
|
||||
val direction: RepositoryI.Direction? = null,
|
||||
val search: String? = null
|
||||
) {
|
||||
) {
|
||||
val page: Int = if (page < 1) 1 else page
|
||||
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.findCitizen(voter: CitizenVoter, repo: CitizenRepository) {
|
||||
fun Route.findCitizen(voter: CitizenVoter, repo: CitizenRepository) {
|
||||
get<CitizensRequest> {
|
||||
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
||||
voter.assert { canView(citizens.result, citizenOrNull) }
|
||||
call.respond(citizens)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/citizens/current")
|
||||
class CurrentCitizenRequest
|
||||
object GetCurrentCitizen {
|
||||
@Location("/citizens/current")
|
||||
class CurrentCitizenRequest
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getCurrentCitizen(voter: CitizenVoter) {
|
||||
fun Route.getCurrentCitizen(voter: CitizenVoter) {
|
||||
get<CurrentCitizenRequest> {
|
||||
val currentUser = citizenOrNull
|
||||
if (currentUser === null) {
|
||||
@@ -27,4 +27,5 @@ fun Route.getCurrentCitizen(voter: CitizenVoter) {
|
||||
call.respond(citizen)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/citizens/{citizen}")
|
||||
class CitizenRequest(val citizen: Citizen)
|
||||
object GetOneCitizen {
|
||||
@Location("/citizens/{citizen}")
|
||||
class CitizenRequest(val citizen: Citizen)
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getOneCitizen(voter: CitizenVoter) {
|
||||
fun Route.getOneCitizen(voter: CitizenVoter) {
|
||||
get<CitizenRequest> {
|
||||
voter.assert { canView(it.citizen, citizenOrNull) }
|
||||
|
||||
call.respond(it.citizen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
src/main/kotlin/component/citizen/routes/install.kt
Normal file
24
src/main/kotlin/component/citizen/routes/install.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package fr.dcproject.component.citizen.routes
|
||||
|
||||
import fr.dcproject.component.auth.routes.Login.authLogin
|
||||
import fr.dcproject.component.auth.routes.Register.authRegister
|
||||
import fr.dcproject.component.auth.routes.Sso.authPasswordless
|
||||
import fr.dcproject.component.citizen.routes.ChangeMyPassword.changeMyPassword
|
||||
import fr.dcproject.component.citizen.routes.FindCitizens.findCitizen
|
||||
import fr.dcproject.component.citizen.routes.GetCurrentCitizen.getCurrentCitizen
|
||||
import fr.dcproject.component.citizen.routes.GetOneCitizen.getOneCitizen
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installCitizenRoutes() {
|
||||
authenticate(optional = true) {
|
||||
findCitizen(get(), get())
|
||||
getOneCitizen(get())
|
||||
getCurrentCitizen(get())
|
||||
changeMyPassword(get(), get())
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,11 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/articles/{article}/comments")
|
||||
class PostArticleCommentRequest(
|
||||
object CreateCommentArticle {
|
||||
@Location("/articles/{article}/comments")
|
||||
class PostArticleCommentRequest(
|
||||
val article: ArticleForView
|
||||
) {
|
||||
) {
|
||||
class Comment(
|
||||
val content: String
|
||||
)
|
||||
@@ -33,10 +34,9 @@ class PostArticleCommentRequest(
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.createCommentArticle(repo: CommentArticleRepository, voter: CommentVoter) {
|
||||
fun Route.createCommentArticle(repo: CommentArticleRepository, voter: CommentVoter) {
|
||||
post<PostArticleCommentRequest> {
|
||||
it.getComment(call).let { comment ->
|
||||
voter.assert { canCreate(comment, citizenOrNull) }
|
||||
@@ -44,4 +44,5 @@ fun Route.createCommentArticle(repo: CommentArticleRepository, voter: CommentVot
|
||||
call.respond(HttpStatusCode.Created, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,21 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/articles/{article}/comments")
|
||||
class ArticleCommentsRequest(
|
||||
object GetArticleComments {
|
||||
@Location("/articles/{article}/comments")
|
||||
class ArticleCommentsRequest(
|
||||
val article: ArticleRef,
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val search: String? = null,
|
||||
sort: String = CommentArticleRepository.Sort.CREATED_AT.sql
|
||||
) {
|
||||
) {
|
||||
val page: Int = if (page < 1) 1 else page
|
||||
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
val sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.fromString(sort) ?: CommentArticleRepository.Sort.CREATED_AT
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getArticleComments(repo: CommentArticleRepository, voter: CommentVoter) {
|
||||
fun Route.getArticleComments(repo: CommentArticleRepository, voter: CommentVoter) {
|
||||
get<ArticleCommentsRequest> {
|
||||
val comment = repo.findByTarget(it.article, it.page, it.limit, it.sort)
|
||||
if (comment.result.isNotEmpty()) {
|
||||
@@ -36,4 +36,5 @@ fun Route.getArticleComments(repo: CommentArticleRepository, voter: CommentVoter
|
||||
}
|
||||
call.respond(HttpStatusCode.OK, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,16 @@ import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/citizens/{citizen}/comments/articles")
|
||||
class CitizenCommentArticleRequest(val citizen: Citizen)
|
||||
object GetCitizenArticleComments {
|
||||
@Location("/citizens/{citizen}/comments/articles")
|
||||
class CitizenCommentArticleRequest(val citizen: Citizen)
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, voter: CommentVoter) {
|
||||
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, voter: CommentVoter) {
|
||||
get<CitizenCommentArticleRequest> {
|
||||
repo.findByCitizen(it.citizen).let { comments ->
|
||||
voter.assert { canView(comments.result, citizenOrNull) }
|
||||
call.respond(comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
src/main/kotlin/component/comment/article/routes/install.kt
Normal file
18
src/main/kotlin/component/comment/article/routes/install.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package fr.dcproject.component.comment.article.routes
|
||||
|
||||
import fr.dcproject.component.comment.article.routes.CreateCommentArticle.createCommentArticle
|
||||
import fr.dcproject.component.comment.article.routes.GetArticleComments.getArticleComments
|
||||
import fr.dcproject.component.comment.article.routes.GetCitizenArticleComments.getCitizenArticleComments
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.routing.Routing
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installCommentArticleRoutes() {
|
||||
authenticate(optional = true) {
|
||||
getArticleComments(get(), get())
|
||||
createCommentArticle(get(), get())
|
||||
getCitizenArticleComments(get(), get())
|
||||
}
|
||||
}
|
||||
@@ -19,14 +19,13 @@ import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/comments/{comment}/children")
|
||||
class CreateCommentChildrenRequest(val comment: CommentRef) {
|
||||
object CreateCommentChildren {
|
||||
@Location("/comments/{comment}/children")
|
||||
class CreateCommentChildrenRequest(val comment: CommentRef) {
|
||||
class Input(val content: String)
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.createCommentChildren(repo: CommentRepository, voter: CommentVoter) {
|
||||
fun Route.createCommentChildren(repo: CommentRepository, voter: CommentVoter) {
|
||||
post<CreateCommentChildrenRequest> {
|
||||
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
||||
val newComment = CommentForUpdate(
|
||||
@@ -40,4 +39,5 @@ fun Route.createCommentChildren(repo: CommentRepository, voter: CommentVoter) {
|
||||
|
||||
call.respond(HttpStatusCode.Created, newComment)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,15 +14,13 @@ import io.ktor.locations.put
|
||||
import io.ktor.request.receiveText
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/comments/{comment}")
|
||||
class EditCommentRequest(val comment: CommentRef)
|
||||
object EditComment {
|
||||
@Location("/comments/{comment}")
|
||||
class EditCommentRequest(val comment: CommentRef)
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.editComment(repo: CommentRepository, voter: CommentVoter) {
|
||||
fun Route.editComment(repo: CommentRepository, voter: CommentVoter) {
|
||||
put<EditCommentRequest> {
|
||||
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
|
||||
voter.assert { canUpdate(comment, citizenOrNull) }
|
||||
@@ -32,4 +30,5 @@ fun Route.editComment(repo: CommentRepository, voter: CommentVoter) {
|
||||
|
||||
call.respond(HttpStatusCode.OK, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,19 @@ import io.ktor.util.KtorExperimentalAPI
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/comments/{comment}/children")
|
||||
class CommentChildrenRequest(
|
||||
object GetCommentChildren {
|
||||
@Location("/comments/{comment}/children")
|
||||
class CommentChildrenRequest(
|
||||
val comment: UUID,
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val search: String? = null
|
||||
) {
|
||||
) {
|
||||
val page: Int = if (page < 1) 1 else page
|
||||
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
}
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getChildrenComments(repo: CommentRepository, voter: CommentVoter) {
|
||||
fun Route.getChildrenComments(repo: CommentRepository, voter: CommentVoter) {
|
||||
get<CommentChildrenRequest> {
|
||||
val comments =
|
||||
repo.findByParent(
|
||||
@@ -41,4 +40,5 @@ fun Route.getChildrenComments(repo: CommentRepository, voter: CommentVoter) {
|
||||
|
||||
call.respond(HttpStatusCode.OK, comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,16 @@ import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@Location("/comments/{comment}")
|
||||
class CommentRequest(val comment: CommentRef)
|
||||
object GetOneComment {
|
||||
@Location("/comments/{comment}")
|
||||
class CommentRequest(val comment: CommentRef)
|
||||
|
||||
@KtorExperimentalAPI
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.getOneComment(repo: CommentRepository, voter: CommentVoter) {
|
||||
fun Route.getOneComment(repo: CommentRepository, voter: CommentVoter) {
|
||||
get<CommentRequest> {
|
||||
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment ${it.comment.id} not found")
|
||||
voter.assert { canView(comment, citizenOrNull) }
|
||||
|
||||
call.respond(HttpStatusCode.OK, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
src/main/kotlin/component/comment/generic/routes/install.kt
Normal file
20
src/main/kotlin/component/comment/generic/routes/install.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package fr.dcproject.component.comment.generic.routes
|
||||
|
||||
import fr.dcproject.component.comment.generic.routes.CreateCommentChildren.createCommentChildren
|
||||
import fr.dcproject.component.comment.generic.routes.EditComment.editComment
|
||||
import fr.dcproject.component.comment.generic.routes.GetCommentChildren.getChildrenComments
|
||||
import fr.dcproject.component.comment.generic.routes.GetOneComment.getOneComment
|
||||
import io.ktor.auth.authenticate
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.routing.Routing
|
||||
import org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installCommentRoutes() {
|
||||
authenticate(optional = true) {
|
||||
editComment(get(), get())
|
||||
getOneComment(get(), get())
|
||||
createCommentChildren(get(), get())
|
||||
getChildrenComments(get(), get())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user