diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..84933ec
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+dcproject
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 9526a82..86360f0 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/All_Tests___Lint.xml b/.idea/runConfigurations/All_Tests___Lint.xml
index ae3a521..4ded737 100644
--- a/.idea/runConfigurations/All_Tests___Lint.xml
+++ b/.idea/runConfigurations/All_Tests___Lint.xml
@@ -1,14 +1,11 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Reset_DB___Run.xml b/.idea/runConfigurations/Reset_DB___Run.xml
index 422e230..d019b97 100644
--- a/.idea/runConfigurations/Reset_DB___Run.xml
+++ b/.idea/runConfigurations/Reset_DB___Run.xml
@@ -32,7 +32,7 @@
true
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Reset_database_schema_and_import_Fixtures.xml b/.idea/runConfigurations/Reset_database_schema_and_import_Fixtures.xml
new file mode 100644
index 0000000..f4113c6
--- /dev/null
+++ b/.idea/runConfigurations/Reset_database_schema_and_import_Fixtures.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Test_All_SQL.xml b/.idea/runConfigurations/Test_All_SQL.xml
index 8a79347..d5d1df9 100644
--- a/.idea/runConfigurations/Test_All_SQL.xml
+++ b/.idea/runConfigurations/Test_All_SQL.xml
@@ -38,7 +38,7 @@
-
+
@@ -52,9 +52,11 @@
+
-
+
+
diff --git a/gradle.properties b/gradle.properties
index 9786bee..79bd462 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,6 +5,6 @@ logback_version=1.2.1
postgresjson_version=0.1
koinVersion=2.0.1
jackson_version=2.9.9
-cucumber_version=4.7.1
+cucumber_version=5.1.3
systemProp.sonar.host.url=http://localhost:9000
systemProp.sonar.login=1196e8015c20035f1aa91e881b95ce9d6e879c8a
diff --git a/src/main/kotlin/fr/dcproject/Application.kt b/src/main/kotlin/fr/dcproject/Application.kt
index 6c7fbe7..7fda334 100644
--- a/src/main/kotlin/fr/dcproject/Application.kt
+++ b/src/main/kotlin/fr/dcproject/Application.kt
@@ -197,6 +197,7 @@ fun Application.module(env: Env = PROD) {
commentConstitution(get())
voteArticle(get(), get(), get())
voteConstitution(get())
+ opinionArticle(get())
opinionChoice(get())
definition()
}
@@ -215,6 +216,9 @@ fun Application.module(env: Env = PROD) {
exception { e ->
call.respond(HttpStatusCode.BadRequest, e.message!!)
}
+ exception {
+ call.respond(HttpStatusCode.Forbidden)
+ }
}
install(CORS) {
diff --git a/src/main/kotlin/fr/dcproject/entity/Comment.kt b/src/main/kotlin/fr/dcproject/entity/Comment.kt
index cbcb059..ec3362c 100644
--- a/src/main/kotlin/fr/dcproject/entity/Comment.kt
+++ b/src/main/kotlin/fr/dcproject/entity/Comment.kt
@@ -32,8 +32,6 @@ open class Comment(
target = parent.target,
content = content
)
-
- override val reference get() = TargetI.getReference(this)
}
open class CommentRef(id: UUID = UUID.randomUUID()) : CommentS(id)
diff --git a/src/main/kotlin/fr/dcproject/entity/Extra.kt b/src/main/kotlin/fr/dcproject/entity/Extra.kt
index 17b8bea..d80a472 100644
--- a/src/main/kotlin/fr/dcproject/entity/Extra.kt
+++ b/src/main/kotlin/fr/dcproject/entity/Extra.kt
@@ -6,7 +6,7 @@ import fr.postgresjson.entity.immutable.UuidEntity
import fr.postgresjson.entity.immutable.UuidEntityI
import java.util.*
import kotlin.reflect.KClass
-import kotlin.reflect.full.isSuperclassOf
+import kotlin.reflect.full.isSubclassOf
interface ExtraI :
UuidEntityI,
@@ -26,16 +26,18 @@ interface TargetI : UuidEntityI {
enum class TargetName(val targetReference: String) {
Article("article"),
Constitution("constitution"),
- Comment("comment")
+ Comment("comment"),
+ Opinion("opinion")
}
companion object {
fun getReference(t: KClass): 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")
+ t.isSubclassOf(ArticleRef::class) -> TargetName.Article.targetReference
+ t.isSubclassOf(ConstitutionRef::class) -> TargetName.Constitution.targetReference
+ t.isSubclassOf(CommentRef::class) -> TargetName.Comment.targetReference
+ t.isSubclassOf(Opinion::class) -> TargetName.Opinion.targetReference
+ else -> throw error("target not implemented: ${t.qualifiedName}")
}
}
diff --git a/src/main/kotlin/fr/dcproject/entity/Opinion.kt b/src/main/kotlin/fr/dcproject/entity/Opinion.kt
index cafaaf5..23fd3d6 100644
--- a/src/main/kotlin/fr/dcproject/entity/Opinion.kt
+++ b/src/main/kotlin/fr/dcproject/entity/Opinion.kt
@@ -1,6 +1,9 @@
package fr.dcproject.entity
-import fr.postgresjson.entity.immutable.*
+import fr.postgresjson.entity.immutable.EntityCreatedAt
+import fr.postgresjson.entity.immutable.EntityCreatedAtImp
+import fr.postgresjson.entity.immutable.EntityCreatedBy
+import fr.postgresjson.entity.immutable.EntityCreatedByImp
import java.util.*
open class Opinion(
@@ -9,11 +12,16 @@ open class Opinion(
override val target: T,
val choice: OpinionChoice
) : ExtraI,
- UuidEntity(id),
+ TargetRef(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy by EntityCreatedByImp(createdBy) {
fun getName(): String = choice.name
}
-typealias OpinionArticle = Opinion
\ No newline at end of file
+class OpinionArticle(
+ id: UUID = UUID.randomUUID(),
+ createdBy: CitizenBasic,
+ target: ArticleRef,
+ choice: OpinionChoice
+) : Opinion(id, createdBy, target, choice)
\ No newline at end of file
diff --git a/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt b/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt
index 735ef4e..94b671f 100644
--- a/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt
+++ b/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt
@@ -10,7 +10,7 @@ import java.util.*
class OpinionChoice(
id: UUID,
val name: String,
- val target: List
+ val target: List?
) : OpinionChoiceRef(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityDeletedAt by EntityDeletedAtImp()
diff --git a/src/main/kotlin/fr/dcproject/entity/Opinionable.kt b/src/main/kotlin/fr/dcproject/entity/Opinionable.kt
index 9bc42c4..ba84451 100644
--- a/src/main/kotlin/fr/dcproject/entity/Opinionable.kt
+++ b/src/main/kotlin/fr/dcproject/entity/Opinionable.kt
@@ -3,13 +3,13 @@ package fr.dcproject.entity
import fr.postgresjson.entity.EntityI
class OpinionAggregation(
- override val entries: Set> = emptySet()
-) : AbstractMap(), EntityI
+ private val underlying: MutableMap = mutableMapOf()
+) : MutableMap by underlying, EntityI
interface Opinionable {
- val opinions: MutableMap
+ var opinions: MutableMap
}
class OpinionableImp : Opinionable {
- override val opinions: MutableMap = mutableMapOf()
+ override var opinions: MutableMap = mutableMapOf()
}
\ No newline at end of file
diff --git a/src/main/kotlin/fr/dcproject/entity/request/ArticleOpinionRequest.kt b/src/main/kotlin/fr/dcproject/entity/request/ArticleOpinionRequest.kt
deleted file mode 100644
index 3b17678..0000000
--- a/src/main/kotlin/fr/dcproject/entity/request/ArticleOpinionRequest.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package fr.dcproject.entity.request
-
-import fr.dcproject.entity.Citizen
-import fr.dcproject.entity.OpinionArticle
-import fr.dcproject.entity.OpinionChoiceRef
-import fr.dcproject.entity.TargetRef
-import fr.dcproject.repository.Article
-import fr.dcproject.repository.OpinionChoice
-import fr.dcproject.utils.toUUID
-import org.koin.core.KoinComponent
-import org.koin.core.get
-
-class ArticleOpinionRequest(
- target: String,
- opinionChoice: String
-) : RequestBuilderWithCreator, KoinComponent {
- val target = TargetRef(target.toUUID())
- val opinionChoice = OpinionChoiceRef(opinionChoice.toUUID())
-
- override fun create(citizen: Citizen): OpinionArticle {
- return OpinionArticle(
- choice = get().findOpinionChoiceById(opinionChoice.id)!!,
- target = get().findById(target.id)!!,
- createdBy = citizen
- )
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/fr/dcproject/entity/request/Request.kt b/src/main/kotlin/fr/dcproject/entity/request/Request.kt
index 10d4282..d1e11cc 100644
--- a/src/main/kotlin/fr/dcproject/entity/request/Request.kt
+++ b/src/main/kotlin/fr/dcproject/entity/request/Request.kt
@@ -1,14 +1,11 @@
package fr.dcproject.entity.request
-import fr.dcproject.entity.CitizenRef
-import fr.postgresjson.entity.EntityI
+import io.ktor.application.ApplicationCall
interface Request
-interface RequestBuilder : Request {
- fun create(): E
+interface RequestBuilder {
+ suspend fun getContent(call: ApplicationCall): E
}
-interface RequestBuilderWithCreator : Request {
- fun create(citizen: C): E
-}
+suspend fun ApplicationCall.getContent(builder: RequestBuilder) = builder.getContent(this)
diff --git a/src/main/kotlin/fr/dcproject/repository/Opinion.kt b/src/main/kotlin/fr/dcproject/repository/Opinion.kt
index 791d31c..e777541 100644
--- a/src/main/kotlin/fr/dcproject/repository/Opinion.kt
+++ b/src/main/kotlin/fr/dcproject/repository/Opinion.kt
@@ -11,6 +11,7 @@ import net.pearx.kasechange.toSnakeCase
import java.util.*
import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.entity.Opinion as OpinionEntity
+import fr.dcproject.entity.OpinionArticle as OpinionArticleEntity
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity
open class OpinionChoice(override val requester: Requester) : RepositoryI {
@@ -25,6 +26,14 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
"targets" to targets
)
+ /**
+ * find opinion choices by name
+ */
+ fun findOpinionsChoiceByName(name: String): OpinionChoiceEntity? =
+ findOpinionsChoices().first {
+ it.name == name
+ }
+
/**
* find one opinion choices by id
*/
@@ -104,9 +113,18 @@ open class Opinion(requester: Requester) : OpinionChoice(requeste
.select(page, limit,
"sort" to sort?.toSnakeCase(),
"direction" to direction,
- "citizen" to citizen.id
+ "citizen_id" to citizen.id
)
}
}
-class OpinionArticle(requester: Requester) : Opinion(requester)
\ No newline at end of file
+class OpinionArticle(requester: Requester) : Opinion(requester) {
+ /**
+ * Create an Opinion on Article
+ */
+ fun opinion(opinion: OpinionArticleEntity): OpinionArticleEntity {
+ return requester
+ .getFunction("upsert_opinion")
+ .selectOne(opinion) ?: error("query 'upsert_opinion' return null")
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/fr/dcproject/routes/OpinionArticle.kt b/src/main/kotlin/fr/dcproject/routes/OpinionArticle.kt
index d9ddb53..c2f867c 100644
--- a/src/main/kotlin/fr/dcproject/routes/OpinionArticle.kt
+++ b/src/main/kotlin/fr/dcproject/routes/OpinionArticle.kt
@@ -1,11 +1,18 @@
package fr.dcproject.routes
import fr.dcproject.citizen
-import fr.dcproject.entity.request.ArticleOpinionRequest
+import fr.dcproject.entity.Citizen
+import fr.dcproject.entity.OpinionArticle
+import fr.dcproject.entity.OpinionChoiceRef
+import fr.dcproject.entity.request.RequestBuilder
+import fr.dcproject.entity.request.getContent
+import fr.dcproject.repository.OpinionChoice
import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
import fr.dcproject.security.voter.assertCan
import fr.dcproject.utils.toUUID
+import io.ktor.application.ApplicationCall
import io.ktor.application.call
+import io.ktor.features.BadRequestException
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
@@ -17,6 +24,7 @@ import io.ktor.routing.Route
import io.ktor.util.KtorExperimentalAPI
import org.koin.core.KoinComponent
import org.koin.core.get
+import java.util.*
import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Citizen as CitizenEntity
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
@@ -24,7 +32,7 @@ import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
@KtorExperimentalLocationsAPI
object OpinionArticlePaths {
/**
- * Get paginated opinion of citizen for one article
+ * Get paginated opinions of citizen for all articles
*/
@Location("/citizens/{citizen}/opinions/articles")
class CitizenOpinionArticleRequest(
@@ -36,16 +44,37 @@ object OpinionArticlePaths {
/**
* Put an opinion on one article
*/
- @Location("/articles/{article}/opinons")
- class ArticleOpinion(val article: ArticleEntity)
+ @Location("/articles/{article}/opinions")
+ @KtorExperimentalAPI
+ class ArticleOpinion(val article: ArticleEntity) : RequestBuilder {
+
+ private class Content(
+ opinionChoice: String
+ ) : KoinComponent {
+ val opinionChoice = OpinionChoiceRef(opinionChoice.toUUID())
+
+ fun create(citizen: Citizen, article: ArticleEntity): OpinionArticle {
+ return OpinionArticle(
+ choice = get().findOpinionChoiceById(opinionChoice.id) ?: throw BadRequestException("OpinionChoice not exist: id(${opinionChoice.id})"),
+ target = article,
+ createdBy = citizen
+ )
+ }
+ }
+
+ override suspend fun getContent(call: ApplicationCall): OpinionArticle {
+ return call.receive().create(call.citizen, article)
+ }
+ }
/**
* Get all Opinion of citizen on targets by target ids
*/
- @Location("/citizen/{citizen}/opinions")
- class CitizenOpinions(val citizen: CitizenEntity, id: List): KoinComponent {
+ @Location("/citizens/{citizen}/opinions")
+ class CitizenOpinions(val citizen: CitizenEntity, id: List) : KoinComponent {
+ val id: List = id.toUUID()
val opinionsEntities = get()
- .findCitizenOpinionsByTargets(citizen, id.toUUID())
+ .findCitizenOpinionsByTargets(citizen, this.id)
}
}
@@ -64,9 +93,12 @@ fun Route.opinionArticle(repo: OpinionArticleRepository) {
}
put {
- val optionArticle = call.receive().create(citizen)
- assertCan(VIEW, optionArticle)
-
- call.respond(HttpStatusCode.Created, optionArticle)
+ call.getContent(it)
+ .let { opinion ->
+ assertCan(VIEW, opinion)
+ repo.opinion(opinion)
+ }.let {
+ call.respond(HttpStatusCode.Created, it)
+ }
}
}
diff --git a/src/main/kotlin/fr/dcproject/routes/OpinionChoice.kt b/src/main/kotlin/fr/dcproject/routes/OpinionChoice.kt
index a1f096b..6e7232e 100644
--- a/src/main/kotlin/fr/dcproject/routes/OpinionChoice.kt
+++ b/src/main/kotlin/fr/dcproject/routes/OpinionChoice.kt
@@ -1,7 +1,7 @@
package fr.dcproject.routes
import fr.dcproject.entity.OpinionChoice
-import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
+import fr.dcproject.security.voter.OpinionChoiceVoter.Action.VIEW
import fr.dcproject.security.voter.assertCan
import io.ktor.application.call
import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -17,7 +17,7 @@ object OpinionChoicePaths {
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
@Location("/opinions")
- class OpinionChoicesRequest(val targets: List)
+ class OpinionChoicesRequest(val targets: List = emptyList())
}
@KtorExperimentalLocationsAPI
diff --git a/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt b/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt
index 689f4a2..bf9653f 100644
--- a/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt
+++ b/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt
@@ -37,9 +37,10 @@ class OpinionVoter : Voter {
}
if (action == Action.DELETE) {
- return if (subject is Opinion<*>
- && user != null
- && subject.createdBy.user.id == user.id)
+ return if (subject is Opinion<*> &&
+ user != null &&
+ subject.createdBy.user.id == user.id
+ )
Vote.GRANTED
else Vote.DENIED
}
diff --git a/src/main/resources/openApi.yaml b/src/main/resources/openapi.yaml
similarity index 86%
rename from src/main/resources/openApi.yaml
rename to src/main/resources/openapi.yaml
index aea5f0c..4e904f7 100644
--- a/src/main/resources/openApi.yaml
+++ b/src/main/resources/openapi.yaml
@@ -650,6 +650,123 @@ paths:
401:
$ref: '#/components/responses/401'
+ /articles/{article}/opinions:
+ parameters:
+ - $ref: '#/components/parameters/article'
+ put:
+ security:
+ - JWTAuth: []
+ summary: Add Opinion on one article
+ tags:
+ - opinion
+ - article
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ArticleOpinionRequest'
+ responses:
+ 201:
+ description: Return the opinion
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Opinion'
+ 401:
+ $ref: '#/components/responses/401'
+ /opinions:
+ get:
+ summary: Get all opinions choices
+ tags:
+ - opinion
+ parameters:
+ - in: query
+ required: false
+ name: targets
+ description: opinion available for defined target
+ example:
+ - article
+ schema:
+ type: array
+ items:
+ type: string
+ responses:
+ 200:
+ description: return
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/OpinionChoices'
+ /opinions/{opinion}:
+ parameters:
+ - $ref: '#/components/parameters/opinion'
+ get:
+ summary: Get one opinion Choices
+ tags:
+ - opinion
+ responses:
+ 200:
+ description: return
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/OpinionChoice'
+ /citizens/{citizen}/opinions:
+ parameters:
+ - $ref: '#/components/parameters/citizen'
+ get:
+ security:
+ - JWTAuth: []
+ summary: Get all opinions of citizen filtered by target ids
+ tags:
+ - opinion
+ - citizen
+ parameters:
+ - in: query
+ required: true
+ name: id
+ description: target ids
+ example:
+ - 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b
+ schema:
+ type: array
+ items:
+ type: string
+ format: uuid
+ responses:
+ 200:
+ description: Opinions
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Opinion'
+ /citizens/{citizen}/opinions/articles:
+ parameters:
+ - $ref: '#/components/parameters/citizen'
+ get:
+ security:
+ - JWTAuth: []
+ summary: Get all opinions of one citizen
+ tags:
+ - opinion
+ - citizen
+ responses:
+ 200:
+ description: Opinions
+ content:
+ application/json:
+ schema:
+ allOf:
+ - $ref: '#/components/schemas/Paginated'
+ - type: object
+ properties:
+ result:
+ type: array
+ items:
+ $ref: '#/components/schemas/Opinion'
+
/citizens/{citizen}/votes/articles:
parameters:
@@ -789,7 +906,7 @@ components:
name: citizen
in: path
description: ID of citizen
- example: 4d673bfa-eaef-4290-b52f-85a9c8a7eba5
+ example: 6434f4f9-f570-f22a-c134-8668350651ff
required: true
schema:
type: string
@@ -804,6 +921,15 @@ components:
schema:
type: string
format: uuid
+ opinion:
+ in: path
+ required: true
+ name: opinion
+ description: Opinion ID
+ example: 6e978eb5-3c48-0def-b093-e01f43983adb
+ schema:
+ type: string
+ format: uuid
constitution:
name: constitution
@@ -861,6 +987,13 @@ components:
updated_at:
type: string
format: 'date-time'
+ DeletedAt:
+ properties:
+ deleted_at:
+ type: string
+ format: 'date-time'
+ deleted:
+ type: boolean
versionId:
properties:
@@ -1221,7 +1354,70 @@ components:
Opinion1: 1
Opinion2: 55
+ ArticleOpinionRequest:
+ type: object
+ properties:
+ opinion_choice:
+ type: string
+ format: uuid
+ example: 6e978eb5-3c48-0def-b093-e01f43983adb
+ OpinionChoices:
+ description: Opinion Choice
+ type: array
+ items:
+ $ref: '#/components/schemas/OpinionChoice'
+
+ OpinionChoice:
+ description: Opinion Choice
+ allOf:
+ - type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ name:
+ type: string
+ example: opinion1
+ target:
+ type: array
+ required: false
+ nullable: true
+ items:
+ type: string
+ description: the name of the target
+ - $ref: '#/components/schemas/CreatedAt'
+ - $ref: '#/components/schemas/DeletedAt'
+
+ Opinion:
+ description: Opinion
+ allOf:
+ - type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ name:
+ type: string
+ example: opinion1
+ target:
+ type: object
+ properties:
+ id:
+ type: string
+ format: uuid
+ reference:
+ type: string
+ example: article
+ choice:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/OpinionChoice'
+ reference:
+ type: string
+ example: opinion_on_article
+ - $ref: '#/components/schemas/CreatedBy'
+ - $ref: '#/components/schemas/CreatedAt'
@@ -1243,11 +1439,7 @@ components:
required: true
servers:
- - description: localhost 80
- url: http://localhost
- - description: localhost 8080
+ - description: localhost
url: http://localhost:8080
- description: production
- url: http://dc-project.fr
- - description: SwaggerHub API Auto Mocking
- url: https://virtserver.swaggerhub.com/flecomte/dc-project/0.1
\ No newline at end of file
+ url: http://dc-project.fr
\ No newline at end of file
diff --git a/src/main/resources/sql/fixtures/09-opinion.sql b/src/main/resources/sql/fixtures/09-opinion.sql
index 4cde828..7791ea5 100644
--- a/src/main/resources/sql/fixtures/09-opinion.sql
+++ b/src/main/resources/sql/fixtures/09-opinion.sql
@@ -7,7 +7,10 @@ begin
delete from opinion_choice;
insert into opinion_choice (id, name, target)
- select uuid_in(md5('opinion_choice'||row_number() over ())::cstring), 'Opinion'||row_number() over (), '{article}'
+ select
+ uuid_in(md5('opinion_choice'||row_number() over ())::cstring),
+ 'Opinion'||row_number() over (),
+ case when row_number() over () % 5 = 0 then null else '{article}'::text[] end
from generate_series(0,20);
for i in 0..9 loop
diff --git a/src/main/resources/sql/functions/helpers/find_reference_by_id.sql b/src/main/resources/sql/functions/helpers/find_reference_by_id.sql
index b289ce1..709c59a 100644
--- a/src/main/resources/sql/functions/helpers/find_reference_by_id.sql
+++ b/src/main/resources/sql/functions/helpers/find_reference_by_id.sql
@@ -11,11 +11,17 @@ begin
find_article_by_id(_id)
when 'constitution'::regclass then
find_constitution_by_id(_id)
+ when 'comment'::regclass then
+ find_comment_by_id(_id)
+ when 'opinion'::regclass then
+ find_opinion_by_id(_id)
else
- json_build_object('id', _id)
+ json_build_object('id', _id, 'reference', _reference)
end
into resource;
-end;
+
+ resource = resource::jsonb || jsonb_build_object('reference', _reference);
+end
$$;
-- drop function if exists find_reference_by_id(uuid, regclass, out json);
diff --git a/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql b/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql
index b38f071..02567e5 100644
--- a/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql
+++ b/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql
@@ -18,6 +18,8 @@ begin
from (
select
o.*,
+ find_reference_by_id(o.target_id, o.target_reference) as target,
+ find_citizen_by_id(o.created_by_id) as created_by,
to_json(ol) as choice
from opinion as o
join opinion_choice ol on o.choice_id = ol.id
diff --git a/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_id.sql b/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_id.sql
index 96f5ce5..5150ad2 100644
--- a/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_id.sql
+++ b/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_id.sql
@@ -11,6 +11,8 @@ begin
from (
select
o.*,
+ find_reference_by_id(o.target_id, o.target_reference) as target,
+ find_citizen_by_id(o.created_by_id) as created_by,
to_json(ol) as choice
from opinion as o
join opinion_choice ol on o.choice_id = ol.id
diff --git a/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_ids.sql b/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_ids.sql
index 265606c..926b8c6 100644
--- a/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_ids.sql
+++ b/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_ids.sql
@@ -9,6 +9,8 @@ begin
from (
select
o.*,
+ find_reference_by_id(o.target_id, o.target_reference) as target,
+ find_citizen_by_id(o.created_by_id) as created_by,
to_json(ol) as choice
from opinion as o
join opinion_choice ol on o.choice_id = ol.id
diff --git a/src/main/resources/sql/functions/opinion/find_opinion_by_id.sql b/src/main/resources/sql/functions/opinion/find_opinion_by_id.sql
new file mode 100644
index 0000000..1e6c19c
--- /dev/null
+++ b/src/main/resources/sql/functions/opinion/find_opinion_by_id.sql
@@ -0,0 +1,22 @@
+create or replace function find_opinion_by_id(
+ _id uuid,
+ out resource json
+) language plpgsql as
+$$
+begin
+ select to_json(t)
+ into resource
+ from (
+ select
+ o.*,
+ find_reference_by_id(o.target_id, o.target_reference) as target,
+ find_citizen_by_id(o.created_by_id) as created_by,
+ to_json(ol) as choice
+ from "opinion" as o
+ join opinion_choice ol on o.choice_id = ol.id
+ where o.id = _id
+ ) as t;
+end;
+$$;
+
+-- drop function if exists find_opinion_by_id(uuid, out json);
diff --git a/src/main/resources/sql/functions/opinion/find_opinion_by_opinion.sql b/src/main/resources/sql/functions/opinion/find_opinion_by_opinion.sql
new file mode 100644
index 0000000..7aef3d2
--- /dev/null
+++ b/src/main/resources/sql/functions/opinion/find_opinion_by_opinion.sql
@@ -0,0 +1,27 @@
+create or replace function find_opinion_by_opinion(
+ inout resource json
+) language plpgsql as
+$$
+declare
+ _target_id uuid = (resource#>>'{target, id}')::uuid;
+ _created_by_id uuid = (resource#>>'{created_by, id}')::uuid;
+ _choice_id uuid = (resource#>>'{choice, id}')::uuid;
+begin
+ select to_json(t)
+ into resource
+ from (
+ select
+ o.*,
+ find_reference_by_id(o.target_id, o.target_reference) as target,
+ find_citizen_by_id(o.created_by_id) as created_by,
+ to_json(ol) as choice
+ from "opinion" as o
+ join opinion_choice ol on o.choice_id = ol.id
+ where o.target_id = _target_id
+ and o.created_by_id = _created_by_id
+ and o.choice_id = _choice_id
+ ) as t;
+end;
+$$;
+
+-- drop function if exists find_opinion_by_opinion(json);
diff --git a/src/main/resources/sql/functions/opinion/find_opinion_choices.sql b/src/main/resources/sql/functions/opinion/find_opinion_choices.sql
index 7d8683a..22cc273 100644
--- a/src/main/resources/sql/functions/opinion/find_opinion_choices.sql
+++ b/src/main/resources/sql/functions/opinion/find_opinion_choices.sql
@@ -8,7 +8,11 @@ begin
select ol.*
from opinion_choice ol
where (ol.deleted_at is null or ol.deleted_at > now())
- and (ol.target is null or targets is null or array_length(targets, 1) = 0 or ol.target && targets)
+ and (
+ ol.target is null or array_length(ol.target, 1) is null -- if choice is compatible with all target
+ or targets is null or array_length(targets, 1) is null -- if no target defined
+ or (ol.target && targets) -- if target is compatible
+ )
order by ol.name
) t;
@@ -17,4 +21,4 @@ $$;
-- drop function if exists find_opinions();
--- select find_opinions();
\ No newline at end of file
+-- select find_opinion_choices('{}');
diff --git a/src/main/resources/sql/functions/opinion/opinion.sql b/src/main/resources/sql/functions/opinion/opinion.sql
deleted file mode 100644
index c2fe6e0..0000000
--- a/src/main/resources/sql/functions/opinion/opinion.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-create or replace function opinion(reference regclass, _target_id uuid, _created_by_id uuid, _opinion uuid, out resource json)
- language plpgsql as
-$$
-begin
- if reference = 'article'::regclass then
- insert into opinion_on_article (created_by_id, target_id, choice_id)
- values (_created_by_id, _target_id, _opinion)
- on conflict (created_by_id, target_id, choice_id) do nothing;
- else
- raise exception '% no implemented for opinion', reference::text;
- end if;
-
- select count_opinion(_target_id) into resource;
-end;
-$$;
-
--- drop function if exists vote(regclass,uuid,uuid,integer,boolean);
\ No newline at end of file
diff --git a/src/main/resources/sql/functions/opinion/upsert_opinion.sql b/src/main/resources/sql/functions/opinion/upsert_opinion.sql
new file mode 100644
index 0000000..2a2ada3
--- /dev/null
+++ b/src/main/resources/sql/functions/opinion/upsert_opinion.sql
@@ -0,0 +1,23 @@
+create or replace function upsert_opinion(inout resource json)
+ language plpgsql as
+$$
+declare
+ _reference regclass = (resource#>>'{target, reference}')::regclass;
+ _id uuid = coalesce((resource->>'id')::uuid, uuid_generate_v4());
+ _target_id uuid = (resource#>>'{target, id}')::uuid;
+ _created_by_id uuid = (resource#>>'{created_by, id}')::uuid;
+ _choice_id uuid = (resource#>>'{choice, id}')::uuid;
+begin
+ if _reference = 'article'::regclass then
+ insert into opinion_on_article (id, created_by_id, target_id, choice_id)
+ values (_id, _created_by_id, _target_id, _choice_id)
+ on conflict (created_by_id, target_id, choice_id) do nothing;
+ else
+ raise exception '% no implemented for opinion', _reference::text;
+ end if;
+
+ select find_opinion_by_opinion(resource) into resource;
+end;
+$$;
+
+-- drop function if exists upsert_opinion(json);
\ No newline at end of file
diff --git a/src/test/kotlin/MailerTest.kt b/src/test/kotlin/MailerTest.kt
index 01ca222..962f83a 100644
--- a/src/test/kotlin/MailerTest.kt
+++ b/src/test/kotlin/MailerTest.kt
@@ -7,6 +7,7 @@ import fr.dcproject.module
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.server.testing.withTestApplication
import io.ktor.util.KtorExperimentalAPI
+import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.koin.test.AutoCloseKoinTest
@@ -18,6 +19,7 @@ import org.koin.test.get
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MailerTest : KoinTest, AutoCloseKoinTest() {
@Test
+ @Tag("online")
fun `can be send an email`() {
withTestApplication({ module(Env.TEST) }) {
get().sendEmail {
diff --git a/src/test/kotlin/RunCucumberTest.kt b/src/test/kotlin/RunCucumberTest.kt
index 5ee4dfe..6c113a6 100644
--- a/src/test/kotlin/RunCucumberTest.kt
+++ b/src/test/kotlin/RunCucumberTest.kt
@@ -6,8 +6,8 @@ import fr.dcproject.utils.LoggerDelegate
import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Requester
import fr.postgresjson.migration.Migrations
-import io.cucumber.core.api.Scenario
import io.cucumber.java8.En
+import io.cucumber.java8.Scenario
import io.cucumber.junit.Cucumber
import io.cucumber.junit.CucumberOptions
import io.ktor.locations.KtorExperimentalLocationsAPI
diff --git a/src/test/kotlin/feature/ArticleSteps.kt b/src/test/kotlin/feature/ArticleSteps.kt
index 63d0f37..05c4f92 100644
--- a/src/test/kotlin/feature/ArticleSteps.kt
+++ b/src/test/kotlin/feature/ArticleSteps.kt
@@ -2,6 +2,8 @@ package feature
import fr.dcproject.entity.*
import fr.dcproject.repository.CommentArticle
+import fr.dcproject.utils.toUUID
+import io.cucumber.datatable.DataTable
import io.cucumber.java8.En
import org.joda.time.DateTime
import org.koin.test.KoinTest
@@ -16,6 +18,9 @@ import fr.dcproject.repository.Citizen as CitizenRepository
class ArticleSteps : En, KoinTest {
init {
+ /**
+ * @deprecated
+ */
Given("I have article with id {string}") { id: String ->
var citizen = Citizen(
name = CitizenI.Name("John", "Doe"),
@@ -40,6 +45,23 @@ class ArticleSteps : En, KoinTest {
get().upsert(article)
}
+ Given("I have article") { extraData: DataTable ->
+ extraData.asMap(String::class.java, String::class.java).let { params ->
+ val username = params["createdBy"]?.toLowerCase()?.replace(' ', '-') ?: error("You must provide the 'createdBy' parameter")
+ val citizen = get().findByUsername(username) ?: error("Citizen not exist")
+ val id = params["id"]?.toUUID() ?: UUID.randomUUID()
+ val article = ArticleEntity(
+ id = id,
+ title = "hello",
+ content = "bla bla bla",
+ description = "A super article",
+ createdBy = citizen
+ )
+ get().upsert(article)
+ }
+
+ }
+
Given("I have article with id {string} created by {string}") { id: String, username: String ->
val citizen = get().findByUsername(username)!!
diff --git a/src/test/kotlin/feature/KtorServerAuthSteps.kt b/src/test/kotlin/feature/KtorServerAuthSteps.kt
index b28b83e..55def43 100644
--- a/src/test/kotlin/feature/KtorServerAuthSteps.kt
+++ b/src/test/kotlin/feature/KtorServerAuthSteps.kt
@@ -5,6 +5,7 @@ import fr.dcproject.JwtConfig
import fr.dcproject.entity.Citizen
import fr.dcproject.entity.CitizenI
import fr.dcproject.entity.User
+import fr.dcproject.utils.toUUID
import fr.postgresjson.connexion.Requester
import io.cucumber.datatable.DataTable
import io.cucumber.java8.En
@@ -70,6 +71,38 @@ class KtorServerAuthSteps : En, KoinTest {
}
}
+ Given("I have citizen {word} {word}") { firstName: String, lastName: String, extraInfo: DataTable? ->
+ val id: UUID = extraInfo?.asMap(String::class.java, String::class.java)?.get("id")?.toUUID() ?: UUID.randomUUID()
+
+ val user = User(
+ id = id,
+ username = "$firstName-$lastName".toLowerCase(),
+ plainPassword = "azerty"
+ )
+ val citizen = Citizen(
+ id = id,
+ name = CitizenI.Name(firstName, lastName),
+ email = ("$firstName-$lastName".toLowerCase()) + "@dc-project.fr",
+ birthday = DateTime.now(),
+ user = user
+ )
+
+ get().insertWithUser(citizen)
+ }
+
+ Given("I am authenticated as {word} {word}") { firstName: String, lastName: String ->
+ val username = "$firstName-$lastName".toLowerCase()
+ val citizen = get().findByUsername(username) ?: error("Cititzen not exist with username $username")
+ val jwtAsString: String = JWT.create()
+ .withIssuer("dc-project.fr")
+ .withClaim("id", citizen.id.toString())
+ .sign(JwtConfig.algorithm)
+
+ KtorServerContext.defaultServer.addPreRequestSetup {
+ addHeader(HttpHeaders.Authorization, "Bearer $jwtAsString")
+ }
+ }
+
Given("I have citizen {word} {word} with id {string}") { firstName: String, lastName: String, id: String ->
val user = User(
id = UUID.randomUUID(),
diff --git a/src/test/kotlin/feature/KtorServerRestSteps.kt b/src/test/kotlin/feature/KtorServerRestSteps.kt
index 9c9191b..89bb931 100644
--- a/src/test/kotlin/feature/KtorServerRestSteps.kt
+++ b/src/test/kotlin/feature/KtorServerRestSteps.kt
@@ -41,20 +41,20 @@ class KtorServerRestSteps : En {
}
}
- private fun findJsonElement(node: String): JsonElement {
+ private fun findJsonElement(path: String): JsonElement {
var jsonElement: JsonElement = responseJsonElement
- val elements = node.split(".")
- elements.forEach {
- val asArrayIndex = """\d+""".toRegex().find(it)
-
- jsonElement = if (asArrayIndex != null) {
- val index = asArrayIndex.groups.first()!!
- jsonElement.jsonArray.get(index.value.toInt())
- } else {
- jsonElement.jsonObject.get(it) ?: throw AssertionError("\"$node\" element not found on json response")
+ path
+ .split("].", "]", "[", ".")
+ .filter { it.trim().isNotBlank() }
+ .map { it.trim() }
+ .forEach {
+ jsonElement = if (jsonElement is JsonArray) {
+ jsonElement.jsonArray[it.toInt()]
+ } else {
+ jsonElement.jsonObject[it]
+ } ?: throw AssertionError("\"$path\" element not found on json response")
}
- }
return jsonElement
}
diff --git a/src/test/kotlin/feature/OpinionSteps.kt b/src/test/kotlin/feature/OpinionSteps.kt
new file mode 100644
index 0000000..507e914
--- /dev/null
+++ b/src/test/kotlin/feature/OpinionSteps.kt
@@ -0,0 +1,40 @@
+package feature
+
+import fr.dcproject.entity.OpinionArticle
+import fr.dcproject.utils.toUUID
+import io.cucumber.datatable.DataTable
+import io.cucumber.java8.En
+import org.koin.test.KoinTest
+import org.koin.test.get
+import fr.dcproject.repository.Article as ArticleRepository
+import fr.dcproject.repository.Citizen as CitizenRepository
+import fr.dcproject.repository.OpinionArticle as OpinionRepository
+import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository
+
+
+class OpinionSteps : En, KoinTest {
+ init {
+ Given("I have the opinion {string} on article {string} created by {string}:") { opinionChoice: String, article: String, citizen: String, extraInfo: DataTable ->
+ extraInfo.asMap(String::class.java, String::class.java).let {
+ val opinion = OpinionArticle(
+ choice = get().findOpinionsChoiceByName(opinionChoice) ?: error("Opinion Choice not exist"),
+ target = get().findById(article.toUUID()) ?: error("Article not exist"),
+ createdBy = get().findById(citizen.toUUID()) ?: error("Citizen not exist")
+ )
+ get().opinion(opinion)
+ }
+ }
+
+ Given("I have an opinion") { extraInfo: DataTable ->
+ extraInfo.asMap(String::class.java, String::class.java)?.let { params ->
+ val username = params["createdBy"]?.toLowerCase()?.replace(' ', '-') ?: error("You must provide the 'createdBy' parameter")
+ val opinion = OpinionArticle(
+ choice = params["opinion"]?.let { get().findOpinionsChoiceByName(it) ?: error("Opinion Choice not exist")} ?: error("You must provide the 'opinion' parameter"),
+ target = params["article"]?.let { get().findById(it.toUUID()) ?: error("Article not exist")} ?: error("You must provide the 'article' parameter"),
+ createdBy = get().findByUsername(username) ?: error("Citizen not exist")
+ )
+ get().opinion(opinion)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/feature/citizen.feature b/src/test/resources/feature/citizen.feature
index d5e9cb2..33e71a2 100644
--- a/src/test/resources/feature/citizen.feature
+++ b/src/test/resources/feature/citizen.feature
@@ -19,6 +19,7 @@ Feature: citizens routes
And the response should contain object:
| id | 64b7b379-2298-43ec-b428-ba134930cabd |
+ @online
Scenario: Can be connect with SSO
Given I have citizen:
| id | c606110c-ff0e-4d09-a79e-74632d7bf7bd |
diff --git a/src/test/resources/feature/opinion.feature b/src/test/resources/feature/opinion.feature
new file mode 100644
index 0000000..b35ea2c
--- /dev/null
+++ b/src/test/resources/feature/opinion.feature
@@ -0,0 +1,59 @@
+@opinion
+Feature: Opinion
+
+ Scenario: Can get one opinion Choices
+ When I send a GET request to "/opinions/6e978eb5-3c48-0def-b093-e01f43983adb"
+ Then the response status code should be 200
+ And the JSON should contain:
+ | name | Opinion1 |
+
+ Scenario: Can get all opinion choices
+ When I send a GET request to "/opinions"
+ Then the response status code should be 200
+ And the JSON should contain:
+ | [0]name | Opinion1 |
+
+ Scenario: Can create opinion on article
+ Given I have citizen Isaac Newton
+ | id | 2f414045-95d9-42ca-a3a9-8cdde52ad253 |
+ And I am authenticated as Isaac Newton
+ And I have article
+ | id | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7 |
+ | createdBy | Isaac Newton |
+ And I have an opinion
+ | opinion | Opinion1 |
+ | article | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b |
+ | createdBy | Isaac Newton |
+ When I send a PUT request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/opinions" with body:
+ """
+ {
+ "opinion_choice": "6e978eb5-3c48-0def-b093-e01f43983adb"
+ }
+ """
+ Then the response status code should be 201
+
+ Scenario: Can I get all opinions of citizen filtered by target ids
+ When I send a GET request to "/citizens/6434f4f9-f570-f22a-c134-8668350651ff/opinions?id=9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b"
+ Then the response status code should be 200
+ And the JSON should contain:
+ | [0].name | Opinion2 |
+
+ Scenario: Can recieve opinion aggregation with article
+ When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b"
+ Then the response status code should be 200
+ And the JSON should contain:
+ | opinions.Opinion2 | 1 |
+
+ Scenario: Can get all opinion of one citizen
+ Given I have citizen Albert Einstein
+ | id | c1542096-3431-432d-8e35-9dc071d4c818 |
+ And I am authenticated as Albert Einstein
+ And I have an opinion
+ | opinion | Opinion1 |
+ | article | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b |
+ | createdBy | Albert Einstein |
+ When I send a GET request to "/citizens/c1542096-3431-432d-8e35-9dc071d4c818/opinions/articles"
+ Then the response status code should be 200
+ And the JSON element result should have 1 items
+ And the JSON should contain:
+ | result[0].name | Opinion1 |
diff --git a/src/test/sql/opinion.sql b/src/test/sql/opinion.sql
index 1096a51..89f4504 100644
--- a/src/test/sql/opinion.sql
+++ b/src/test/sql/opinion.sql
@@ -38,8 +38,9 @@ declare
"draft":false
}
$json$;
- opinion1 uuid = uuid_generate_v4();
- opinion2 uuid = uuid_generate_v4();
+ opinion_choice1_id uuid = uuid_generate_v4();
+ opinion_choice2_id uuid = uuid_generate_v4();
+ opinion2 json;
begin
-- insert user for context
select insert_user(created_user) into created_user;
@@ -59,32 +60,38 @@ begin
insert into opinion_choice(id, name, target)
- values (opinion1, 'Opinion1', '{article}');
+ values (opinion_choice1_id, 'Opinion1', '{article}');
- insert into opinion_choice(id, name, target)
- values (opinion2, 'Opinion2', '{article}');
+ insert into opinion_choice(id, name)
+ values (opinion_choice2_id, 'Opinion2');
insert into opinion_choice(name, target)
values ('Opinion3', '{article}');
- perform opinion(
- reference => 'article'::regclass,
- _target_id => (created_article->>'id')::uuid,
- _created_by_id => _citizen_id,
- _opinion => opinion1
- );
- perform opinion(
- reference => 'article'::regclass,
- _target_id => (created_article->>'id')::uuid,
- _created_by_id => _citizen_id,
- _opinion => opinion2
+ perform upsert_opinion(
+ resource => json_build_object(
+ 'target', json_build_object('id', (created_article->'id'), 'reference', 'article'),
+ 'created_by', json_build_object('id', _citizen_id),
+ 'choice', json_build_object('id', opinion_choice1_id)
+ )
);
+ select upsert_opinion(
+ resource => json_build_object(
+ 'target', json_build_object('id', (created_article->'id'), 'reference', 'article'),
+ 'created_by', json_build_object('id', _citizen_id),
+ 'choice', json_build_object('id', opinion_choice2_id)
+ )
+ ) into opinion2;
assert (select count(*) = 2 from opinion_on_article), 'opinions must be inserted';
- assert (select choice_id = opinion1 from opinion_on_article limit 1), 'opinion must be inserted';
+ assert (select choice_id = opinion_choice1_id from opinion_on_article limit 1), 'opinion must be inserted';
assert(select (a#>>'{opinions, Opinion1}')::int = 1
from find_article_by_id((created_article->>'id')::uuid) a), 'the article must be have a opinion';
+ raise notice '%', opinion2;
+ assert(select (opinion2#>>'{choice, id}')::uuid = opinion_choice2_id), 'opinion2 is not inserted';
+ assert(select (opinion2#>>'{choice, name}') = 'Opinion2'), 'no name for opinion2';
+
assert(
select (o#>>'{0, choice, name}') = 'Opinion1'
from find_citizen_opinions_by_target_id(_citizen_id, (created_article->>'id')::uuid) o),
@@ -100,7 +107,11 @@ begin
), 'find_opinion_choices must be return all opinions';
assert(
- select (find_opinion_choice_by_id(opinion1)->>'name') = 'Opinion1'
+ select find_opinion_choices('{}')#>>'{0, name}' = 'Opinion1'
+ ), 'find_opinion_choices must be return all opinions if no target is defined';
+
+ assert(
+ select (find_opinion_choice_by_id(opinion_choice1_id)->>'name') = 'Opinion1'
), 'find_opinion_choice_by_id must return the opinion_choice';
assert(