Split Entities for remove nullable variables

This commit is contained in:
2020-01-28 11:08:43 +01:00
parent 3cdd1f3a46
commit 813d6857e9
52 changed files with 569 additions and 287 deletions

View File

@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Tests" type="JUnit" factoryName="JUnit" show_console_on_std_err="true"> <configuration default="false" name="All Tests + Lint" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
@@ -19,6 +19,7 @@
<dir value="$PROJECT_DIR$" /> <dir value="$PROJECT_DIR$" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Lint" run_configuration_type="GradleRunConfiguration" />
</method> </method>
</configuration> </configuration>
</component> </component>

View File

@@ -1,10 +1,10 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Cucumber Tests" type="JUnit" factoryName="JUnit"> <configuration default="false" name="Cucumber Tests" type="JUnit" factoryName="JUnit">
<output_file path="$PROJECT_DIR$/var/log/test/cucumber.out.log" is_save="true" /> <output_file path="$PROJECT_DIR$/var/log/test/cucumber.out.log" />
<module name="dcproject.test" /> <module name="dcproject.test" />
<option name="PACKAGE_NAME" value="" /> <option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" /> <option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
<option name="METHOD_NAME" value="testArticle" /> <option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" /> <option name="TEST_OBJECT" value="class" />
<option name="PARAMETERS" value="" /> <option name="PARAMETERS" value="" />
<envs> <envs>

31
.idea/runConfigurations/Lint.xml generated Normal file
View File

@@ -0,0 +1,31 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Lint" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="ktlintCheck" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<extension name="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />
<option name="IS_SUBST" value="false" />
<option name="IS_PATH_MACRO_SUPPORTED" value="false" />
<option name="IS_IGNORE_MISSING_FILES" value="false" />
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
<ENTRIES>
<ENTRY IS_ENABLED="true" PARSER="runconfig" />
</ENTRIES>
</extension>
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2" />
</configuration>
</component>

View File

@@ -8,10 +8,7 @@ import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.joda.JodaModule import com.fasterxml.jackson.datatype.joda.JodaModule
import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
import fr.dcproject.Env.PROD import fr.dcproject.Env.PROD
import fr.dcproject.entity.Article import fr.dcproject.entity.*
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.Constitution
import fr.dcproject.entity.User
import fr.dcproject.routes.* import fr.dcproject.routes.*
import fr.dcproject.security.voter.* import fr.dcproject.security.voter.*
import fr.postgresjson.migration.Migrations import fr.postgresjson.migration.Migrations
@@ -80,9 +77,16 @@ fun Application.module(env: Env = PROD) {
// TODO: create generic convert for entityI // TODO: create generic convert for entityI
convert<Article> { convert<Article> {
decode { values, _ -> decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) } values.singleOrNull()?.let {
?: throw InternalError("Cannot convert $values to UUID") get<RepositoryArticle>().findById(UUID.fromString(it)) ?: throw InternalError("Article $values not found")
get<RepositoryArticle>().findById(id) ?: throw InternalError("Article $values not found") } ?: throw NotFoundException("Article $values not found")
}
}
convert<ArticleRef> {
decode { values, _ ->
values.singleOrNull()?.let {
ArticleRef(UUID.fromString(it))
} ?: throw NotFoundException("Article $values not found")
} }
} }
@@ -90,7 +94,7 @@ fun Application.module(env: Env = PROD) {
decode { values, _ -> decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) } val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID") ?: throw InternalError("Cannot convert $values to UUID")
get<RepositoryConstitution>().findById(id) ?: throw InternalError("Constitution $values not found") get<RepositoryConstitution>().findById(id) ?: throw NotFoundException("Constitution $values not found")
} }
} }
@@ -98,7 +102,7 @@ fun Application.module(env: Env = PROD) {
decode { values, _ -> decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) } val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID") ?: throw InternalError("Cannot convert $values to UUID")
get<RepositoryCitizen>().findById(id, true) ?: throw InternalError("Citizen $values not found") get<RepositoryCitizen>().findById(id, true) ?: throw NotFoundException("Citizen $values not found")
} }
} }
} }
@@ -179,6 +183,9 @@ fun Application.module(env: Env = PROD) {
throw e throw e
} }
} }
exception<NotFoundException> { e ->
call.respond(HttpStatusCode.BadRequest, e.message!!)
}
} }
install(CORS) { install(CORS) {

View File

@@ -1,5 +1,6 @@
package fr.dcproject package fr.dcproject
import fr.dcproject.entity.UserI
import fr.dcproject.security.voter.ForbiddenException import fr.dcproject.security.voter.ForbiddenException
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.auth.authentication import io.ktor.auth.authentication
@@ -8,7 +9,6 @@ import io.ktor.util.pipeline.PipelineContext
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.koin.core.context.GlobalContext import org.koin.core.context.GlobalContext
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.entity.User as UserEntity
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
private val citizenAttributeKey = AttributeKey<CitizenEntity>("CitizenContext") private val citizenAttributeKey = AttributeKey<CitizenEntity>("CitizenContext")
@@ -16,7 +16,7 @@ private val citizenAttributeKey = AttributeKey<CitizenEntity>("CitizenContext")
val ApplicationCall.citizen: CitizenEntity val ApplicationCall.citizen: CitizenEntity
get() = attributes.computeIfAbsent(citizenAttributeKey) { get() = attributes.computeIfAbsent(citizenAttributeKey) {
runBlocking { runBlocking {
val user = authentication.principal<UserEntity>() ?: throw ForbiddenException() val user = authentication.principal<UserI>() ?: throw ForbiddenException()
GlobalContext.get().koin.get<CitizenRepository>().findByUser(user) ?: throw ForbiddenException("Citizen not found for this user id \"${user.id}\"") GlobalContext.get().koin.get<CitizenRepository>().findByUser(user) ?: throw ForbiddenException("Citizen not found for this user id \"${user.id}\"")
} }
} }

View File

@@ -4,7 +4,7 @@ import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm import com.auth0.jwt.algorithms.Algorithm
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import fr.dcproject.entity.User import fr.dcproject.entity.UserI
import org.eclipse.jetty.util.resource.JarResource import org.eclipse.jetty.util.resource.JarResource
import java.io.File import java.io.File
import java.util.* import java.util.*
@@ -46,7 +46,7 @@ object JwtConfig {
/** /**
* Produce a token for this combination of User and Account * Produce a token for this combination of User and Account
*/ */
fun makeToken(user: User): String = JWT.create() fun makeToken(user: UserI): String = JWT.create()
.withSubject("Authentication") .withSubject("Authentication")
.withIssuer(issuer) .withIssuer(issuer)
.withClaim("id", user.id.toString()) .withClaim("id", user.id.toString())

View File

@@ -1,26 +1,77 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.* import fr.postgresjson.entity.immutable.*
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import fr.postgresjson.entity.mutable.EntityVersioning
import fr.postgresjson.entity.mutable.UuidEntityVersioning
import java.util.* import java.util.*
class Article( class Article(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
var title: String?, title: String,
var anonymous: Boolean? = true, anonymous: Boolean = true,
var content: String?, content: String,
var description: String?, description: String,
var tags: List<String> = emptyList(), tags: List<String> = emptyList(),
var draft: Boolean = false, override var draft: Boolean = false,
var lastVersion: Boolean = false, override var lastVersion: Boolean = false,
createdBy: Citizen? createdBy: CitizenBasic
) : ) : ArticleFull,
UuidEntity(id), ArticleBasic(id, title, anonymous, content, description, tags, createdBy)
EntityVersioning<UUID, Int> by UuidEntityVersioning(),
EntityCreatedAt by EntityCreatedAtImp(), open class ArticleBasic(
EntityCreatedBy<Citizen> by EntityCreatedByImp(createdBy), id: UUID = UUID.randomUUID(),
EntityDeletedAt by EntityDeletedAtImp(), title: String,
Votable by VotableImp() { override var anonymous: Boolean = true,
override var content: String,
override var description: String,
override var tags: List<String> = emptyList(),
override val createdBy: CitizenBasic
) : ArticleBasicI,
ArticleSimple(id, title, createdBy) {
init { init {
tags = tags.distinct() tags = tags.distinct()
} }
} }
open class ArticleSimple(
id: UUID = UUID.randomUUID(),
override var title: String,
override val createdBy: CitizenBasic
) : ArticleSimpleI,
ArticleRef(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy<CitizenBasicI> by EntityCreatedByImp(createdBy),
EntityDeletedAt by EntityDeletedAtImp(),
EntityVersioning<UUID, Int> by UuidEntityVersioning(),
Votable by VotableImp()
open class ArticleRef(
id: UUID = UUID.randomUUID()
) : ArticleI, TargetRef(id)
interface ArticleI : UuidEntityI, TargetI
interface ArticleSimpleI :
ArticleI,
EntityVersioning<UUID, Int>,
EntityCreatedBy<CitizenBasicI>,
EntityCreatedAt,
EntityDeletedAt,
Votable {
var title: String
}
interface ArticleBasicI :
ArticleSimpleI {
var anonymous: Boolean
var content: String
var description: String
var tags: List<String>
}
interface ArticleFull :
ArticleBasicI {
var draft: Boolean
var lastVersion: Boolean
}

View File

@@ -1,24 +1,68 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.* import fr.dcproject.entity.CitizenI.Name
import fr.postgresjson.entity.immutable.EntityCreatedAt
import fr.postgresjson.entity.immutable.EntityCreatedAtImp
import fr.postgresjson.entity.immutable.UuidEntity
import fr.postgresjson.entity.immutable.UuidEntityI
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.* import java.util.*
class Citizen( class Citizen(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
var name: Name?, name: Name,
var email: String?, email: String,
var birthday: DateTime?, birthday: DateTime,
var userId: UUID? = null, voteAnonymous: Boolean = true,
var voteAnonymous: Boolean = true, followAnonymous: Boolean = true,
var followAnonymous: Boolean = true, override val user: User
var user: User? ) : CitizenFull,
CitizenBasic(id, name, email, birthday, voteAnonymous, followAnonymous, user),
EntityCreatedAt by EntityCreatedAtImp()
open class CitizenBasic(
id: UUID = UUID.randomUUID(),
name: Name,
override var email: String,
override var birthday: DateTime,
override var voteAnonymous: Boolean = true,
override var followAnonymous: Boolean = true,
user: UserRef
) : CitizenBasicI,
CitizenSimple(id, name, user)
open class CitizenSimple(
id: UUID = UUID.randomUUID(),
var name: Name,
user: UserRef
) : CitizenRef(id, user)
open class CitizenRef(
id: UUID = UUID.randomUUID(),
open val user: UserRef
) : UuidEntity(id), ) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(), CitizenI,
EntityDeletedAt by EntityDeletedAtImp() { EntityDeletedAt by EntityDeletedAtImp()
interface CitizenI : UuidEntityI {
data class Name( data class Name(
var firstName: String?, var firstName: String,
var lastName: String?, var lastName: String,
var civility: String? = null var civility: String? = null
) )
} }
interface CitizenBasicI : CitizenI, EntityDeletedAt {
var name: Name
var email: String
var birthday: DateTime
var voteAnonymous: Boolean
var followAnonymous: Boolean
val user: UserI
}
interface CitizenFull : CitizenBasicI {
override val user: User
}

View File

@@ -1,13 +1,15 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.* import fr.postgresjson.entity.immutable.EntityUpdatedAt
import fr.postgresjson.entity.immutable.EntityUpdatedAtImp
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import java.util.* import java.util.*
open class Comment <T : UuidEntity> ( open class Comment<T : TargetI>(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
createdBy: Citizen, override val createdBy: CitizenBasic,
target: T, target: T,
override var targetReference: String = target::class.simpleName!!.toLowerCase(),
var content: String, var content: String,
val responses: List<Comment<T>>? = null, val responses: List<Comment<T>>? = null,
var parent: Comment<T>? = null, var parent: Comment<T>? = null,
@@ -16,17 +18,18 @@ open class Comment <T : UuidEntity> (
) : Extra<T>(id, createdBy, target), ) : Extra<T>(id, createdBy, target),
EntityUpdatedAt by EntityUpdatedAtImp(), EntityUpdatedAt by EntityUpdatedAtImp(),
EntityDeletedAt by EntityDeletedAtImp(), EntityDeletedAt by EntityDeletedAtImp(),
Votable by VotableImp() Votable by VotableImp(),
{ TargetI {
constructor( constructor(
createdBy: Citizen, createdBy: CitizenBasic,
parent: Comment<T>, parent: Comment<T>,
content: String content: String
) : this( ) : this(
createdBy = createdBy, createdBy = createdBy,
parent = parent, parent = parent,
target = parent.target, target = parent.target,
targetReference = parent.targetReference,
content = content content = content
) )
override val reference get() = TargetI.getReference(this)
} }

View File

@@ -1,36 +1,69 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.* import fr.postgresjson.entity.immutable.*
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import java.util.* import java.util.*
class Constitution( class Constitution(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
var title: String?, title: String,
var anonymous: Boolean? = true, anonymous: Boolean = true,
var titles: List<Title> = listOf(), titles: MutableList<TitleSimple<ArticleSimple>> = mutableListOf(),
draft: Boolean = false,
lastVersion: Boolean = false,
override val createdBy: CitizenSimple
) : ConstitutionSimple<CitizenSimple, ConstitutionSimple.TitleSimple<ArticleSimple>>(
id,
title = title,
anonymous = anonymous,
titles = titles,
draft = draft,
lastVersion = lastVersion,
createdBy = createdBy
) {
class Title(
id: UUID = UUID.randomUUID(),
name: String,
rank: Int? = null,
override val articles: MutableList<ArticleSimple> = mutableListOf()
) : ConstitutionSimple.TitleSimple<ArticleSimple>(id, name, rank)
}
open class ConstitutionSimple<Cr : CitizenRef, T : ConstitutionSimple.TitleSimple<*>>(
id: UUID = UUID.randomUUID(),
var title: String,
var anonymous: Boolean = true,
open var titles: MutableList<T> = mutableListOf(),
var draft: Boolean = false, var draft: Boolean = false,
var lastVersion: Boolean = false, var lastVersion: Boolean = false,
createdBy: Citizen? override val createdBy: Cr,
) : UuidEntity(id), versionId: UUID = UUID.randomUUID()
EntityVersioning<UUID, Int> by UuidEntityVersioning(), ) : ConstitutionRef(id),
EntityVersioning<UUID, Int?> by UuidEntityVersioning(versionId = versionId),
EntityCreatedAt by EntityCreatedAtImp(), EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy<Citizen> by EntityCreatedByImp(createdBy), EntityCreatedBy<Cr> by EntityCreatedByImp(createdBy),
EntityDeletedAt by EntityDeletedAtImp() { EntityDeletedAt by EntityDeletedAtImp() {
init { init {
titles.forEachIndexed { index, title -> titles.forEachIndexed { index, title ->
title.createdBy = this.createdBy
title.rank = index title.rank = index
} }
} }
class Title( open class TitleSimple<A : ArticleI>(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
var name: String?, var name: String,
var rank: Int? = null, var rank: Int? = null,
var articles: List<Article> = listOf(), open val articles: MutableList<A> = mutableListOf()
createdBy: Citizen? = null ) : TitleRef(id)
) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy<Citizen> by EntityCreatedByImp(createdBy)
} }
open class ConstitutionRef(id: UUID = UUID.randomUUID()) : ConstitutionS(id) {
open class TitleRef(
id: UUID = UUID.randomUUID()
) : UuidEntity(id)
}
sealed class ConstitutionS(id: UUID = UUID.randomUUID()) : TargetRef(id), TargetI

View File

@@ -1,24 +1,62 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.immutable.*
import fr.postgresjson.entity.mutable.*
import java.util.* import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.full.isSuperclassOf
interface ExtraI <T : EntityI> : interface ExtraI<T : TargetI> :
EntityI, UuidEntityI,
EntityCreatedAt, EntityCreatedAt,
EntityCreatedBy<Citizen> { EntityCreatedBy<CitizenBasicI> {
var target: T var target: T
var targetReference: String
} }
abstract class Extra<T : UuidEntity>( abstract class Extra<T : TargetI>(
id: UUID? = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
createdBy: Citizen, override val createdBy: CitizenBasic,
override var target: T, override var target: T
override var targetReference: String = target::class.simpleName!!.toLowerCase()
) : ) :
ExtraI<T>, ExtraI<T>,
UuidEntity(id), UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(), EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy<Citizen> by EntityCreatedByImp(createdBy) EntityCreatedBy<CitizenBasicI> by EntityCreatedByImp(createdBy)
open class TargetRef(id: UUID = UUID.randomUUID()) : TargetI, UuidEntity(id) {
override val reference: String = ""
get() {
return if (field != "") field else TargetI.getReference(this)
}
}
interface TargetI : UuidEntityI {
enum class TargetName(val targetReference: String) {
Article("article"),
Constitution("constitution"),
Comment("comment")
}
companion object {
fun <T : TargetI> getReference(t: KClass<T>): String {
return when {
t.isSuperclassOf(Article::class) -> TargetName.Article.targetReference
t.isSuperclassOf(Constitution::class) -> TargetName.Constitution.targetReference
t.isSuperclassOf(Comment::class) -> TargetName.Comment.targetReference
else -> throw error("target not implemented")
}
}
fun getReference(t: TargetI): String {
val ref = this.getReference(t::class)
return if (t is ExtraI<*>) {
ref +
"_on_" +
t.target.reference
} else {
ref
}
}
}
val reference: String
}

View File

@@ -1,9 +1,8 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.UuidEntity
import java.util.* import java.util.*
class Follow <T : UuidEntity> ( class Follow <T : TargetI> (
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
createdBy: Citizen, override val createdBy: CitizenBasic,
target: T target: T
) : Extra<T>(id, createdBy, target) ) : Extra<T>(id, createdBy, target)

View File

@@ -1,19 +1,41 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.* import fr.dcproject.entity.UserI.Roles
import fr.postgresjson.entity.immutable.*
import io.ktor.auth.Principal import io.ktor.auth.Principal
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.* import java.util.*
class User( class User(
id: UUID? = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
var username: String?, username: String,
var blockedAt: DateTime? = null, blockedAt: DateTime? = null,
var plainPassword: String?, override var plainPassword: String?,
var roles: List<Roles> = emptyList() override var roles: List<Roles> = emptyList()
) : UuidEntity(id), ) : UserFull, UserBasic(id, username, blockedAt),
EntityCreatedAt by EntityCreatedAtImp(), EntityCreatedAt by EntityCreatedAtImp(),
EntityUpdatedAt by EntityUpdatedAtImp(), EntityUpdatedAt by EntityUpdatedAtImp()
Principal {
open class UserBasic(
id: UUID = UUID.randomUUID(),
override var username: String,
override var blockedAt: DateTime? = null
) : UserBasicI, UserRef(id)
open class UserRef(
id: UUID = UUID.randomUUID()
) : UserI, UuidEntity(id)
interface UserI : UuidEntityI, Principal {
enum class Roles { ROLE_USER, ROLE_ADMIN } enum class Roles { ROLE_USER, ROLE_ADMIN }
} }
interface UserBasicI : UserI {
var username: String
var blockedAt: DateTime?
}
interface UserFull : UserBasicI, EntityCreatedAt, EntityUpdatedAt {
var plainPassword: String?
var roles: List<Roles>
}

View File

@@ -1,13 +1,12 @@
package fr.dcproject.entity package fr.dcproject.entity
import fr.postgresjson.entity.mutable.EntityUpdatedAt import fr.postgresjson.entity.immutable.EntityUpdatedAt
import fr.postgresjson.entity.mutable.EntityUpdatedAtImp import fr.postgresjson.entity.immutable.EntityUpdatedAtImp
import fr.postgresjson.entity.mutable.UuidEntity
import java.util.* import java.util.*
open class Vote <T : UuidEntity> ( open class Vote <T : TargetI> (
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
createdBy: Citizen, override val createdBy: CitizenBasic,
target: T, target: T,
var note: Int, var note: Int,
var anonymous: Boolean = true var anonymous: Boolean = true

View File

@@ -1,5 +1,6 @@
package fr.dcproject.entity.request package fr.dcproject.entity.request
import fr.dcproject.entity.ArticleFull
import fr.dcproject.entity.Citizen import fr.dcproject.entity.Citizen
import java.util.* import java.util.*
import fr.dcproject.entity.Article as ArticleEntity import fr.dcproject.entity.Article as ArticleEntity
@@ -7,7 +8,7 @@ import fr.dcproject.entity.Article as ArticleEntity
class Article( class Article(
val id: UUID?, val id: UUID?,
val title: String, val title: String,
val anonymous: Boolean? = true, val anonymous: Boolean = true,
val content: String, val content: String,
val description: String, val description: String,
val tags: List<String> = emptyList(), val tags: List<String> = emptyList(),
@@ -16,7 +17,7 @@ class Article(
) : ) :
Request { Request {
fun merge(article: ArticleEntity) { fun merge(article: ArticleFull) {
article.title = this.title article.title = this.title
article.content = this.content article.content = this.content
article.description = this.description article.description = this.description

View File

@@ -0,0 +1,48 @@
package fr.dcproject.entity.request
import fr.dcproject.entity.ArticleRef
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.CitizenSimple
import fr.dcproject.entity.ConstitutionSimple
import fr.postgresjson.entity.immutable.UuidEntity
import java.util.*
class Constitution(
var title: String,
var anonymous: Boolean = true,
var titles: MutableList<Title> = mutableListOf(),
var draft: Boolean = false,
var lastVersion: Boolean = false,
var versionId: UUID = UUID.randomUUID()
) {
init {
titles.forEachIndexed { index, title ->
title.rank = index
}
}
class Title(
id: UUID = UUID.randomUUID(),
var name: String,
var rank: Int? = null,
var articles: MutableList<ArticleRef> = mutableListOf()
) : UuidEntity(id) {
fun create(): ConstitutionSimple.TitleSimple<ArticleRef> {
return ConstitutionSimple.TitleSimple(
id, name, rank, articles
)
}
}
fun create(createdBy: Citizen): ConstitutionSimple<CitizenSimple, ConstitutionSimple.TitleSimple<ArticleRef>> {
return ConstitutionSimple(
title = title,
titles = titles.create(),
createdBy = createdBy,
versionId = versionId
)
}
}
fun List<Constitution.Title>.create(): MutableList<ConstitutionSimple.TitleSimple<ArticleRef>> =
map { it.create() }.toMutableList()

View File

@@ -4,8 +4,8 @@ import com.sendgrid.helpers.mail.Mail
import com.sendgrid.helpers.mail.objects.Content import com.sendgrid.helpers.mail.objects.Content
import com.sendgrid.helpers.mail.objects.Email import com.sendgrid.helpers.mail.objects.Email
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.entity.CitizenBasicI
import io.ktor.http.URLBuilder import io.ktor.http.URLBuilder
import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
class SsoManager( class SsoManager(
@@ -15,11 +15,15 @@ class SsoManager(
) { ) {
fun sendMail(email: String, url: String) { fun sendMail(email: String, url: String) {
val citizen = citizenRepo.findByEmail(email) ?: noEmail(email) val citizen = citizenRepo.findByEmail(email) ?: noEmail(email)
sendMail(citizen, url)
}
fun sendMail(citizen: CitizenBasicI, url: String) {
mailer.sendEmail { mailer.sendEmail {
Mail( Mail(
Email("sso@$domain"), Email("sso@$domain"),
"Connection", "Connection",
Email(email), Email(citizen.email),
Content("text/plain", generateContent(citizen, url)) Content("text/plain", generateContent(citizen, url))
).apply { ).apply {
addContent(Content("text/html", generateHtmlContent(citizen, url))) addContent(Content("text/html", generateHtmlContent(citizen, url)))
@@ -27,15 +31,15 @@ class SsoManager(
} }
} }
private fun generateHtmlContent(citizen: CitizenEntity, url: String): String? { private fun generateHtmlContent(citizen: CitizenBasicI, url: String): String? {
val urlObject = URLBuilder(url) val urlObject = URLBuilder(url)
urlObject.parameters.append("token", JwtConfig.makeToken(citizen.user ?: error("Citizen must have User"))) urlObject.parameters.append("token", JwtConfig.makeToken(citizen.user))
return "Click <a href=\"${urlObject.buildString()}\">here</a> for connect to $domain" return "Click <a href=\"${urlObject.buildString()}\">here</a> for connect to $domain"
} }
private fun generateContent(citizen: CitizenEntity, url: String): String { private fun generateContent(citizen: CitizenBasicI, url: String): String {
val urlObject = URLBuilder(url) val urlObject = URLBuilder(url)
urlObject.parameters.append("token", JwtConfig.makeToken(citizen.user ?: error("Citizen must have User"))) urlObject.parameters.append("token", JwtConfig.makeToken(citizen.user))
return "Copy this link into your browser for connect to $domain: \n${urlObject.buildString()}" return "Copy this link into your browser for connect to $domain: \n${urlObject.buildString()}"
} }

View File

@@ -1,5 +1,7 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.ArticleFull
import fr.dcproject.entity.ArticleSimple
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.Parameter import fr.postgresjson.entity.Parameter
@@ -28,7 +30,7 @@ class Article(override var requester: Requester) : RepositoryI {
direction: Direction? = null, direction: Direction? = null,
search: String? = null, search: String? = null,
filter: Filter = Filter() filter: Filter = Filter()
): Paginated<ArticleEntity> { ): Paginated<ArticleSimple> {
return requester return requester
.getFunction("find_articles") .getFunction("find_articles")
.select( .select(
@@ -40,7 +42,7 @@ class Article(override var requester: Requester) : RepositoryI {
) )
} }
fun upsert(article: ArticleEntity): ArticleEntity? { fun upsert(article: ArticleFull): ArticleEntity? {
return requester return requester
.getFunction("upsert_article") .getFunction("upsert_article")
.selectOne("resource" to article) .selectOne("resource" to article)

View File

@@ -1,5 +1,8 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.CitizenBasic
import fr.dcproject.entity.CitizenFull
import fr.dcproject.entity.UserI
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
@@ -7,7 +10,6 @@ import fr.postgresjson.repository.RepositoryI.Direction
import net.pearx.kasechange.toSnakeCase import net.pearx.kasechange.toSnakeCase
import java.util.* import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.entity.User as UserEntity
class Citizen(override var requester: Requester) : RepositoryI { class Citizen(override var requester: Requester) : RepositoryI {
fun findById(id: UUID, withUser: Boolean = false): CitizenEntity? { fun findById(id: UUID, withUser: Boolean = false): CitizenEntity? {
@@ -16,7 +18,7 @@ class Citizen(override var requester: Requester) : RepositoryI {
.selectOne("id" to id) .selectOne("id" to id)
} }
fun findByUser(user: UserEntity): CitizenEntity? { fun findByUser(user: UserI): CitizenEntity? {
return requester return requester
.getFunction("find_citizen_by_user_id") .getFunction("find_citizen_by_user_id")
.selectOne("user_id" to user.id) .selectOne("user_id" to user.id)
@@ -40,7 +42,7 @@ class Citizen(override var requester: Requester) : RepositoryI {
sort: String? = null, sort: String? = null,
direction: Direction? = null, direction: Direction? = null,
search: String? = null search: String? = null
): Paginated<CitizenEntity> { ): Paginated<CitizenBasic> {
return requester return requester
.getFunction("find_citizens") .getFunction("find_citizens")
.select( .select(
@@ -51,13 +53,13 @@ class Citizen(override var requester: Requester) : RepositoryI {
) )
} }
fun upsert(citizen: CitizenEntity): CitizenEntity? { fun upsert(citizen: CitizenFull): CitizenEntity? {
return requester return requester
.getFunction("upsert_citizen") .getFunction("upsert_citizen")
.selectOne("resource" to citizen) .selectOne("resource" to citizen)
} }
fun insertWithUser(citizen: CitizenEntity): CitizenEntity? { fun insertWithUser(citizen: CitizenFull): CitizenEntity? {
return requester return requester
.getFunction("insert_citizen_with_user") .getFunction("insert_citizen_with_user")
.selectOne("resource" to citizen) .selectOne("resource" to citizen)

View File

@@ -1,16 +1,18 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.ArticleRef
import fr.dcproject.entity.TargetI
import fr.dcproject.entity.TargetRef
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.mutable.UuidEntity import fr.postgresjson.entity.immutable.UuidEntityI
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import java.util.* import java.util.*
import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.Comment as CommentEntity
import fr.dcproject.entity.Constitution as ConstitutionEntity import fr.dcproject.entity.Constitution as ConstitutionEntity
abstract class Comment <T : UuidEntity>(override var requester: Requester) : RepositoryI { abstract class Comment <T : TargetI>(override var requester: Requester) : RepositoryI {
abstract fun findById(id: UUID): CommentEntity<T>? abstract fun findById(id: UUID): CommentEntity<T>?
abstract fun findByCitizen( abstract fun findByCitizen(
@@ -24,7 +26,7 @@ abstract class Comment <T : UuidEntity>(override var requester: Requester) : Rep
page: Int = 1, page: Int = 1,
limit: Int = 50 limit: Int = 50
): Paginated<CommentEntity<T>> { ): Paginated<CommentEntity<T>> {
return findByParent(parent.id ?: error("comment must have an ID"), page, limit) return findByParent(parent.id, page, limit)
} }
open fun findByParent( open fun findByParent(
@@ -41,11 +43,11 @@ abstract class Comment <T : UuidEntity>(override var requester: Requester) : Rep
} }
open fun findByTarget( open fun findByTarget(
target: UuidEntity, target: UuidEntityI,
page: Int = 1, page: Int = 1,
limit: Int = 50 limit: Int = 50
): Paginated<CommentEntity<T>> { ): Paginated<CommentEntity<T>> {
return findByTarget(target.id ?: error("comment must have an ID"), page, limit) return findByTarget(target.id, page, limit)
} }
open fun findByTarget( open fun findByTarget(
@@ -65,7 +67,7 @@ abstract class Comment <T : UuidEntity>(override var requester: Requester) : Rep
requester requester
.getFunction("comment") .getFunction("comment")
.sendQuery( .sendQuery(
"reference" to comment.targetReference, "reference" to comment.target.reference,
"resource" to comment "resource" to comment
) )
} }
@@ -80,8 +82,8 @@ abstract class Comment <T : UuidEntity>(override var requester: Requester) : Rep
} }
} }
class CommentGeneric(requester: Requester) : Comment<UuidEntity>(requester) { class CommentGeneric(requester: Requester) : Comment<TargetRef>(requester) {
override fun findById(id: UUID): CommentEntity<UuidEntity>? { override fun findById(id: UUID): CommentEntity<TargetRef>? {
return requester return requester
.getFunction("find_comment_by_id") .getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id)) .selectOne(mapOf("id" to id))
@@ -91,7 +93,7 @@ class CommentGeneric(requester: Requester) : Comment<UuidEntity>(requester) {
citizen: CitizenEntity, citizen: CitizenEntity,
page: Int, page: Int,
limit: Int limit: Int
): Paginated<CommentEntity<UuidEntity>> { ): Paginated<CommentEntity<TargetRef>> {
return requester.run { return requester.run {
getFunction("find_comments_by_citizen") getFunction("find_comments_by_citizen")
.select(page, limit, .select(page, limit,
@@ -101,8 +103,8 @@ class CommentGeneric(requester: Requester) : Comment<UuidEntity>(requester) {
} }
} }
class CommentArticle(requester: Requester) : Comment<ArticleEntity>(requester) { class CommentArticle(requester: Requester) : Comment<ArticleRef>(requester) {
override fun findById(id: UUID): CommentEntity<ArticleEntity>? { override fun findById(id: UUID): CommentEntity<ArticleRef>? {
return requester return requester
.getFunction("find_comment_by_id") .getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id)) .selectOne(mapOf("id" to id))
@@ -112,13 +114,12 @@ class CommentArticle(requester: Requester) : Comment<ArticleEntity>(requester) {
citizen: CitizenEntity, citizen: CitizenEntity,
page: Int, page: Int,
limit: Int limit: Int
): Paginated<CommentEntity<ArticleEntity>> { ): Paginated<CommentEntity<ArticleRef>> {
val reference = ArticleEntity::class.simpleName!!.toLowerCase()
return requester.run { return requester.run {
getFunction("find_comments_by_citizen") getFunction("find_comments_by_citizen")
.select(page, limit, .select(page, limit,
"created_by_id" to citizen.id, "created_by_id" to citizen.id,
"reference" to reference "reference" to TargetI.getReference(ArticleRef::class)
) )
} }
} }
@@ -136,12 +137,11 @@ class CommentConstitution(requester: Requester) : Comment<ConstitutionEntity>(re
page: Int, page: Int,
limit: Int limit: Int
): Paginated<CommentEntity<ConstitutionEntity>> { ): Paginated<CommentEntity<ConstitutionEntity>> {
val reference = ConstitutionEntity::class.simpleName!!.toLowerCase()
return requester.run { return requester.run {
getFunction("find_comments_by_citizen") getFunction("find_comments_by_citizen")
.select(page, limit, .select(page, limit,
"created_by_id" to citizen.id, "created_by_id" to citizen.id,
"reference" to reference "reference" to TargetI.getReference(ConstitutionEntity::class)
) )
} }
} }

View File

@@ -1,5 +1,8 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.ArticleRef
import fr.dcproject.entity.CitizenSimple
import fr.dcproject.entity.ConstitutionSimple
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
@@ -31,7 +34,7 @@ class Constitution(override var requester: Requester) : RepositoryI {
) )
} }
fun upsert(constitution: ConstitutionEntity): ConstitutionEntity? { fun upsert(constitution: ConstitutionSimple<CitizenSimple, ConstitutionSimple.TitleSimple<ArticleRef>>): ConstitutionEntity? {
return requester return requester
.getFunction("upsert_constitution") .getFunction("upsert_constitution")
.selectOne("resource" to constitution) .selectOne("resource" to constitution)

View File

@@ -1,22 +1,22 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.TargetI
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.mutable.UuidEntity
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import java.util.* import java.util.*
import fr.dcproject.entity.Article as ArticleEntity import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.entity.Constitution as ConstitutionEntity import fr.dcproject.entity.Constitution as ConstitutionEntity
import fr.dcproject.entity.Follow as FollowEntity import fr.dcproject.entity.Follow as FollowEntity
open class Follow <T : UuidEntity>(override var requester: Requester) : RepositoryI { open class Follow <T : TargetI>(override var requester: Requester) : RepositoryI {
open fun findByCitizen( open fun findByCitizen(
citizen: CitizenEntity, citizen: CitizenI,
page: Int = 1, page: Int = 1,
limit: Int = 50 limit: Int = 50
): Paginated<FollowEntity<T>> = ): Paginated<FollowEntity<T>> =
findByCitizen(citizen.id ?: error("The citizen must have an id"), page, limit) findByCitizen(citizen.id, page, limit)
open fun findByCitizen( open fun findByCitizen(
citizenId: UUID, citizenId: UUID,
@@ -32,24 +32,22 @@ open class Follow <T : UuidEntity>(override var requester: Requester) : Reposito
} }
fun follow(follow: FollowEntity<T>) { fun follow(follow: FollowEntity<T>) {
val reference = follow.target::class.simpleName!!.toLowerCase()
requester requester
.getFunction("follow") .getFunction("follow")
.sendQuery( .sendQuery(
"reference" to reference, "reference" to follow.target.reference,
"target_id" to follow.target.id, "target_id" to follow.target.id,
"created_by_id" to follow.createdBy?.id "created_by_id" to follow.createdBy.id
) )
} }
fun unfollow(follow: FollowEntity<T>) { fun unfollow(follow: FollowEntity<T>) {
val reference = follow.target::class.simpleName!!.toLowerCase()
requester requester
.getFunction("unfollow") .getFunction("unfollow")
.sendQuery( .sendQuery(
"reference" to reference, "reference" to follow.target.reference,
"target_id" to follow.target.id, "target_id" to follow.target.id,
"created_by_id" to follow.createdBy?.id "created_by_id" to follow.createdBy.id
) )
} }
} }

View File

@@ -1,5 +1,6 @@
package fr.dcproject.repository package fr.dcproject.repository
import fr.dcproject.entity.UserFull
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import io.ktor.auth.UserPasswordCredential import io.ktor.auth.UserPasswordCredential
@@ -30,7 +31,7 @@ class User(override var requester: Requester) : RepositoryI {
.selectOne("resource" to user) .selectOne("resource" to user)
} }
fun changePassword(user: UserEntity) { fun changePassword(user: UserFull) {
requester requester
.getFunction("change_user_password") .getFunction("change_user_password")
.sendQuery("resource" to user) .sendQuery("resource" to user)

View File

@@ -1,34 +1,25 @@
package fr.dcproject.repository package fr.dcproject.repository
import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.entity.*
import fr.dcproject.entity.Article import fr.dcproject.entity.Article
import fr.dcproject.entity.Comment import fr.dcproject.entity.Comment
import fr.dcproject.entity.Constitution import fr.dcproject.entity.Constitution
import fr.dcproject.entity.VoteAggregation
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.mutable.UuidEntity
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import java.util.* import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.entity.Vote as VoteEntity import fr.dcproject.entity.Vote as VoteEntity
open class Vote <T : UuidEntity>(override var requester: Requester) : RepositoryI { open class Vote <T : TargetI>(override var requester: Requester) : RepositoryI {
fun vote(vote: VoteEntity<T>): VoteAggregation { fun vote(vote: VoteEntity<T>): VoteAggregation {
val target = vote.target val author = vote.createdBy
val reference = if (target is Comment<*>) {
target::class.simpleName!!.toLowerCase() +
"_on_" +
target.targetReference
} else {
target::class.simpleName!!.toLowerCase()
}
val author = vote.createdBy ?: error("vote must be contain an author")
val anonymous = author.voteAnonymous val anonymous = author.voteAnonymous
return requester return requester
.getFunction("vote") .getFunction("vote")
.selectOne( .selectOne(
"reference" to reference, "reference" to vote.target.reference,
"target_id" to vote.target.id, "target_id" to vote.target.id,
"note" to vote.note, "note" to vote.note,
"created_by_id" to author.id, "created_by_id" to author.id,
@@ -56,12 +47,11 @@ open class Vote <T : UuidEntity>(override var requester: Requester) : Repository
citizen: CitizenEntity, citizen: CitizenEntity,
targets: List<UUID> targets: List<UUID>
): List<VoteEntity<*>> { ): List<VoteEntity<*>> {
val typeReference = object : TypeReference<List<VoteEntity<UuidEntity>>>() {} val typeReference = object : TypeReference<List<VoteEntity<TargetRef>>>() {}
return requester.run { return requester.run {
val citizenId = citizen.id ?: error("The citizen must have an id")
getFunction("find_citizen_votes_by_target_ids") getFunction("find_citizen_votes_by_target_ids")
.select(typeReference, mapOf( .select(typeReference, mapOf(
"citizen_id" to citizenId, "citizen_id" to citizen.id,
"ids" to targets "ids" to targets
)) ))
} }
@@ -75,7 +65,7 @@ class VoteArticle(requester: Requester) : Vote<Article>(requester) {
limit: Int = 50 limit: Int = 50
): Paginated<VoteEntity<Article>> = ): Paginated<VoteEntity<Article>> =
findByCitizen( findByCitizen(
citizen.id ?: error("The citizen must have an id"), citizen.id,
"article", "article",
object : TypeReference<List<VoteEntity<Article>>>() {}, object : TypeReference<List<VoteEntity<Article>>>() {},
page, page,
@@ -90,7 +80,7 @@ class VoteArticleComment(requester: Requester) : Vote<Comment<Article>>(requeste
limit: Int = 50 limit: Int = 50
): Paginated<VoteEntity<Comment<Article>>> = ): Paginated<VoteEntity<Comment<Article>>> =
findByCitizen( findByCitizen(
citizen.id ?: error("The citizen must have an id"), citizen.id,
"article", "article",
object : TypeReference<List<VoteEntity<Comment<Article>>>>() {}, object : TypeReference<List<VoteEntity<Comment<Article>>>>() {},
page, page,
@@ -98,16 +88,16 @@ class VoteArticleComment(requester: Requester) : Vote<Comment<Article>>(requeste
) )
} }
class VoteComment(requester: Requester) : Vote<Comment<UuidEntity>>(requester) { class VoteComment(requester: Requester) : Vote<Comment<TargetRef>>(requester) {
fun findByCitizen( fun findByCitizen(
citizen: CitizenEntity, citizen: CitizenEntity,
page: Int = 1, page: Int = 1,
limit: Int = 50 limit: Int = 50
): Paginated<VoteEntity<Comment<UuidEntity>>> = ): Paginated<VoteEntity<Comment<TargetRef>>> =
findByCitizen( findByCitizen(
citizen.id ?: error("The citizen must have an id"), citizen.id,
"article", "article",
object : TypeReference<List<VoteEntity<Comment<UuidEntity>>>>() {}, object : TypeReference<List<VoteEntity<Comment<TargetRef>>>>() {},
page, page,
limit limit
) )
@@ -120,7 +110,7 @@ class VoteConstitution(requester: Requester) : Vote<Constitution>(requester) {
limit: Int = 50 limit: Int = 50
): Paginated<VoteEntity<Constitution>> = ): Paginated<VoteEntity<Constitution>> =
findByCitizen( findByCitizen(
citizen.id ?: error("The citizen must have an id"), citizen.id,
"constitution", "constitution",
object : TypeReference<List<VoteEntity<Constitution>>>() {}, object : TypeReference<List<VoteEntity<Constitution>>>() {},
page, page,

View File

@@ -1,8 +1,9 @@
package fr.dcproject.routes package fr.dcproject.routes
import com.fasterxml.jackson.databind.exc.MismatchedInputException import com.fasterxml.jackson.databind.exc.MismatchedInputException
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.entity.User import fr.dcproject.entity.UserI.Roles.ROLE_USER
import fr.dcproject.messages.SsoManager import fr.dcproject.messages.SsoManager
import fr.dcproject.routes.AuthPaths.LoginRequest import fr.dcproject.routes.AuthPaths.LoginRequest
import fr.dcproject.routes.AuthPaths.RegisterRequest import fr.dcproject.routes.AuthPaths.RegisterRequest
@@ -52,12 +53,14 @@ fun Route.auth(
} }
post <RegisterRequest> { post <RegisterRequest> {
val citizen = call.receive<CitizenEntity>() try {
citizen.user?.roles = listOf(User.Roles.ROLE_USER) val citizen = call.receive<CitizenEntity>()
// TODO implement with validator citizen.user.roles = listOf(ROLE_USER)
citizen.email ?: throw BadRequestException("Bad request") val created = citizenRepo.insertWithUser(citizen)?.user ?: throw BadRequestException("Bad request")
val created = citizenRepo.insertWithUser(citizen)?.user ?: throw BadRequestException("Bad request") call.respondText(JwtConfig.makeToken(created))
call.respondText(JwtConfig.makeToken(created)) } catch (e: MissingKotlinParameterException) {
call.respond(HttpStatusCode.BadRequest)
}
} }
post<SsoRequest> { post<SsoRequest> {

View File

@@ -62,7 +62,7 @@ fun Route.citizen(
assertCan(CHANGE_PASSWORD, it.citizen) assertCan(CHANGE_PASSWORD, it.citizen)
val content = call.receive<ChangePasswordCitizenRequest.Content>() val content = call.receive<ChangePasswordCitizenRequest.Content>()
val user = it.citizen.user ?: error("Citizen must have User") val user = it.citizen.user
user.plainPassword = content.password user.plainPassword = content.password
userRepository.changePassword(user) userRepository.changePassword(user)

View File

@@ -73,7 +73,6 @@ fun Route.comment(repo: CommentRepository) {
assertCan(CREATE, newComment) assertCan(CREATE, newComment)
repo.comment(newComment) repo.comment(newComment)
call.respond(HttpStatusCode.Created, newComment) call.respond(HttpStatusCode.Created, newComment)
} }
@@ -86,4 +85,4 @@ fun Route.comment(repo: CommentRepository) {
call.respond(HttpStatusCode.OK, comment) call.respond(HttpStatusCode.OK, comment)
} }
} }

View File

@@ -1,6 +1,7 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.ArticleRef
import fr.dcproject.entity.Citizen import fr.dcproject.entity.Citizen
import fr.dcproject.security.voter.CommentVoter.Action.CREATE import fr.dcproject.security.voter.CommentVoter.Action.CREATE
import fr.dcproject.security.voter.CommentVoter.Action.VIEW import fr.dcproject.security.voter.CommentVoter.Action.VIEW
@@ -14,7 +15,6 @@ import io.ktor.locations.post
import io.ktor.request.receive import io.ktor.request.receive
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.routing.Route import io.ktor.routing.Route
import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.Comment as CommentEntity
import fr.dcproject.entity.request.Comment as CommentEntityRequest import fr.dcproject.entity.request.Comment as CommentEntityRequest
import fr.dcproject.repository.CommentArticle as CommentArticleRepository import fr.dcproject.repository.CommentArticle as CommentArticleRepository
@@ -23,7 +23,7 @@ import fr.dcproject.repository.CommentArticle as CommentArticleRepository
object CommentArticlePaths { object CommentArticlePaths {
@Location("/articles/{article}/comments") @Location("/articles/{article}/comments")
class ArticleCommentRequest( class ArticleCommentRequest(
val article: ArticleEntity, val article: ArticleRef,
page: Int = 1, page: Int = 1,
limit: Int = 50, limit: Int = 50,
val search: String? = null val search: String? = null

View File

@@ -1,6 +1,7 @@
package fr.dcproject.routes package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.request.Constitution
import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE import fr.dcproject.security.voter.ConstitutionVoter.Action.CREATE
import fr.dcproject.security.voter.ConstitutionVoter.Action.VIEW import fr.dcproject.security.voter.ConstitutionVoter.Action.VIEW
import fr.dcproject.security.voter.assertCan import fr.dcproject.security.voter.assertCan
@@ -41,8 +42,7 @@ fun Route.constitution(repo: ConstitutionRepository) {
} }
post<ConstitutionPaths.PostConstitutionRequest> { post<ConstitutionPaths.PostConstitutionRequest> {
val constitution = call.receive<ConstitutionEntity>() val constitution = call.receive<Constitution>().create(citizen)
constitution.createdBy = citizen
assertCan(CREATE, constitution) assertCan(CREATE, constitution)
repo.upsert(constitution) repo.upsert(constitution)

View File

@@ -1,8 +1,8 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.entity.User import fr.dcproject.entity.ArticleSimpleI
import fr.dcproject.entity.UserI
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.Comment as CommentEntity
import fr.dcproject.entity.Vote as VoteEntity import fr.dcproject.entity.Vote as VoteEntity
@@ -16,23 +16,23 @@ class ArticleVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action) return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action)
.and(subject is List<*> || subject is ArticleEntity? || subject is VoteEntity<*> || subject is CommentEntity<*>) .and(subject is List<*> || subject is ArticleSimpleI? || subject is VoteEntity<*> || subject is CommentEntity<*>)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
val user = call.user val user = call.user
if (action == Action.CREATE && user is User) { if (action == Action.CREATE && user is UserI) {
return Vote.GRANTED return Vote.GRANTED
} }
if (action == Action.VIEW) { if (action == Action.VIEW) {
if (subject is ArticleEntity) { if (subject is ArticleSimpleI) {
return if (subject.isDeleted()) Vote.DENIED return if (subject.isDeleted()) Vote.DENIED
else Vote.GRANTED else Vote.GRANTED
} }
if (subject is List<*>) { if (subject is List<*>) {
subject.forEach { subject.forEach {
if (it !is ArticleEntity || it.isDeleted()) { if (it !is ArticleSimpleI || it.isDeleted()) {
return Vote.DENIED return Vote.DENIED
} }
} }
@@ -44,12 +44,12 @@ class ArticleVoter : Voter {
if (action is CommentVoter.Action) return voteForComment(action) if (action is CommentVoter.Action) return voteForComment(action)
if (action is VoteVoter.Action) return voteForVote(action, subject) if (action is VoteVoter.Action) return voteForVote(action, subject)
if (subject is ArticleEntity) { if (subject is ArticleSimpleI) {
if (action == Action.DELETE && user is User && subject.createdBy?.userId == user.id) { if (action == Action.DELETE && user is UserI && subject.createdBy.user.id == user.id) {
return Vote.GRANTED return Vote.GRANTED
} }
if (action == Action.UPDATE && user is User && subject.createdBy?.userId == user.id) { if (action == Action.UPDATE && user is UserI && subject.createdBy.user.id == user.id) {
return Vote.GRANTED return Vote.GRANTED
} }
@@ -66,7 +66,7 @@ class ArticleVoter : Voter {
private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote { private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote {
if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) { if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) {
val target = subject.target val target = subject.target
if (target !is ArticleEntity) { if (target !is ArticleSimpleI) {
return Vote.ABSTAIN return Vote.ABSTAIN
} }
if (target.isDeleted()) { if (target.isDeleted()) {

View File

@@ -1,7 +1,7 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.entity.Citizen import fr.dcproject.entity.CitizenBasicI
import fr.dcproject.entity.User import fr.dcproject.entity.UserI
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -17,7 +17,7 @@ class CitizenVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action) return (action is Action)
.and(subject is List<*> || subject is Citizen?) .and(subject is List<*> || subject is CitizenBasicI?)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -28,13 +28,13 @@ class CitizenVoter : Voter {
if (action == Action.VIEW) { if (action == Action.VIEW) {
if (user == null) return Vote.DENIED if (user == null) return Vote.DENIED
if (subject is Citizen) { if (subject is CitizenBasicI) {
return if (subject.isDeleted()) Vote.DENIED return if (subject.isDeleted()) Vote.DENIED
else Vote.GRANTED else Vote.GRANTED
} }
if (subject is List<*>) { if (subject is List<*>) {
subject.forEach { subject.forEach {
if (it !is Citizen || it.isDeleted()) { if (it !is CitizenBasicI || it.isDeleted()) {
return Vote.DENIED return Vote.DENIED
} }
} }
@@ -48,14 +48,14 @@ class CitizenVoter : Voter {
} }
if (action == Action.UPDATE && if (action == Action.UPDATE &&
user is User && user is UserI &&
subject is Citizen && subject is CitizenBasicI &&
subject.user?.id == user.id) { subject.user.id == user.id) {
return Vote.GRANTED return Vote.GRANTED
} }
if (action == Action.CHANGE_PASSWORD && user != null && subject is Citizen) { if (action == Action.CHANGE_PASSWORD && user != null && subject is CitizenBasicI) {
val userToChange = subject.user ?: error("Citizen must have User") val userToChange = subject.user
return if (user.id == userToChange.id) { return if (user.id == userToChange.id) {
Vote.GRANTED Vote.GRANTED
} else { } else {

View File

@@ -38,7 +38,7 @@ class CommentVoter : Voter {
return Vote.DENIED return Vote.DENIED
} }
if (action == Action.UPDATE && user != null && subject is Comment<*> && user.id == subject.createdBy?.userId) { if (action == Action.UPDATE && user != null && subject is Comment<*> && user.id == subject.createdBy.user.id) {
return Vote.GRANTED return Vote.GRANTED
} }

View File

@@ -1,9 +1,9 @@
package fr.dcproject.security.voter package fr.dcproject.security.voter
import fr.dcproject.entity.Comment import fr.dcproject.entity.Comment
import fr.dcproject.entity.User import fr.dcproject.entity.ConstitutionSimple
import fr.dcproject.entity.UserI
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import fr.dcproject.entity.Constitution as ConstitutionEntity
import fr.dcproject.entity.Vote as VoteEntity import fr.dcproject.entity.Vote as VoteEntity
class ConstitutionVoter : Voter { class ConstitutionVoter : Voter {
@@ -16,7 +16,7 @@ class ConstitutionVoter : Voter {
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean { override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action) return (action is Action || action is CommentVoter.Action || action is VoteVoter.Action)
.and(subject is List<*> || subject is ConstitutionEntity? || subject is VoteEntity<*> || subject is Comment<*>) .and(subject is List<*> || subject is ConstitutionSimple<*, *>? || subject is VoteEntity<*> || subject is Comment<*>)
} }
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote { override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
@@ -26,13 +26,13 @@ class ConstitutionVoter : Voter {
} }
if (action == Action.VIEW) { if (action == Action.VIEW) {
if (subject is ConstitutionEntity) { if (subject is ConstitutionSimple<*, *>) {
return if (subject.isDeleted()) Vote.DENIED return if (subject.isDeleted()) Vote.DENIED
else Vote.GRANTED else Vote.GRANTED
} }
if (subject is List<*>) { if (subject is List<*>) {
subject.forEach { subject.forEach {
if (it !is ConstitutionEntity || it.isDeleted()) { if (it !is ConstitutionSimple<*, *> || it.isDeleted()) {
return Vote.DENIED return Vote.DENIED
} }
} }
@@ -41,11 +41,11 @@ class ConstitutionVoter : Voter {
return Vote.DENIED return Vote.DENIED
} }
if (action == Action.DELETE && user is User && subject is ConstitutionEntity && subject.createdBy?.userId == user.id) { if (action == Action.DELETE && user is UserI && subject is ConstitutionSimple<*, *> && subject.createdBy.user.id == user.id) {
return Vote.GRANTED return Vote.GRANTED
} }
if (action == Action.UPDATE && user is User && subject is ConstitutionEntity && subject.createdBy?.userId == user.id) { if (action == Action.UPDATE && user is UserI && subject is ConstitutionSimple<*, *> && subject.createdBy.user.id == user.id) {
return Vote.GRANTED return Vote.GRANTED
} }
@@ -62,7 +62,7 @@ class ConstitutionVoter : Voter {
private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote { private fun voteForVote(action: VoteVoter.Action, subject: Any?): Vote {
if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) { if (action == VoteVoter.Action.CREATE && subject is VoteEntity<*>) {
val target = subject.target val target = subject.target
if (target !is ConstitutionEntity) { if (target !is ConstitutionSimple<*, *>) {
return Vote.ABSTAIN return Vote.ABSTAIN
} }
if (target.isDeleted()) { if (target.isDeleted()) {

View File

@@ -47,7 +47,7 @@ class FollowVoter : Voter {
} }
private fun voteView(user: UserEntity?, subject: FollowEntity<*>): Vote { private fun voteView(user: UserEntity?, subject: FollowEntity<*>): Vote {
return if ((user != null && subject.createdBy?.user?.id == user.id) || subject.createdBy?.followAnonymous == false) Vote.GRANTED return if ((user != null && subject.createdBy.user.id == user.id) || !subject.createdBy.followAnonymous) Vote.GRANTED
else Vote.DENIED else Vote.DENIED
} }
} }

View File

@@ -24,7 +24,7 @@ class VoteVoter : Voter {
if (action == Action.VIEW && user != null) { if (action == Action.VIEW && user != null) {
if (subject is VoteEntity<*>) { if (subject is VoteEntity<*>) {
return if (subject.createdBy?.userId != user.id) { return if (subject.createdBy.user.id != user.id) {
Vote.DENIED Vote.DENIED
} else { } else {
Vote.GRANTED Vote.GRANTED
@@ -33,7 +33,7 @@ class VoteVoter : Voter {
if (subject is List<*>) { if (subject is List<*>) {
subject.forEach { subject.forEach {
if (it !is VoteEntity<*> || it.createdBy?.userId != user.id) { if (it !is VoteEntity<*> || it.createdBy.user.id != user.id) {
return Vote.DENIED return Vote.DENIED
} }
} }

View File

@@ -17,20 +17,18 @@ begin
now() + (row_number() over () * interval '7 minute 3 second') now() + (row_number() over () * interval '7 minute 3 second')
from citizen z; from citizen z;
insert into title (id, created_by_id, name, rank, constitution_id) insert into title (id, name, rank, constitution_id)
select select
uuid_in(md5('constitution_title'||row_number() over ())::cstring), uuid_in(md5('constitution_title'||row_number() over ())::cstring),
c.created_by_id,
'name' || row_number() over (), 'name' || row_number() over (),
row_number() over (), row_number() over (),
c.id c.id
from constitution c, from constitution c,
lateral generate_series(1, 5) g; lateral generate_series(1, 5) g;
insert into article_in_title (id, created_by_id, rank, title_id, article_id, constitution_id) insert into article_in_title (id, rank, title_id, article_id, constitution_id)
select select
uuid_in(md5('article_in_title'||row_number() over ())::cstring), uuid_in(md5('article_in_title'||row_number() over ())::cstring),
ti.created_by_id,
row_number() over (), row_number() over (),
ti.id, ti.id,
a.id, a.id,

View File

@@ -6,7 +6,8 @@ begin
select to_json(t) into resource select to_json(t) into resource
from ( from (
select select
z.* z.*,
find_user_by_id(z.user_id) as "user"
from citizen as z from citizen as z
where z.id = _id where z.id = _id
) as t; ) as t;

View File

@@ -7,9 +7,8 @@ begin
from ( from (
select select
z.*, z.*,
u as "user" find_user_by_id(z.user_id) as "user"
from citizen as z from citizen as z
join "user" u on z.user_id = u.id
where z.id = _id where z.id = _id
) as t; ) as t;
end; end;

View File

@@ -13,7 +13,8 @@ begin
into resource, total into resource, total
from ( from (
select select
z.* z.*,
json_build_object('id', z.user_id) as "user"
from citizen as z from citizen as z
where "search" is null or ( where "search" is null or (
(name->'first_name')::text ilike '%'||"search"||'%' or (name->'first_name')::text ilike '%'||"search"||'%' or

View File

@@ -4,12 +4,10 @@ $$
declare declare
_title alias for title; _title alias for title;
_constitution_id uuid = coalesce(constitution_id, (title#>>'{constitution_id}')::uuid); _constitution_id uuid = coalesce(constitution_id, (title#>>'{constitution_id}')::uuid);
_author_id uuid = (title#>>'{created_by, id}')::uuid;
new_id uuid; new_id uuid;
begin begin
insert into title (created_by_id, name, rank, constitution_id) insert into title (name, rank, constitution_id)
select select
_author_id,
ti.name, ti.name,
row_number() OVER (), row_number() OVER (),
_constitution_id _constitution_id
@@ -17,9 +15,8 @@ begin
returning id into new_id; returning id into new_id;
if (_title->'articles' is not null) then if (_title->'articles' is not null) then
insert into article_in_title (created_by_id, rank, title_id, article_id, constitution_id) insert into article_in_title (rank, title_id, article_id, constitution_id)
select select
_author_id,
row_number() over (), row_number() over (),
new_id, new_id,
id, id,

View File

@@ -46,10 +46,6 @@ begin
titles := (resource->>'titles'); titles := (resource->>'titles');
for _title in select json_array_elements(titles) loop for _title in select json_array_elements(titles) loop
if _title#>>'{created_by, id}' is null then
_title := jsonb_set(_title::jsonb, '{created_by}'::text[], jsonb_build_object('id', _citizen_id::text), true)::json;
end if;
perform create_title_in_constitution(_title, new_id); perform create_title_in_constitution(_title, new_id);
end loop; end loop;

View File

@@ -274,7 +274,6 @@ create table title
( (
id uuid default uuid_generate_v4() not null primary key, id uuid default uuid_generate_v4() not null primary key,
created_at timestamptz default now() not null, created_at timestamptz default now() not null,
created_by_id uuid not null references citizen (id),
name text not null check ( name != '' ), name text not null check ( name != '' ),
rank int not null check ( rank >= 0 ), rank int not null check ( rank >= 0 ),
constitution_id uuid not null references constitution (id) constitution_id uuid not null references constitution (id)
@@ -284,7 +283,6 @@ create table article_in_title
( (
id uuid default uuid_generate_v4() not null primary key, id uuid default uuid_generate_v4() not null primary key,
created_at timestamptz default now() not null, created_at timestamptz default now() not null,
created_by_id uuid not null references citizen (id),
rank int not null check ( rank >= 0 ), rank int not null check ( rank >= 0 ),
title_id uuid not null references title (id), title_id uuid not null references title (id),
article_id uuid not null references article (id), article_id uuid not null references article (id),

View File

@@ -1,5 +1,6 @@
import fr.dcproject.entity.Article import fr.dcproject.entity.Article
import fr.dcproject.entity.Citizen import fr.dcproject.entity.CitizenBasic
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.deserialize
import fr.postgresjson.serializer.serialize import fr.postgresjson.serializer.serialize
@@ -57,8 +58,8 @@ class ArticleTest {
@Test @Test
fun `test Article serialize`() { fun `test Article serialize`() {
val user = User(username = "jaque", plainPassword = "azerty") val user = User(username = "jaque", plainPassword = "azerty")
val citizen = Citizen( val citizen = CitizenBasic(
name = Citizen.Name("Jaque", "Bono"), name = CitizenI.Name("Jaque", "Bono"),
birthday = DateTime.now(), birthday = DateTime.now(),
email = "jaque.bono@gmail.com", email = "jaque.bono@gmail.com",
user = user user = user

View File

@@ -1,4 +1,5 @@
import fr.dcproject.entity.Citizen import fr.dcproject.entity.CitizenBasic
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.Constitution import fr.dcproject.entity.Constitution
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.deserialize
@@ -78,8 +79,8 @@ class ConstitutionTest {
@Test @Test
fun `test Constitution serialize`() { fun `test Constitution serialize`() {
val user = User(username = "jaque", plainPassword = "azerty") val user = User(username = "jaque", plainPassword = "azerty")
val citizen = Citizen( val citizen = CitizenBasic(
name = Citizen.Name("Jaque", "Bono"), name = CitizenI.Name("Jaque", "Bono"),
email = "jaque.bono@gmail.com", email = "jaque.bono@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = user user = user
@@ -90,7 +91,7 @@ class ConstitutionTest {
val constitution = Constitution( val constitution = Constitution(
title = "Hello world!", title = "Hello world!",
anonymous = true, anonymous = true,
titles = listOf(title1), titles = mutableListOf(title1),
createdBy = citizen createdBy = citizen
) )
println(constitution.serialize()) println(constitution.serialize())

View File

@@ -1,7 +1,4 @@
import fr.dcproject.entity.Article import fr.dcproject.entity.*
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.Follow
import fr.dcproject.entity.User
import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.deserialize
import fr.postgresjson.serializer.serialize import fr.postgresjson.serializer.serialize
import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -53,7 +50,25 @@ class FollowTest {
], ],
"created_by":{ "created_by":{
"id":"4a87ad24-187a-46a8-97ab-00b30a24e561" "id":"4a87ad24-187a-46a8-97ab-00b30a24e561",
"name":{
"first_name":"Jaque",
"last_name":"Bono",
"civility":null
},
"email": "jaque.bono@gmail.com",
"birthday":"2019-08-09T11:42:47.168Z",
"user_id":null,
"vote_anonymous":null,
"follow_anonymous":null,
"user":{
"id":"721db690-d050-46e6-92b0-056f2e8ba993",
"username":"jaque",
"blocked_at":null,
"plain_password":"azerty",
"created_at":null,
"updated_at":null
}
}, },
"version_id":"a4aa7dd4-d174-42d2-9ba5-ae6f1129ffce", "version_id":"a4aa7dd4-d174-42d2-9ba5-ae6f1129ffce",
"version_number":null, "version_number":null,
@@ -65,8 +80,8 @@ class FollowTest {
@Test @Test
fun `test Follow Article serialize`() { fun `test Follow Article serialize`() {
val user = User(username = "jaque", plainPassword = "azerty") val user = User(username = "jaque", plainPassword = "azerty")
val citizen = Citizen( val citizen = CitizenBasic(
name = Citizen.Name("Jaque", "Bono"), name = CitizenI.Name("Jaque", "Bono"),
email = "jaque.bono@gmail.com", email = "jaque.bono@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = user user = user
@@ -87,7 +102,7 @@ class FollowTest {
@Test @Test
fun `test Follow Article Deserialize`() { fun `test Follow Article Deserialize`() {
val follow: Follow<Article> = followJson.deserialize()!! val follow: Follow<ArticleSimple> = followJson.deserialize()!!
follow.id.toString() `should equal` "bae81585-d985-4d7a-9b58-3a13e911688a" follow.id.toString() `should equal` "bae81585-d985-4d7a-9b58-3a13e911688a"
} }
} }

View File

@@ -1,7 +1,4 @@
import fr.dcproject.entity.Article import fr.dcproject.entity.*
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.User
import fr.dcproject.entity.Vote
import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.deserialize
import fr.postgresjson.serializer.serialize import fr.postgresjson.serializer.serialize
import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -40,11 +37,12 @@ class VoteTest {
"blocked_at": null, "blocked_at": null,
"plain_password": "azerty", "plain_password": "azerty",
"roles": [], "roles": [],
"created_at": null, "created_at": "2019-10-01T10:59:40.570Z",
"updated_at": null "updated_at": "2019-10-01T10:59:40.570Z"
}, },
"deleted": false, "deleted": false,
"created_at": null, "created_at": "2019-10-01T10:59:40.570Z",
"updated_at": "2019-10-01T10:59:40.570Z",
"deleted_at": null "deleted_at": null
}, },
"target": { "target": {
@@ -90,20 +88,20 @@ class VoteTest {
"version_id": "48dad61e-c54b-4f4c-9f66-428f90b94045", "version_id": "48dad61e-c54b-4f4c-9f66-428f90b94045",
"version_number": null, "version_number": null,
"deleted": false, "deleted": false,
"created_at": null, "created_at": "2019-10-01T10:59:40.570Z",
"deleted_at": null "deleted_at": "2019-10-01T10:59:40.570Z"
}, },
"note": -1, "note": -1,
"anonymous": true, "anonymous": true,
"updated_at": null, "updated_at": "2019-10-01T10:59:40.570Z",
"created_at": null "created_at": "2019-10-01T10:59:40.570Z"
}""".trimIndent() }""".trimIndent()
@Test @Test
fun `test Vote Article serialize`() { fun `test Vote Article serialize`() {
val user = User(username = "jaque", plainPassword = "azerty") val user = User(username = "jaque", plainPassword = "azerty")
val citizen = Citizen( val citizen = CitizenBasic(
name = Citizen.Name("Jaque", "Bono"), name = CitizenI.Name("Jaque", "Bono"),
email = "jaque.bono@gmail.com", email = "jaque.bono@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = user user = user

View File

@@ -1,6 +1,6 @@
package feature package feature
import fr.dcproject.entity.Citizen import fr.dcproject.entity.*
import fr.dcproject.repository.CommentArticle import fr.dcproject.repository.CommentArticle
import io.cucumber.java8.En import io.cucumber.java8.En
import org.joda.time.DateTime import org.joda.time.DateTime
@@ -18,7 +18,7 @@ class ArticleSteps : En, KoinTest {
init { init {
Given("I have article with id {string}") { id: String -> Given("I have article with id {string}") { id: String ->
var citizen = Citizen( var citizen = Citizen(
name = Citizen.Name("John", "Doe"), name = CitizenI.Name("John", "Doe"),
email = "john.doe@gmail.com", email = "john.doe@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = UserEntity(username = "john-doe", plainPassword = "azerty") user = UserEntity(username = "john-doe", plainPassword = "azerty")
@@ -55,7 +55,7 @@ class ArticleSteps : En, KoinTest {
Given("I have comment {string} on article {string}") { commentId: String, articleId: String -> Given("I have comment {string} on article {string}") { commentId: String, articleId: String ->
var citizen = Citizen( var citizen = Citizen(
name = Citizen.Name("John", "Doe"), name = CitizenI.Name("John", "Doe"),
email = "john.doe@gmail.com", email = "john.doe@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = UserEntity(username = "john-doe", plainPassword = "azerty") user = UserEntity(username = "john-doe", plainPassword = "azerty")
@@ -76,7 +76,7 @@ class ArticleSteps : En, KoinTest {
) )
get<ArticleRepository>().upsert(article) get<ArticleRepository>().upsert(article)
val comment = CommentEntity( val comment: CommentEntity<ArticleRef> = CommentEntity(
id = UUID.fromString(commentId), id = UUID.fromString(commentId),
createdBy = citizen, createdBy = citizen,
target = article, target = article,

View File

@@ -1,14 +1,14 @@
package feature package feature
import fr.dcproject.entity.Citizen import fr.dcproject.entity.*
import fr.dcproject.entity.ConstitutionSimple.TitleSimple
import fr.dcproject.entity.request.Constitution
import io.cucumber.java8.En import io.cucumber.java8.En
import org.joda.time.DateTime import org.joda.time.DateTime
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.* import java.util.*
import java.util.concurrent.CompletionException import java.util.concurrent.CompletionException
import fr.dcproject.entity.Constitution as ConstitutionEntity
import fr.dcproject.entity.Constitution.Title as TitleEntity
import fr.dcproject.entity.User as UserEntity import fr.dcproject.entity.User as UserEntity
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository import fr.dcproject.repository.Constitution as ConstitutionRepository
@@ -17,7 +17,7 @@ class ConstitutionSteps : En, KoinTest {
init { init {
Given("I have constitution with id {string}") { id: String -> Given("I have constitution with id {string}") { id: String ->
var citizen = Citizen( var citizen = Citizen(
name = Citizen.Name("John", "Doe"), name = CitizenI.Name("John", "Doe"),
email = "john.doe@gmail.com", email = "john.doe@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = UserEntity(username = "john-doe", plainPassword = "azerty") user = UserEntity(username = "john-doe", plainPassword = "azerty")
@@ -29,33 +29,31 @@ class ConstitutionSteps : En, KoinTest {
citizen = get<CitizenRepository>().findByUsername("john-doe")!! citizen = get<CitizenRepository>().findByUsername("john-doe")!!
} }
val title1 = TitleEntity( val title1 = Constitution.Title(
name = "My Title" name = "My Title"
) )
val constitution = ConstitutionEntity( val constitution = Constitution(
id = UUID.fromString(id),
title = "hello", title = "hello",
titles = listOf(title1), titles = mutableListOf(title1),
createdBy = citizen,
anonymous = false anonymous = false
) )
get<ConstitutionRepository>().upsert(constitution) get<ConstitutionRepository>().upsert(constitution.create(citizen))
} }
Given("I have constitution with id {string} created by {string}") { id: String, username: String -> Given("I have constitution with id {string} created by {string}") { id: String, username: String ->
val citizen = get<CitizenRepository>().findByUsername(username)!! val citizen = get<CitizenRepository>().findByUsername(username)!!
val title1 = TitleEntity( val title1 = TitleSimple<ArticleRef>(
name = "My Title" name = "My Title"
) )
val constitution = ConstitutionEntity( val constitution = ConstitutionSimple<CitizenSimple, TitleSimple<ArticleRef>>(
id = UUID.fromString(id), id = UUID.fromString(id),
title = "hello", title = "hello",
titles = listOf(title1), titles = mutableListOf(title1),
createdBy = citizen, anonymous = false,
anonymous = false createdBy = citizen
) )
get<ConstitutionRepository>().upsert(constitution) get<ConstitutionRepository>().upsert(constitution)
} }

View File

@@ -3,6 +3,7 @@ package feature
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import fr.dcproject.JwtConfig import fr.dcproject.JwtConfig
import fr.dcproject.entity.Citizen import fr.dcproject.entity.Citizen
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.User import fr.dcproject.entity.User
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
@@ -29,7 +30,7 @@ class KtorServerAuthSteps : En, KoinTest {
val data = body.asMap<String, String>(String::class.java, String::class.java) val data = body.asMap<String, String>(String::class.java, String::class.java)
val citizen = Citizen( val citizen = Citizen(
id = UUID.fromString(data["id"]), id = UUID.fromString(data["id"]),
name = Citizen.Name(data["firstName"], data["lastName"]), name = CitizenI.Name(data["firstName"]!!, data["lastName"]!!),
email = data["email"] ?: ((data["firstName"] + "-" + data["lastName"]).toLowerCase()) + "@dc-project.com", email = data["email"] ?: ((data["firstName"] + "-" + data["lastName"]).toLowerCase()) + "@dc-project.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = user user = user
@@ -52,7 +53,7 @@ class KtorServerAuthSteps : En, KoinTest {
) )
val citizen = Citizen( val citizen = Citizen(
id = UUID.fromString(id), id = UUID.fromString(id),
name = Citizen.Name(firstName, lastName), name = CitizenI.Name(firstName, lastName),
email = ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr", email = ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr",
birthday = DateTime.now(), birthday = DateTime.now(),
user = user user = user
@@ -77,7 +78,7 @@ class KtorServerAuthSteps : En, KoinTest {
) )
val citizen = Citizen( val citizen = Citizen(
id = UUID.fromString(id), id = UUID.fromString(id),
name = Citizen.Name(firstName, lastName), name = CitizenI.Name(firstName, lastName),
email = "$firstName-$lastName".toLowerCase() + "@gmail.com", email = "$firstName-$lastName".toLowerCase() + "@gmail.com",
birthday = DateTime.now(), birthday = DateTime.now(),
user = user, user = user,

View File

@@ -1,9 +1,9 @@
Feature: comment Constitution Feature: comment Constitution
Scenario: Can comment an constitution Scenario: Can comment an constitution
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I am authenticated as John Toe with id "a6eb1f5a-8c02-42f4-8e8e-a722f26841ef"
And I have constitution with id "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" And I have constitution with id "d7e20f0b-3fdd-4638-817a-bbd87054eb82" created by "john-toe"
When I send a POST request to "/constitutions/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/comments" with body: When I send a POST request to "/constitutions/d7e20f0b-3fdd-4638-817a-bbd87054eb82/comments" with body:
""" """
Hello mister Hello mister
""" """

View File

@@ -32,7 +32,6 @@ Feature: constitution routes
"id":"64b7b379-2298-43ec-b428-ba134930cabd" "id":"64b7b379-2298-43ec-b428-ba134930cabd"
}, },
"created_at":null, "created_at":null,
"version_id":"3311a7af-2a62-4e31-b4cd-889f8ead9737",
"version_number":null "version_number":null
} }
""" """

View File

@@ -37,7 +37,7 @@ Feature: vote Article
And the response should contain object: And the response should contain object:
| [0].note | 1 | | [0].note | 1 |
Scenario: Can vote a comment on article Scenario: Can vote a comment
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
And I have comment "ea5c9e87-c99e-4646-a381-2910219e077f" on article "cc9c624e-a27e-42de-af78-ae821c657a68" And I have comment "ea5c9e87-c99e-4646-a381-2910219e077f" on article "cc9c624e-a27e-42de-af78-ae821c657a68"
When I send a PUT request to "/comments/ea5c9e87-c99e-4646-a381-2910219e077f/vote" with body: When I send a PUT request to "/comments/ea5c9e87-c99e-4646-a381-2910219e077f/vote" with body: