Big refactoring #77

Merged
flecomte merged 166 commits from refactoring-component-and-immutable into master 2021-03-24 19:06:07 +01:00
69 changed files with 152 additions and 117 deletions
Showing only changes of commit a79e1ec086 - Show all commits

View File

@@ -26,7 +26,8 @@ plugins {
maven
id("maven-publish")
id("org.jetbrains.kotlin.jvm") version "1.3.50"
kotlin("jvm") version "1.4.21"
kotlin("plugin.serialization") version "1.4.21"
id("com.github.johnrengelman.shadow") version "5.2.0"
id("org.jlleitschuh.gradle.ktlint") version "8.2.0"
@@ -123,6 +124,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1")
implementation("io.ktor:ktor-server-jetty:$ktor_version")
implementation("io.ktor:ktor-client-jetty:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
@@ -139,8 +141,8 @@ dependencies {
implementation("com.auth0:java-jwt:3.12.0")
implementation("com.github.jasync-sql:jasync-postgresql:1.1.6")
implementation("com.github.flecomte:postgres-json:2.0.0")
implementation("com.sendgrid:sendgrid-java:4.4.1")
implementation("io.lettuce:lettuce-core:5.3.6.RELEASE") //TODO update to 6.0.2
implementation("com.sendgrid:sendgrid-java:4.7.1")
implementation("io.lettuce:lettuce-core:5.3.6.RELEASE") // TODO update to 6.0.2
implementation("com.rabbitmq:amqp-client:5.10.0")
implementation("org.elasticsearch.client:elasticsearch-rest-client:6.7.1")
implementation("com.jayway.jsonpath:json-path:2.5.0")

View File

@@ -1,6 +1,6 @@
ktor_version=1.2.2
ktor_version=1.5.0
kotlin.code.style=official
kotlin_version=1.3.40
kotlin_version=1.4.21-2
coroutinesVersion=1.4.2
logback_version=1.2.3
koinVersion=2.0.1

View File

@@ -49,6 +49,7 @@ import io.ktor.client.*
import io.ktor.client.engine.jetty.Jetty
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.http.cio.websocket.*
import io.ktor.jackson.*
import io.ktor.locations.*
import io.ktor.response.*
@@ -117,10 +118,12 @@ fun Application.module(env: Env = PROD) {
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
})
setDefaultPrettyPrinter(
DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
}
)
}
}

View File

@@ -123,4 +123,4 @@ val converters: ConverterDeclaration = {
?: throw NotFoundException("Workgroup $values not found")
}
}
}
}

View File

@@ -1,10 +1,10 @@
package fr.dcproject.component.article
import fr.dcproject.component.citizen.*
import fr.dcproject.component.workgroup.WorkgroupCart
import fr.dcproject.component.workgroup.WorkgroupCartI
import fr.dcproject.component.workgroup.WorkgroupRef
import fr.dcproject.component.workgroup.WorkgroupSimple
import fr.dcproject.component.citizen.*
import fr.dcproject.entity.*
import fr.postgresjson.entity.*
import org.joda.time.DateTime
@@ -128,4 +128,4 @@ interface ArticleAuthI<U : CitizenI> :
CreatedBy<U>,
EntityDeletedAt {
val draft: Boolean
}
}

View File

@@ -30,7 +30,8 @@ class ArticleRepository(override var requester: Requester) : RepositoryI {
return requester
.getFunction("find_articles")
.select(
page, limit,
page,
limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search,
@@ -48,4 +49,4 @@ class ArticleRepository(override var requester: Requester) : RepositoryI {
val createdById: String? = null,
val workgroupId: String? = null
) : Parameter
}
}

View File

@@ -1,11 +1,11 @@
package fr.dcproject.component.article
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.views.ViewManager
import fr.dcproject.entity.ViewAggregation
import fr.dcproject.utils.contentToString
import fr.dcproject.utils.getJsonField
import fr.dcproject.utils.toIso
import fr.dcproject.component.views.ViewManager
import org.elasticsearch.client.Request
import org.elasticsearch.client.Response
import org.elasticsearch.client.RestClient
@@ -27,7 +27,8 @@ class ArticleViewManager(private val restClient: RestClient) : ViewManager<Artic
"/views/_doc/"
).apply {
//language=JSON
setJsonEntity("""
setJsonEntity(
"""
{
"logged": $isLogged,
"type": "article",
@@ -38,7 +39,8 @@ class ArticleViewManager(private val restClient: RestClient) : ViewManager<Artic
"citizen_id": "${citizen?.id}",
"view_at": "${dateTime.toIso()}"
}
""".trimIndent())
""".trimIndent()
)
}
return restClient.performRequest(request)
@@ -53,7 +55,8 @@ class ArticleViewManager(private val restClient: RestClient) : ViewManager<Artic
"/views/_search"
).apply {
//language=JSON
setJsonEntity("""
setJsonEntity(
"""
{
"size": 0,
"query": {
@@ -81,7 +84,8 @@ class ArticleViewManager(private val restClient: RestClient) : ViewManager<Artic
}
}
}
""".trimIndent())
""".trimIndent()
)
}
return restClient
@@ -92,4 +96,4 @@ class ArticleViewManager(private val restClient: RestClient) : ViewManager<Artic
)
}
}
}
}

View File

@@ -33,10 +33,10 @@ class ArticleVoter(private val articleRepo: ArticleRepository) : Voter() {
/* The new Article must by created by the same citizen of the connected citizen */
if (subject.createdBy.id == citizen.id) {
/* The creator must be the same of the creator of preview version of article */
val lastVersionId = articleRepo
.findVersionsByVersionId(1, 1, subject.versionId)
.result
.firstOrNull()?.createdBy?.id
val lastVersionId = articleRepo
.findVersionsByVersionId(1, 1, subject.versionId)
.result
.firstOrNull()?.createdBy?.id
return when (lastVersionId) {
null -> granted("You can create a new Article")
@@ -46,4 +46,4 @@ class ArticleVoter(private val articleRepo: ArticleRepository) : Voter() {
}
return denied("This article must be yours for update it", "article.update.notYours")
}
}
}

View File

@@ -65,4 +65,4 @@ fun Route.getOneArticle(viewManager: ArticleViewManager, voter: ArticleVoter) {
viewManager.addView(call.request.local.remoteHost, it.article, citizenOrNull)
}
}
}
}

View File

@@ -54,4 +54,4 @@ class PasswordlessAuth(
}
private fun noEmail(email: String): Nothing = throw EmailNotFound(email)
}
}

View File

@@ -12,7 +12,8 @@ class User(
blockedAt: DateTime? = null,
override var plainPassword: String? = null,
override var roles: List<Roles> = emptyList()
) : UserFull, UserBasic(id, username, blockedAt),
) : UserFull,
UserBasic(id, username, blockedAt),
EntityCreatedAt by EntityCreatedAtImp(),
EntityUpdatedAt by EntityUpdatedAtImp()

View File

@@ -12,4 +12,4 @@ fun UserI.makeToken(): String = JWT.create()
.withIssuer(JwtConfig.issuer)
.withClaim("id", id.toString())
.withExpiresAt(JwtConfig.getExpiration())
.sign(JwtConfig.algorithm)
.sign(JwtConfig.algorithm)

View File

@@ -22,4 +22,4 @@ object JwtConfig {
* Calculate the expiration Date based on current time + the given validity
*/
fun getExpiration() = Date(System.currentTimeMillis() + validityInMs)
}
}

View File

@@ -40,4 +40,4 @@ fun jwtInstallation(userRepo: UserRepository): Authentication.Configuration.() -
}
}
}
}
}

View File

@@ -33,7 +33,8 @@ class CitizenRepository(override var requester: Requester) : RepositoryI {
): Paginated<CitizenBasic> = requester
.getFunction("find_citizens")
.select(
page, limit,
page,
limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search

View File

@@ -23,4 +23,4 @@ class CitizenVoter : Voter() {
if (connectedCitizen == null) return denied("You must be connected to change your password", "citizen.changePassword.notConnected")
return if (subject.id == connectedCitizen.id) granted() else denied("You can only change your password", "citizen.password.notYours")
}
}
}

View File

@@ -42,4 +42,4 @@ fun Route.changeMyPassword(voter: CitizenVoter, userRepository: UserRepository)
call.respond(HttpStatusCode.BadRequest, "Request format is not correct")
}
}
}
}

View File

@@ -30,4 +30,4 @@ fun Route.findCitizen(voter: CitizenVoter, repo: CitizenRepository) {
voter.assert { canView(citizens.result, citizenOrNull) }
call.respond(citizens)
}
}
}

View File

@@ -25,4 +25,4 @@ fun Route.getCurrentCitizen(voter: CitizenVoter) {
call.respond(citizen)
}
}
}
}

View File

@@ -20,4 +20,4 @@ fun Route.getOneCitizen(voter: CitizenVoter) {
call.respond(it.citizen)
}
}
}

View File

@@ -27,7 +27,8 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
page,
limit,
"created_by_id" to citizen.id,
"reference" to TargetI.getReference(ArticleRef::class)
)
@@ -42,7 +43,8 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
): Paginated<CommentForView<ArticleForView, CitizenRef>> = requester
.getFunction("find_comments_by_target")
.select(
page, limit,
page,
limit,
"target_id" to target.id,
"sort" to sort.sql
)
@@ -57,4 +59,4 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
}
}
}
}
}

View File

@@ -41,4 +41,4 @@ fun Route.createCommentArticle(repo: CommentArticleRepository, voter: CommentVot
call.respond(HttpStatusCode.Created, comment)
}
}
}
}

View File

@@ -22,4 +22,4 @@ fun Route.getCitizenArticleComments(repo: CommentArticleRepository, voter: Comme
call.respond(comments)
}
}
}
}

View File

@@ -36,7 +36,8 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
return requester.run {
getFunction("find_comments_by_parent")
.select(
page, limit,
page,
limit,
"parent_id" to parentId
)
}
@@ -60,7 +61,8 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
return requester.run {
getFunction("find_comments_by_target")
.select(
page, limit,
page,
limit,
"target_id" to targetId,
"sort" to sort.sql
)
@@ -101,7 +103,8 @@ class CommentRepository(requester: Requester) : CommentRepositoryAbs<TargetRef>(
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
page,
limit,
"created_by_id" to citizen.id
)
}
@@ -115,7 +118,8 @@ class CommentRepository(requester: Requester) : CommentRepositoryAbs<TargetRef>(
return requester.run {
getFunction("find_comments_by_parent")
.select(
page, limit,
page,
limit,
"parent_id" to parentId
)
}

View File

@@ -30,4 +30,4 @@ fun Route.editComment(repo: CommentRepository, voter: CommentVoter) {
call.respond(HttpStatusCode.OK, comment)
}
}
}

View File

@@ -39,4 +39,4 @@ fun Route.getChildrenComments(repo: CommentRepository, voter: CommentVoter) {
call.respond(HttpStatusCode.OK, comments)
}
}
}

View File

@@ -26,4 +26,4 @@ fun Route.getOneComment(repo: CommentRepository, voter: CommentVoter) {
call.respond(HttpStatusCode.OK, comment)
}
}
}

View File

@@ -9,7 +9,7 @@ object ConfigViews {
private fun waitElasticsearchIsUp(client: RestClient) {
val logger: Logger = LoggerFactory.getLogger("fr.dcproject.elasticsearch")
val request = Request("GET", "/_cluster/health")
repeat(5*60 / 2) { // 5 minutes
repeat(5 * 60 / 2) { // 5 minutes
runCatching {
client.performRequest(request).statusLine.statusCode
}.onSuccess {
@@ -74,7 +74,7 @@ object ConfigViews {
}
}
}
""".trimIndent()
""".trimIndent()
)
}.let {
performRequest(it)

View File

@@ -15,4 +15,4 @@ interface ViewManager <T> {
* Get Views aggregations
*/
fun getViewsCount(entity: T): ViewAggregation
}
}

View File

@@ -104,4 +104,4 @@ fun <Z : CitizenWithUserI> List<Member<Z>>.getRoles(user: UserI): List<Role> =
fun <Z : CitizenWithUserI> List<Member<Z>>.getRoles(citizen: CitizenI): List<Role> =
firstOrNull { it.citizen.id == citizen.id }?.roles ?: emptyList()
interface WorkgroupI : UuidEntityI
interface WorkgroupI : UuidEntityI

View File

@@ -30,7 +30,8 @@ class WorkgroupRepository(override var requester: Requester) : RepositoryI {
return requester
.getFunction("find_workgroups")
.select(
page, limit,
page,
limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search,
@@ -43,8 +44,8 @@ class WorkgroupRepository(override var requester: Requester) : RepositoryI {
.selectOne("resource" to workgroup) ?: error("query 'upsert_workgroup' return null")
fun <W : WorkgroupRef> delete(workgroup: W) = requester
.getFunction("delete_workgroup")
.perform("id" to workgroup.id)
.getFunction("delete_workgroup")
.perform("id" to workgroup.id)
fun addMember(workgroup: WorkgroupI, member: Member<CitizenI>): Member<CitizenBasic>? =
addMember(workgroup, member.citizen, member.roles)

View File

@@ -32,7 +32,12 @@ object GetWorkgroups {
fun Route.getWorkgroups(repo: WorkgroupRepository, voter: WorkgroupVoter) {
get<WorkgroupsRequest> {
val workgroups =
repo.find(it.page, it.limit, it.sort, it.direction, it.search,
repo.find(
it.page,
it.limit,
it.sort,
it.direction,
it.search,
WorkgroupRepository.Filter(createdById = it.createdBy, members = it.members)
)
voter.assert { canView(workgroups.result, citizenOrNull) }

View File

@@ -41,10 +41,10 @@ object DeleteMembersOfWorkgroup {
repo.findById(it.workgroupId)?.let { workgroup ->
call.getMembersFromRequest().let { members ->
voter.assert { canView(workgroup, citizenOrNull) }
repo.removeMembers(workgroup, members)
}.let { members ->
call.respond(HttpStatusCode.OK, members)
}
repo.removeMembers(workgroup, members)
}.let { members ->
call.respond(HttpStatusCode.OK, members)
}
} ?: call.respond(HttpStatusCode.NotFound)
}
}

View File

@@ -41,10 +41,10 @@ object UpdateMemberOfWorkgroup {
repo.findById(it.workgroupId)?.let { workgroup ->
call.getMembersFromRequest().let { members ->
voter.assert { canUpdateMembers(workgroup, citizenOrNull) }
repo.updateMembers(workgroup, members)
}.let { members ->
call.respond(HttpStatusCode.OK, members)
}
repo.updateMembers(workgroup, members)
}.let { members ->
call.respond(HttpStatusCode.OK, members)
}
} ?: call.respond(HttpStatusCode.NotFound)
}
}

View File

@@ -8,4 +8,4 @@ interface Opinionable {
class Imp(parent: fr.dcproject.entity.Opinionable) : Opinionable {
override val opinions: Opinions = parent.opinions
}
}
}

View File

@@ -6,4 +6,4 @@ interface Votable {
class Imp(parent: fr.dcproject.entity.Votable) : Votable {
override val votes: VoteAggregation = VoteAggregation(parent)
}
}
}

View File

@@ -68,4 +68,4 @@ open class ConstitutionRef(id: UUID = UUID.randomUUID()) : ConstitutionS(id) {
) : UuidEntity(id)
}
sealed class ConstitutionS(id: UUID = UUID.randomUUID()) : TargetRef(id), TargetI
sealed class ConstitutionS(id: UUID = UUID.randomUUID()) : TargetRef(id), TargetI

View File

@@ -11,4 +11,4 @@ interface CreatedBy<T : CitizenI> : EntityCreatedBy<EntityI> {
override val createdBy: T
}
class CreatedByImp<T : CitizenI>(override val createdBy: T) : CreatedBy<T>
class CreatedByImp<T : CitizenI>(override val createdBy: T) : CreatedBy<T>

View File

@@ -5,4 +5,4 @@ import java.util.*
interface EntityI : EntityI {
val id: UUID
}
}

View File

@@ -61,4 +61,4 @@ interface TargetI : UuidEntityI {
}
val reference: String
}
}

View File

@@ -36,4 +36,4 @@ open class FollowRef(
override val id: UUID
) : FollowI
interface FollowI : UuidEntityI
interface FollowI : UuidEntityI

View File

@@ -43,4 +43,4 @@ open class OpinionRef(
override val id: UUID
) : OpinionI, TargetRef(id)
interface OpinionI : UuidEntityI
interface OpinionI : UuidEntityI

View File

@@ -13,4 +13,4 @@ class OpinionChoice(
open class OpinionChoiceRef(
id: UUID?
) : UuidEntity(id ?: UUID.randomUUID())
) : UuidEntity(id ?: UUID.randomUUID())

View File

@@ -9,4 +9,4 @@ interface Opinionable {
class OpinionableImp : Opinionable {
override var opinions: OpinionsMutable = mutableMapOf()
}
}

View File

@@ -14,4 +14,4 @@ class VersionableRefImp(
interface Versionable : VersionableRef, EntityVersioning<UUID, Int> {
override val versionId: UUID
override val versionNumber: Int
}
}

View File

@@ -6,4 +6,4 @@ interface Votable {
class VotableImp : Votable {
override val votes: VoteAggregation = VoteAggregation()
}
}

View File

@@ -13,12 +13,12 @@ import fr.dcproject.repository.Follow
import fr.postgresjson.serializer.deserialize
import io.ktor.application.*
import io.ktor.util.pipeline.*
import io.ktor.utils.io.errors.*
import io.lettuce.core.api.async.RedisAsyncCommands
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.io.errors.IOException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import fr.dcproject.repository.FollowArticle as FollowArticleRepository

View File

@@ -24,4 +24,4 @@ class Mailer(
throw ex
}
}
}
}

View File

@@ -67,4 +67,4 @@ class NotificationEmailSender(
private fun noCitizen(id: UUID): Nothing = throw NoCitizen("No Citizen with this id : $id")
private fun noTarget(id: UUID): Nothing = throw NoTarget("No Target with this id : $id")
}
}

View File

@@ -27,7 +27,8 @@ class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
page,
limit,
"created_by_id" to citizen.id,
"reference" to TargetI.getReference(ConstitutionRef::class)
)
@@ -43,7 +44,8 @@ class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs
return requester.run {
getFunction("find_comments_by_target")
.select(
page, limit,
page,
limit,
"target_id" to target.id,
"sort" to sort.sql
)

View File

@@ -27,7 +27,8 @@ class Constitution(override var requester: Requester) : RepositoryI {
return requester
.getFunction("find_constitutions")
.select(
page, limit,
page,
limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"search" to search

View File

@@ -34,7 +34,8 @@ sealed class Follow<IN : TargetRef, OUT : TargetRef>(override var requester: Req
return requester
.getFunction("find_follows_by_citizen")
.select(
page, limit,
page,
limit,
"created_by_id" to citizenId
)
}
@@ -101,7 +102,8 @@ class FollowArticle(requester: Requester) : Follow<ArticleRef, ArticleForView>(r
return requester.run {
getFunction("find_follows_article_by_citizen")
.select(
page, limit,
page,
limit,
"created_by_id" to citizenId
)
}
@@ -115,7 +117,8 @@ class FollowArticle(requester: Requester) : Follow<ArticleRef, ArticleForView>(r
return requester
.getFunction("find_follows_article_by_target")
.select(
page, limit,
page,
limit,
"target_id" to target.id
)
}
@@ -130,7 +133,8 @@ class FollowConstitution(requester: Requester) : Follow<ConstitutionRef, Constit
return requester.run {
getFunction("find_follows_constitution_by_citizen")
.select(
page, limit,
page,
limit,
"created_by_id" to citizenId
)
}

View File

@@ -82,7 +82,8 @@ abstract class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requ
return requester.run {
getFunction("find_citizen_opinions_by_target_ids")
.select(
typeReference, mapOf(
typeReference,
mapOf(
"citizen_id" to citizen.id,
"ids" to targets
)
@@ -101,7 +102,8 @@ abstract class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requ
return requester
.getFunction("find_citizen_opinions_by_target_id")
.select(
typeReference, mapOf(
typeReference,
mapOf(
"citizen_id" to citizen.id,
"id" to target
)
@@ -121,7 +123,9 @@ abstract class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requ
): Paginated<OpinionEntity<TargetRef>> {
return requester
.getFunction("find_citizen_opinions")
.select(page, limit,
.select(
page,
limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
"citizen_id" to citizen.id
@@ -153,4 +157,4 @@ class OpinionArticle(requester: Requester) : Opinion<ArticleRef>(requester) {
.getFunction("upsert_opinion")
.selectOne("resource" to opinion)!!
}
}
}

View File

@@ -37,7 +37,10 @@ open class Vote<T : TargetI>(override var requester: Requester) : RepositoryI {
return requester.run {
getFunction("find_votes_by_citizen")
.select(
page, limit, typeReference, mapOf(
page,
limit,
typeReference,
mapOf(
"created_by_id" to citizenId,
"reference" to target
)
@@ -53,7 +56,8 @@ open class Vote<T : TargetI>(override var requester: Requester) : RepositoryI {
return requester.run {
getFunction("find_citizen_votes_by_target_ids")
.select(
typeReference, mapOf(
typeReference,
mapOf(
"citizen_id" to citizen.id,
"ids" to targets
)
@@ -120,4 +124,4 @@ class VoteConstitution(requester: Requester) : Vote<Constitution>(requester) {
page,
limit
)
}
}

View File

@@ -50,4 +50,4 @@ fun Route.commentConstitution(repo: CommentConstitutionRepository, voter: Commen
voter.assert { canView(comments.result, citizenOrNull) }
call.respond(comments)
}
}
}

View File

@@ -60,7 +60,10 @@ object ConstitutionPaths {
) : UuidEntity(id) {
fun create(): TitleSimple<ArticleRef> =
TitleSimple(
id, name, rank, articles
id,
name,
rank,
articles
)
}
@@ -100,4 +103,4 @@ fun Route.constitution(repo: ConstitutionRepository, voter: ConstitutionVoter) {
call.respond(constitution)
}
}
}
}

View File

@@ -53,4 +53,4 @@ fun Route.followArticle(repo: FollowArticleRepository, voter: FollowVoter) {
}
call.respond(follows)
}
}
}

View File

@@ -33,4 +33,4 @@ fun Route.opinionChoice(repo: OpinionChoiceRepository, voter: OpinionChoiceVoter
call.respond(opinionChoices)
}
}
}

View File

@@ -11,4 +11,4 @@ open class PaginatedRequest(
) : PaginatedRequestI {
override val page: Int = if (page < 1) 1 else page
override val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
}
}

View File

@@ -88,4 +88,4 @@ fun Route.voteArticle(repo: VoteArticleRepository, voteCommentRepo: VoteComment,
}
call.respond(votes)
}
}
}

View File

@@ -40,4 +40,4 @@ fun Route.voteConstitution(repo: VoteConstitutionRepository, voter: VoteVoter) {
repo.vote(vote)
call.respond(HttpStatusCode.Created)
}
}
}

View File

@@ -3,4 +3,4 @@ package fr.dcproject.utils
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
fun DateTime.toIso(): String = ISODateTimeFormat.dateTime().print(this)
fun DateTime.toIso(): String = ISODateTimeFormat.dateTime().print(this)

View File

@@ -26,4 +26,4 @@ fun String.getJsonField(jsonPath: String): Int? {
.warn("No value for Json path ${JsonPath.compile(jsonPath).path}")
null
}
}
}

View File

@@ -7,4 +7,4 @@ import kotlin.reflect.KProperty
internal class LoggerDelegate<in R : Any> : ReadOnlyProperty<R, Logger> {
override fun getValue(thisRef: R, property: KProperty<*>): Logger = LoggerFactory.getLogger(thisRef.javaClass.packageName)
}
}

View File

@@ -4,4 +4,4 @@ fun String.readResource(callback: (String) -> Unit = {}): String {
val content = callback::class.java.getResource(this).readText()
callback(content)
return content
}
}

View File

@@ -8,4 +8,4 @@ fun List<String?>.toUUID(): List<UUID> = this
.filterNotNull()
.map { it.trim() }
.filter { it.isNotBlank() }
.map { UUID.fromString(it) }
.map { UUID.fromString(it) }

View File

@@ -1,6 +1,7 @@
package steps
import io.ktor.application.*
import io.ktor.server.engine.*
import io.ktor.server.testing.*
import java.util.concurrent.TimeUnit
import kotlin.test.fail

View File

@@ -6,11 +6,9 @@ import io.cucumber.java8.En
import io.ktor.http.*
import io.ktor.server.testing.*
import io.ktor.util.*
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
@ImplicitReflectionSerializer
@KtorExperimentalAPI
class KtorServerRequestSteps : En {
init {

View File

@@ -2,17 +2,11 @@ package steps
import io.cucumber.datatable.DataTable
import io.cucumber.java8.En
import kotlinx.serialization.ImplicitReflectionSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.parse
import kotlinx.serialization.json.*
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.fail
@ImplicitReflectionSerializer
class KtorServerRestSteps : En {
init {
Then("the JSON should contain:") { dataTable: DataTable ->
@@ -60,7 +54,7 @@ class KtorServerRestSteps : En {
}
private val responseJsonElement: JsonElement
get() = Json.parse(KtorServerContext.defaultServer.call?.response?.content ?: fail("The response isn't valid JSON"))
get() = Json.parseToJsonElement(KtorServerContext.defaultServer.call?.response?.content ?: fail("The response isn't valid JSON"))
private val response: String
get() = KtorServerContext.defaultServer.call?.response?.content ?: fail("The response isn't valid")