Move all file in fr.dcproject.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package fr.dcproject.component.article.routes
|
||||
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.article.ArticleAccessControl
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
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
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object FindArticleVersions {
|
||||
@Location("/articles/{article}/versions")
|
||||
class ArticleVersionsRequest(
|
||||
article: UUID,
|
||||
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
|
||||
val article = ArticleRef(article)
|
||||
}
|
||||
|
||||
private fun ArticleRepository.findVersions(request: ArticleVersionsRequest) =
|
||||
findVersionsById(request.page, request.limit, request.article.id)
|
||||
|
||||
fun Route.findArticleVersions(repo: ArticleRepository, ac: ArticleAccessControl) {
|
||||
get<ArticleVersionsRequest> {
|
||||
repo.findVersions(it)
|
||||
.apply { ac.assert { canView(result, citizenOrNull) } }
|
||||
.let { call.respond(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package fr.dcproject.component.article.routes
|
||||
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.article.ArticleAccessControl
|
||||
import fr.dcproject.component.article.ArticleForListing
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.routes.PaginatedRequest
|
||||
import fr.dcproject.routes.PaginatedRequestI
|
||||
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
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object FindArticles {
|
||||
@Location("/articles")
|
||||
class ArticlesRequest(
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val sort: String? = null,
|
||||
val direction: RepositoryI.Direction? = null,
|
||||
val search: String? = null,
|
||||
val createdBy: String? = null,
|
||||
val workgroup: String? = null
|
||||
) : PaginatedRequestI by PaginatedRequest(page, limit)
|
||||
|
||||
private fun ArticleRepository.findArticles(request: ArticlesRequest): Paginated<ArticleForListing> {
|
||||
return find(
|
||||
request.page,
|
||||
request.limit,
|
||||
request.sort,
|
||||
request.direction,
|
||||
request.search,
|
||||
ArticleRepository.Filter(createdById = request.createdBy, workgroupId = request.workgroup)
|
||||
)
|
||||
}
|
||||
|
||||
fun Route.findArticles(repo: ArticleRepository, ac: ArticleAccessControl) {
|
||||
get<ArticlesRequest> {
|
||||
repo.findArticles(it)
|
||||
.apply { ac.assert { canView(result, citizenOrNull) } }
|
||||
.let { call.respond(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package fr.dcproject.component.article.routes
|
||||
|
||||
import fr.dcproject.common.dto.CreatedAt
|
||||
import fr.dcproject.common.dto.Versionable
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.component.article.ArticleAccessControl
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.article.ArticleRef
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
import fr.dcproject.component.article.ArticleViewManager
|
||||
import fr.dcproject.component.auth.citizenOrNull
|
||||
import fr.dcproject.component.opinion.dto.Opinionable
|
||||
import fr.dcproject.component.views.dto.Viewable
|
||||
import fr.dcproject.component.views.entity.ViewAggregation
|
||||
import fr.dcproject.component.vote.dto.Votable
|
||||
import io.ktor.application.call
|
||||
import io.ktor.features.NotFoundException
|
||||
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
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object GetOneArticle {
|
||||
@Location("/articles/{article}")
|
||||
class ArticleRequest(article: UUID) {
|
||||
val article = ArticleRef(article)
|
||||
}
|
||||
|
||||
class Output(
|
||||
article: ArticleForView,
|
||||
views: ViewAggregation = ViewAggregation()
|
||||
) : CreatedAt by CreatedAt.Imp(article),
|
||||
Opinionable by Opinionable.Imp(article),
|
||||
Votable by Votable.Imp(article),
|
||||
Versionable by Versionable.Imp(article),
|
||||
Viewable by Viewable.Imp(views) {
|
||||
val id = article.id
|
||||
val title = article.title
|
||||
val anonymous = article.anonymous
|
||||
val content = article.content
|
||||
val description = article.description
|
||||
val tags = article.tags
|
||||
val draft = article.draft
|
||||
val lastVersion = article.lastVersion
|
||||
val createdBy = article.createdBy
|
||||
val workgroup = article.workgroup // TODO change to workgroup DTO
|
||||
}
|
||||
|
||||
fun Route.getOneArticle(viewManager: ArticleViewManager<ArticleForView>, ac: ArticleAccessControl, repo: ArticleRepository) {
|
||||
get<ArticleRequest> {
|
||||
val article: ArticleForView = repo.findById(it.article.id) ?: throw NotFoundException("Article ${it.article.id} not found")
|
||||
ac.assert { canView(article, citizenOrNull) }
|
||||
|
||||
Output(
|
||||
article,
|
||||
viewManager.getViewsCount(article)
|
||||
).also { out ->
|
||||
call.respond(out)
|
||||
}
|
||||
|
||||
launch {
|
||||
viewManager.addView(call.request.local.remoteHost, article, citizenOrNull)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package fr.dcproject.component.article.routes
|
||||
|
||||
import fr.dcproject.common.security.assert
|
||||
import fr.dcproject.common.utils.receiveOrBadRequest
|
||||
import fr.dcproject.component.article.ArticleAccessControl
|
||||
import fr.dcproject.component.article.ArticleForUpdate
|
||||
import fr.dcproject.component.article.ArticleForView
|
||||
import fr.dcproject.component.article.ArticleRepository
|
||||
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.notification.ArticleUpdateNotification
|
||||
import fr.dcproject.component.notification.Publisher
|
||||
import fr.dcproject.component.workgroup.WorkgroupRef
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
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 java.util.UUID
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object UpsertArticle {
|
||||
@Location("/articles")
|
||||
class UpsertArticleRequest {
|
||||
class Input(
|
||||
val id: UUID?,
|
||||
val title: String,
|
||||
val anonymous: Boolean = true,
|
||||
val content: String,
|
||||
val description: String,
|
||||
val tags: List<String> = emptyList(),
|
||||
val draft: Boolean = false,
|
||||
val versionId: UUID,
|
||||
val workgroup: WorkgroupRef? = null,
|
||||
)
|
||||
}
|
||||
|
||||
fun Route.upsertArticle(repo: ArticleRepository, publisher: Publisher, ac: ArticleAccessControl) {
|
||||
suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receiveOrBadRequest<Input>().run {
|
||||
ArticleForUpdate(
|
||||
id = id ?: UUID.randomUUID(),
|
||||
title = title,
|
||||
anonymous = anonymous,
|
||||
content = content,
|
||||
description = description,
|
||||
tags = tags,
|
||||
draft = draft,
|
||||
createdBy = citizen,
|
||||
workgroup = workgroup,
|
||||
versionId = versionId
|
||||
)
|
||||
}
|
||||
|
||||
post<UpsertArticleRequest> {
|
||||
val article = call.convertRequestToEntity()
|
||||
ac.assert { canUpsert(article, citizenOrNull) }
|
||||
val newArticle: ArticleForView = repo.upsert(article) ?: error("Article not updated")
|
||||
call.respond(newArticle)
|
||||
publisher.publish(ArticleUpdateNotification(newArticle))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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 org.koin.ktor.ext.get
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Routing.installArticleRoutes() {
|
||||
authenticate(optional = true) {
|
||||
findArticles(get(), get())
|
||||
findArticleVersions(get(), get())
|
||||
getOneArticle(get(), get(), get())
|
||||
upsertArticle(get(), get(), get())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user