Move all file in fr.dcproject.

This commit is contained in:
2021-02-11 01:37:29 +01:00
parent c85401aa86
commit 066b01e86f
148 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
package fr.dcproject.component.follow
import fr.dcproject.common.entity.ExtraI
import fr.dcproject.common.entity.HasTarget
import fr.dcproject.common.entity.TargetI
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI
import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedAtImp
import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.EntityCreatedByImp
import fr.postgresjson.entity.UuidEntityI
import java.util.UUID
@Deprecated("")
class Follow<T : TargetI>(
id: UUID = UUID.randomUUID(),
override val createdBy: CitizenBasic,
override var target: T
) : ExtraI<T, CitizenBasicI>,
FollowSimple<T, CitizenBasicI>(id, createdBy, target)
@Deprecated("")
open class FollowSimple<T : TargetI, C : CitizenI>(
id: UUID = UUID.randomUUID(),
override val createdBy: C,
override var target: T
) : ExtraI<T, C>,
FollowRef(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy<C> by EntityCreatedByImp(createdBy)
class FollowForUpdate<T : TargetI, C : CitizenI>(
id: UUID = UUID.randomUUID(),
override val target: T,
override val createdBy: C
) : FollowRef(id),
HasTarget<T>,
EntityCreatedBy<C> by EntityCreatedByImp<C>(createdBy)
open class FollowRef(
override val id: UUID
) : FollowI
interface FollowI : UuidEntityI

View File

@@ -0,0 +1,26 @@
package fr.dcproject.component.follow
import fr.dcproject.common.security.AccessControl
import fr.dcproject.common.security.AccessResponse
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.follow.Follow as FollowEntity
class FollowAccessControl : AccessControl() {
fun canCreate(subject: FollowI, citizen: CitizenI?): AccessResponse {
return if (citizen == null) denied("You must be connected to follow", "follow.create.notConnected")
else granted()
}
fun canDelete(subject: FollowI, citizen: CitizenI?): AccessResponse {
return if (citizen == null) denied("You must be connected to unfollow", "follow.delete.notConnected")
else granted()
}
fun <S : FollowEntity<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
canAll(subjects) { canView(it, citizen) }
fun canView(subject: FollowEntity<*>, citizen: CitizenI?): AccessResponse {
return if ((citizen != null && subject.createdBy.id == citizen.id) || !subject.createdBy.followAnonymous) granted()
else denied("You cannot view an anonymous follow", "follow.view.anonymous")
}
}

View File

@@ -0,0 +1,148 @@
package fr.dcproject.component.follow
import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.constitution.ConstitutionRef
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntity
import fr.postgresjson.repository.RepositoryI
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.util.UUID
import fr.dcproject.component.constitution.Constitution as ConstitutionEntity
import fr.dcproject.component.follow.Follow as FollowEntity
sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requester: Requester) : RepositoryI {
open fun findByCitizen(
citizen: CitizenI,
page: Int = 1,
limit: Int = 50
): Paginated<FollowEntity<OUT>> =
findByCitizen(citizen.id, page, limit)
open fun findByCitizen(
citizenId: UUID,
page: Int = 1,
limit: Int = 50
): Paginated<FollowEntity<OUT>> {
return requester
.getFunction("find_follows_by_citizen")
.select(
page,
limit,
"created_by_id" to citizenId
)
}
fun follow(follow: FollowForUpdate<IN, *>) {
requester
.getFunction("follow")
.sendQuery(
"reference" to follow.target.reference,
"target_id" to follow.target.id,
"created_by_id" to follow.createdBy.id
)
}
fun unfollow(follow: FollowForUpdate<IN, *>) {
requester
.getFunction("unfollow")
.sendQuery(
"reference" to follow.target.reference,
"target_id" to follow.target.id,
"created_by_id" to follow.createdBy.id
)
}
open fun findFollow(
citizen: CitizenI,
target: TargetRef
): FollowEntity<OUT>? =
requester
.getFunction("find_follow")
.selectOne(
"citizen_id" to citizen.id,
"target_id" to target.id,
"target_reference" to target.reference
)
fun findFollowsByTarget(
target: UuidEntity,
bulkSize: Int = 300
): Flow<FollowSimple<IN, CitizenRef>> = flow {
var nextPage = 1
do {
val paginate = findFollowsByTarget(target, nextPage, bulkSize)
paginate.result.forEach {
emit(it)
}
nextPage = paginate.currentPage + 1
} while (!paginate.isLastPage())
}
abstract fun findFollowsByTarget(
target: UuidEntity,
page: Int = 1,
limit: Int = 300
): Paginated<FollowSimple<IN, CitizenRef>>
}
class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRef, ArticleForView>(requester) {
override fun findByCitizen(
citizenId: UUID,
page: Int,
limit: Int
): Paginated<FollowEntity<ArticleForView>> {
return requester.run {
getFunction("find_follows_article_by_citizen")
.select(
page,
limit,
"created_by_id" to citizenId
)
}
}
override fun findFollowsByTarget(
target: UuidEntity,
page: Int,
limit: Int
): Paginated<FollowSimple<ArticleRef, CitizenRef>> {
return requester
.getFunction("find_follows_article_by_target")
.select(
page,
limit,
"target_id" to target.id
)
}
}
class FollowConstitutionRepository(requester: Requester) : FollowRepository<ConstitutionRef, ConstitutionEntity>(requester) {
override fun findByCitizen(
citizenId: UUID,
page: Int,
limit: Int
): Paginated<FollowEntity<ConstitutionEntity>> {
return requester.run {
getFunction("find_follows_constitution_by_citizen")
.select(
page,
limit,
"created_by_id" to citizenId
)
}
}
override fun findFollowsByTarget(
target: UuidEntity,
page: Int,
limit: Int
): Paginated<FollowSimple<ConstitutionRef, CitizenRef>> {
TODO("Not yet implemented")
}
}

View File

@@ -0,0 +1,9 @@
package fr.dcproject.component.follow
import org.koin.dsl.module
val followKoinModule = module {
single { FollowArticleRepository(get()) }
single { FollowConstitutionRepository(get()) }
single { FollowAccessControl() }
}

View File

@@ -0,0 +1,34 @@
package fr.dcproject.component.follow.routes.article
import fr.dcproject.common.security.assert
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowArticleRepository
import fr.dcproject.component.follow.FollowForUpdate
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 java.util.UUID
@KtorExperimentalLocationsAPI
object FollowArticle {
@Location("/articles/{article}/follows")
class ArticleFollowRequest(article: UUID) {
val article = ArticleRef(article)
}
fun Route.followArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
post<ArticleFollowRequest> {
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
ac.assert { canCreate(follow, citizenOrNull) }
repo.follow(follow)
call.respond(HttpStatusCode.Created)
}
}
}

View File

@@ -0,0 +1,33 @@
package fr.dcproject.component.follow.routes.article
import fr.dcproject.common.security.assert
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowArticleRepository
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
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 GetFollowArticle {
@Location("/articles/{article}/follows")
class ArticleFollowRequest(article: UUID) {
val article = ArticleRef(article)
}
fun Route.getFollowArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
get<ArticleFollowRequest> {
repo.findFollow(citizen, it.article)?.let { follow ->
ac.assert { canView(follow, citizenOrNull) }
call.respond(follow)
} ?: call.respond(HttpStatusCode.NoContent)
}
}
}

View File

@@ -0,0 +1,30 @@
package fr.dcproject.component.follow.routes.article
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowArticleRepository
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 GetMyFollowsArticle {
@Location("/citizens/{citizen}/follows/articles")
class CitizenFollowArticleRequest(citizen: UUID) {
val citizen = CitizenRef(citizen)
}
fun Route.getMyFollowsArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
get<CitizenFollowArticleRequest> {
val follows = repo.findByCitizen(it.citizen)
ac.assert { canView(follows.result, citizenOrNull) }
call.respond(follows)
}
}
}

View File

@@ -0,0 +1,34 @@
package fr.dcproject.component.follow.routes.article
import fr.dcproject.common.security.assert
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowArticleRepository
import fr.dcproject.component.follow.FollowForUpdate
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.delete
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object UnfollowArticle {
@Location("/articles/{article}/follows")
class ArticleFollowRequest(article: UUID) {
val article = ArticleRef(article)
}
fun Route.unfollowArticle(repo: FollowArticleRepository, ac: FollowAccessControl) {
delete<ArticleFollowRequest> {
val follow = FollowForUpdate(target = it.article, createdBy = this.citizen)
ac.assert { canDelete(follow, citizenOrNull) }
repo.unfollow(follow)
call.respond(HttpStatusCode.NoContent)
}
}
}

View File

@@ -0,0 +1,20 @@
package fr.dcproject.component.follow.routes.article
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
import fr.dcproject.component.follow.routes.article.UnfollowArticle.unfollowArticle
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installFollowArticleRoutes() {
authenticate(optional = true) {
followArticle(get(), get())
unfollowArticle(get(), get())
getFollowArticle(get(), get())
getMyFollowsArticle(get(), get())
}
}

View File

@@ -0,0 +1,34 @@
package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository
import fr.dcproject.component.follow.FollowForUpdate
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 java.util.UUID
@KtorExperimentalLocationsAPI
object FollowConstitution {
@Location("/constitutions/{constitution}/follows")
class ConstitutionFollowRequest(constitution: UUID) {
val constitution = ConstitutionRef(constitution)
}
fun Route.followConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
post<ConstitutionFollowRequest> {
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
ac.assert { canCreate(follow, citizenOrNull) }
repo.follow(follow)
call.respond(HttpStatusCode.Created)
}
}
}

View File

@@ -0,0 +1,33 @@
package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
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 GetFollowConstitution {
@Location("/constitutions/{constitution}/follows")
class ConstitutionFollowRequest(constitution: UUID) {
val constitution = ConstitutionRef(constitution)
}
fun Route.getFollowConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
get<ConstitutionFollowRequest> {
repo.findFollow(citizen, it.constitution)?.let { follow ->
ac.assert { canView(follow, citizenOrNull) }
call.respond(follow)
} ?: call.respond(HttpStatusCode.NotFound)
}
}
}

View File

@@ -0,0 +1,30 @@
package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository
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 GetMyFollowsConstitution {
@Location("/citizens/{citizen}/follows/constitutions")
class CitizenFollowConstitutionRequest(citizen: UUID) {
val citizen = CitizenRef(citizen)
}
fun Route.getMyFollowsConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
get<CitizenFollowConstitutionRequest> {
val follows = repo.findByCitizen(it.citizen)
ac.assert { canView(follows.result, citizenOrNull) }
call.respond(follows)
}
}
}

View File

@@ -0,0 +1,34 @@
package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository
import fr.dcproject.component.follow.FollowForUpdate
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.delete
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object UnfollowConstitution {
@Location("/constitutions/{constitution}/follows")
class ConstitutionUnfollowRequest(constitution: UUID) {
val constitution = ConstitutionRef(constitution)
}
fun Route.unfollowConstitution(repo: FollowConstitutionRepository, ac: FollowAccessControl) {
delete<ConstitutionUnfollowRequest> {
val follow = FollowForUpdate(target = it.constitution, createdBy = this.citizen)
ac.assert { canDelete(follow, citizenOrNull) }
repo.unfollow(follow)
call.respond(HttpStatusCode.NoContent)
}
}
}

View File

@@ -0,0 +1,20 @@
package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.component.follow.routes.constitution.FollowConstitution.followConstitution
import fr.dcproject.component.follow.routes.constitution.GetFollowConstitution.getFollowConstitution
import fr.dcproject.component.follow.routes.constitution.GetMyFollowsConstitution.getMyFollowsConstitution
import fr.dcproject.component.follow.routes.constitution.UnfollowConstitution.unfollowConstitution
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installFollowConstitutionRoutes() {
authenticate(optional = true) {
followConstitution(get(), get())
unfollowConstitution(get(), get())
getFollowConstitution(get(), get())
getMyFollowsConstitution(get(), get())
}
}