Big refactoring #77

Merged
flecomte merged 166 commits from refactoring-component-and-immutable into master 2021-03-24 19:06:07 +01:00
11 changed files with 78 additions and 53 deletions
Showing only changes of commit 8d93fc8b3c - Show all commits

5
.idea/gradle.xml generated
View File

@@ -4,8 +4,8 @@
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="delegatedBuild" value="false" /> <option name="delegatedBuild" value="true" />
<option name="testRunner" value="PLATFORM" /> <option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules"> <option name="modules">
@@ -13,7 +13,6 @@
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
</set> </set>
</option> </option>
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>

View File

@@ -164,6 +164,14 @@ tasks.withType<Jar> {
} }
} }
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "11"
sourceCompatibility = "11"
targetCompatibility = "11"
}
}
tasks.named<ShadowJar>("shadowJar") { tasks.named<ShadowJar>("shadowJar") {
mergeServiceFiles("META-INF/services") mergeServiceFiles("META-INF/services")
archiveFileName.set("${archiveBaseName.get()}-latest-all.${archiveExtension.get()}") archiveFileName.set("${archiveBaseName.get()}-latest-all.${archiveExtension.get()}")

View File

@@ -27,6 +27,15 @@ open class User(
CreatedAt by CreatedAt.Imp(), CreatedAt by CreatedAt.Imp(),
UpdatedAt by UpdatedAt.Imp() UpdatedAt by UpdatedAt.Imp()
class UserCreator(
id: UUID = UUID.randomUUID(),
override val username: String,
) : UserRef(id), UserWithUsername
interface UserWithUsername {
val username: String
}
interface UserWithPasswordI { interface UserWithPasswordI {
val id: UUID val id: UUID
val password: String val password: String

View File

@@ -5,6 +5,7 @@ import fr.dcproject.common.entity.DeletedAt
import fr.dcproject.common.entity.Entity import fr.dcproject.common.entity.Entity
import fr.dcproject.common.entity.EntityI import fr.dcproject.common.entity.EntityI
import fr.dcproject.component.auth.User import fr.dcproject.component.auth.User
import fr.dcproject.component.auth.UserCreator
import fr.dcproject.component.auth.UserForCreate import fr.dcproject.component.auth.UserForCreate
import fr.dcproject.component.auth.UserI import fr.dcproject.component.auth.UserI
import fr.dcproject.component.auth.UserRef import fr.dcproject.component.auth.UserRef
@@ -50,6 +51,28 @@ class Citizen(
) )
} }
data class CitizenCreator(
override var id: UUID = UUID.randomUUID(),
override var name: Name,
override var email: String,
override var voteAnonymous: Boolean = true,
override var followAnonymous: Boolean = true,
override val user: UserCreator,
override val deletedAt: DateTime? = null
) : CitizenCreatorI,
CitizenRefWithUser(id, user),
DeletedAt by DeletedAt.Imp(deletedAt)
interface CitizenCreatorI : CitizenWithUserI, CitizenWithEmail, CitizenCartI, DeletedAt {
override val id: UUID
override val name: Name
override val email: String
val voteAnonymous: Boolean
val followAnonymous: Boolean
override val user: UserCreator
override val deletedAt: DateTime?
}
@Deprecated("") @Deprecated("")
data class CitizenBasic( data class CitizenBasic(
override var id: UUID = UUID.randomUUID(), override var id: UUID = UUID.randomUUID(),

View File

@@ -6,27 +6,18 @@ import fr.dcproject.common.entity.EntityI
import fr.dcproject.common.entity.ExtraI import fr.dcproject.common.entity.ExtraI
import fr.dcproject.common.entity.HasTarget import fr.dcproject.common.entity.HasTarget
import fr.dcproject.common.entity.TargetI import fr.dcproject.common.entity.TargetI
import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenCreator
import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import java.util.UUID import java.util.UUID
@Deprecated("") open class FollowForView<T : TargetI>(
class Follow<T : TargetI>(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
override val createdBy: CitizenBasic, override val createdBy: CitizenCreator,
override var target: T override var target: T
) : ExtraI<T, CitizenBasicI>, ) : ExtraI<T, CitizenRef>,
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), FollowRef(id),
Created<C> by Created.Imp(createdBy) Created<CitizenRef> by Created.Imp(createdBy)
class FollowForUpdate<T : TargetI, C : CitizenI>( class FollowForUpdate<T : TargetI, C : CitizenI>(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),

View File

@@ -3,7 +3,6 @@ package fr.dcproject.component.follow
import fr.dcproject.common.security.AccessControl import fr.dcproject.common.security.AccessControl
import fr.dcproject.common.security.AccessResponse import fr.dcproject.common.security.AccessResponse
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.follow.Follow as FollowEntity
class FollowAccessControl : AccessControl() { class FollowAccessControl : AccessControl() {
fun canCreate(subject: FollowI, citizen: CitizenI?): AccessResponse { fun canCreate(subject: FollowI, citizen: CitizenI?): AccessResponse {
@@ -16,10 +15,10 @@ class FollowAccessControl : AccessControl() {
else granted() else granted()
} }
fun <S : FollowEntity<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse = fun <S : FollowForView<*>> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse =
canAll(subjects) { canView(it, citizen) } canAll(subjects) { canView(it, citizen) }
fun canView(subject: FollowEntity<*>, citizen: CitizenI?): AccessResponse { fun canView(subject: FollowForView<*>, citizen: CitizenI?): AccessResponse {
return if ((citizen != null && subject.createdBy.id == citizen.id) || !subject.createdBy.followAnonymous) granted() return if ((citizen != null && subject.createdBy.id == citizen.id) || !subject.createdBy.followAnonymous) granted()
else denied("You cannot view an anonymous follow", "follow.view.anonymous") else denied("You cannot view an anonymous follow", "follow.view.anonymous")
} }

View File

@@ -5,7 +5,6 @@ import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.constitution.ConstitutionRef import fr.dcproject.component.constitution.ConstitutionRef
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
@@ -14,21 +13,20 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import java.util.UUID import java.util.UUID
import fr.dcproject.component.constitution.Constitution as ConstitutionEntity 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 { sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requester: Requester) : RepositoryI {
open fun findByCitizen( open fun findByCitizen(
citizen: CitizenI, citizen: CitizenI,
page: Int = 1, page: Int = 1,
limit: Int = 50 limit: Int = 50
): Paginated<FollowEntity<OUT>> = ): Paginated<FollowForView<OUT>> =
findByCitizen(citizen.id, page, limit) findByCitizen(citizen.id, page, limit)
open fun findByCitizen( open fun findByCitizen(
citizenId: UUID, citizenId: UUID,
page: Int = 1, page: Int = 1,
limit: Int = 50 limit: Int = 50
): Paginated<FollowEntity<OUT>> { ): Paginated<FollowForView<OUT>> {
return requester return requester
.getFunction("find_follows_by_citizen") .getFunction("find_follows_by_citizen")
.select( .select(
@@ -61,7 +59,7 @@ sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requ
open fun findFollow( open fun findFollow(
citizen: CitizenI, citizen: CitizenI,
target: TargetRef target: TargetRef
): FollowEntity<OUT>? = ): FollowForView<OUT>? =
requester requester
.getFunction("find_follow") .getFunction("find_follow")
.selectOne( .selectOne(
@@ -73,7 +71,7 @@ sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requ
fun findFollowsByTarget( fun findFollowsByTarget(
target: Entity, target: Entity,
bulkSize: Int = 300 bulkSize: Int = 300
): Flow<FollowSimple<IN, CitizenRef>> = flow { ): Flow<FollowForView<IN>> = flow {
var nextPage = 1 var nextPage = 1
do { do {
val paginate = findFollowsByTarget(target, nextPage, bulkSize) val paginate = findFollowsByTarget(target, nextPage, bulkSize)
@@ -88,7 +86,7 @@ sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requ
target: Entity, target: Entity,
page: Int = 1, page: Int = 1,
limit: Int = 300 limit: Int = 300
): Paginated<FollowSimple<IN, CitizenRef>> ): Paginated<FollowForView<IN>>
} }
class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRef, ArticleForView>(requester) { class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRef, ArticleForView>(requester) {
@@ -96,7 +94,7 @@ class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRe
citizenId: UUID, citizenId: UUID,
page: Int, page: Int,
limit: Int limit: Int
): Paginated<FollowEntity<ArticleForView>> { ): Paginated<FollowForView<ArticleForView>> {
return requester.run { return requester.run {
getFunction("find_follows_article_by_citizen") getFunction("find_follows_article_by_citizen")
.select( .select(
@@ -111,7 +109,7 @@ class FollowArticleRepository(requester: Requester) : FollowRepository<ArticleRe
target: Entity, target: Entity,
page: Int, page: Int,
limit: Int limit: Int
): Paginated<FollowSimple<ArticleRef, CitizenRef>> { ): Paginated<FollowForView<ArticleRef>> {
return requester return requester
.getFunction("find_follows_article_by_target") .getFunction("find_follows_article_by_target")
.select( .select(
@@ -127,7 +125,7 @@ class FollowConstitutionRepository(requester: Requester) : FollowRepository<Cons
citizenId: UUID, citizenId: UUID,
page: Int, page: Int,
limit: Int limit: Int
): Paginated<FollowEntity<ConstitutionEntity>> { ): Paginated<FollowForView<ConstitutionEntity>> {
return requester.run { return requester.run {
getFunction("find_follows_constitution_by_citizen") getFunction("find_follows_constitution_by_citizen")
.select( .select(
@@ -142,7 +140,7 @@ class FollowConstitutionRepository(requester: Requester) : FollowRepository<Cons
target: Entity, target: Entity,
page: Int, page: Int,
limit: Int limit: Int
): Paginated<FollowSimple<ConstitutionRef, CitizenRef>> { ): Paginated<FollowForView<ConstitutionRef>> {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
} }

View File

@@ -7,10 +7,9 @@ import com.rabbitmq.client.Consumer
import com.rabbitmq.client.DefaultConsumer import com.rabbitmq.client.DefaultConsumer
import com.rabbitmq.client.Envelope import com.rabbitmq.client.Envelope
import fr.dcproject.common.entity.TargetRef import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.follow.FollowArticleRepository import fr.dcproject.component.follow.FollowArticleRepository
import fr.dcproject.component.follow.FollowConstitutionRepository import fr.dcproject.component.follow.FollowConstitutionRepository
import fr.dcproject.component.follow.FollowSimple import fr.dcproject.component.follow.FollowForView
import io.ktor.utils.io.errors.IOException import io.ktor.utils.io.errors.IOException
import io.lettuce.core.RedisClient import io.lettuce.core.RedisClient
import io.lettuce.core.api.async.RedisAsyncCommands import io.lettuce.core.api.async.RedisAsyncCommands
@@ -108,6 +107,6 @@ class NotificationConsumer(
private class DecodedMessage( private class DecodedMessage(
val event: EntityNotification, val event: EntityNotification,
val rawMessage: String, val rawMessage: String,
val follow: FollowSimple<out TargetRef, CitizenRef> val follow: FollowForView<out TargetRef>
) )
} }

View File

@@ -9,9 +9,8 @@ import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.component.article.ArticleWithTitleI import fr.dcproject.component.article.ArticleWithTitleI
import fr.dcproject.component.citizen.CitizenBasicI import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.follow.FollowSimple import fr.dcproject.component.follow.FollowForView
import java.util.UUID import java.util.UUID
class NotificationEmailSender( class NotificationEmailSender(
@@ -20,7 +19,7 @@ class NotificationEmailSender(
private val citizenRepo: CitizenRepository, private val citizenRepo: CitizenRepository,
private val articleRepo: ArticleRepository private val articleRepo: ArticleRepository
) { ) {
fun sendEmail(follow: FollowSimple<out TargetRef, CitizenRef>) { fun sendEmail(follow: FollowForView<out TargetRef>) {
val citizen = citizenRepo.findById(follow.createdBy.id) ?: noCitizen(follow.createdBy.id) val citizen = citizenRepo.findById(follow.createdBy.id) ?: noCitizen(follow.createdBy.id)
val target = when (follow.target.reference) { val target = when (follow.target.reference) {
"article" -> "article" ->

View File

@@ -5,9 +5,12 @@ import com.rabbitmq.client.ShutdownSignalException
import fr.dcproject.application.Configuration import fr.dcproject.application.Configuration
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.UserCreator
import fr.dcproject.component.citizen.CitizenCreator
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.follow.FollowArticleRepository import fr.dcproject.component.follow.FollowArticleRepository
import fr.dcproject.component.follow.FollowSimple import fr.dcproject.component.follow.FollowForView
import fr.dcproject.component.notification.ArticleUpdateNotification import fr.dcproject.component.notification.ArticleUpdateNotification
import fr.dcproject.component.notification.NotificationConsumer import fr.dcproject.component.notification.NotificationConsumer
import fr.dcproject.component.notification.NotificationEmailSender import fr.dcproject.component.notification.NotificationEmailSender
@@ -78,8 +81,8 @@ class NotificationConsumerTest {
val rabbitFactory: ConnectionFactory = ConnectionFactory().apply { setUri(config.rabbitmq) } val rabbitFactory: ConnectionFactory = ConnectionFactory().apply { setUri(config.rabbitmq) }
val followArticleRepo = mockk<FollowArticleRepository> { val followArticleRepo = mockk<FollowArticleRepository> {
every { findFollowsByTarget(any()) } returns flow { every { findFollowsByTarget(any()) } returns flow {
FollowSimple( FollowForView(
createdBy = CitizenRef(), createdBy = CitizenCreator(name = CitizenI.Name("John", "Doe"), email = "john.doe@dc-project.com", user = UserCreator(username = "john-doe")),
target = ArticleRef(), target = ArticleRef(),
).let { emit(it) } ).let { emit(it) }
} }

View File

@@ -4,13 +4,14 @@ import fr.dcproject.common.security.AccessDecision.DENIED
import fr.dcproject.common.security.AccessDecision.GRANTED import fr.dcproject.common.security.AccessDecision.GRANTED
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.auth.User import fr.dcproject.component.auth.User
import fr.dcproject.component.auth.UserCreator
import fr.dcproject.component.auth.UserI import fr.dcproject.component.auth.UserI
import fr.dcproject.component.citizen.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenCreator
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.follow.Follow
import fr.dcproject.component.follow.FollowAccessControl import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowForView
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.joda.time.DateTime import org.joda.time.DateTime
import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tag
@@ -25,12 +26,10 @@ import java.util.UUID
@Execution(CONCURRENT) @Execution(CONCURRENT)
@Tags(Tag("security"), Tag("unit")) @Tags(Tag("security"), Tag("unit"))
internal class `Follow Access Control` { internal class `Follow Access Control` {
private val tesla = CitizenBasic( private val tesla = CitizenCreator(
user = User( user = UserCreator(
username = "nicolas-tesla", username = "nicolas-tesla",
roles = listOf(UserI.Roles.ROLE_USER)
), ),
birthday = DateTime.now(),
email = "tesla@best.com", email = "tesla@best.com",
name = CitizenI.Name("Nicolas", "Tesla"), name = CitizenI.Name("Nicolas", "Tesla"),
followAnonymous = false followAnonymous = false
@@ -46,13 +45,11 @@ internal class `Follow Access Control` {
followAnonymous = false followAnonymous = false
) )
private val einstein = CitizenBasic( private val einstein = CitizenCreator(
id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"), id = UUID.fromString("319f1226-8f47-4df3-babd-2c7671ad0fbc"),
user = User( user = UserCreator(
username = "albert-einstein", username = "albert-einstein",
roles = listOf(UserI.Roles.ROLE_USER)
), ),
birthday = DateTime.now(),
email = "einstein@best.com", email = "einstein@best.com",
name = CitizenI.Name("Albert", "Einstein"), name = CitizenI.Name("Albert", "Einstein"),
followAnonymous = true followAnonymous = true
@@ -86,12 +83,12 @@ internal class `Follow Access Control` {
title = "Super article" title = "Super article"
) )
private val follow1 = Follow( private val follow1 = FollowForView(
createdBy = tesla, createdBy = tesla,
target = article1 target = article1
) )
private val followAnon = Follow( private val followAnon = FollowForView(
createdBy = einstein, createdBy = einstein,
target = article1 target = article1
) )