diff --git a/.idea/runConfigurations/Test_All_SQL.xml b/.idea/runConfigurations/Test_All_SQL.xml
index f2f182f..8a79347 100644
--- a/.idea/runConfigurations/Test_All_SQL.xml
+++ b/.idea/runConfigurations/Test_All_SQL.xml
@@ -49,10 +49,12 @@
+
+
+
-
diff --git a/src/main/kotlin/fr/dcproject/Application.kt b/src/main/kotlin/fr/dcproject/Application.kt
index 70c8622..6c7fbe7 100644
--- a/src/main/kotlin/fr/dcproject/Application.kt
+++ b/src/main/kotlin/fr/dcproject/Application.kt
@@ -143,6 +143,7 @@ fun Application.module(env: Env = PROD) {
CommentVoter(),
VoteVoter(),
FollowVoter(),
+ OpinionVoter(),
OpinionChoiceVoter()
)
}
diff --git a/src/main/kotlin/fr/dcproject/Module.kt b/src/main/kotlin/fr/dcproject/Module.kt
index 07c4d7e..3a2ee3a 100644
--- a/src/main/kotlin/fr/dcproject/Module.kt
+++ b/src/main/kotlin/fr/dcproject/Module.kt
@@ -15,6 +15,7 @@ import fr.dcproject.repository.CommentGeneric as CommentGenericRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository
import fr.dcproject.repository.FollowArticle as FollowArticleRepository
import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository
+import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository
import fr.dcproject.repository.User as UserRepository
import fr.dcproject.repository.VoteArticle as VoteArticleRepository
@@ -59,6 +60,7 @@ val Module = module {
single { VoteConstitutionRepository(get()) }
single { VoteCommentRepository(get()) }
single { OpinionChoiceRepository(get()) }
+ single { OpinionArticleRepository(get()) }
single { Migrations(connection = get(), directory = config.sqlFiles) }
diff --git a/src/main/kotlin/fr/dcproject/entity/Opinion.kt b/src/main/kotlin/fr/dcproject/entity/Opinion.kt
index 5de8699..cafaaf5 100644
--- a/src/main/kotlin/fr/dcproject/entity/Opinion.kt
+++ b/src/main/kotlin/fr/dcproject/entity/Opinion.kt
@@ -6,9 +6,14 @@ import java.util.*
open class Opinion(
id: UUID = UUID.randomUUID(),
override val createdBy: CitizenBasic,
- override var target: T,
- var name: String
+ override val target: T,
+ val choice: OpinionChoice
) : ExtraI,
UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(),
- EntityCreatedBy by EntityCreatedByImp(createdBy)
+ EntityCreatedBy by EntityCreatedByImp(createdBy) {
+
+ fun getName(): String = choice.name
+}
+
+typealias OpinionArticle = Opinion
\ 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 5249a89..735ef4e 100644
--- a/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt
+++ b/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt
@@ -11,6 +11,10 @@ class OpinionChoice(
id: UUID,
val name: String,
val target: List
-) : UuidEntity(id),
+) : OpinionChoiceRef(id),
EntityCreatedAt by EntityCreatedAtImp(),
- EntityDeletedAt by EntityDeletedAtImp()
\ No newline at end of file
+ EntityDeletedAt by EntityDeletedAtImp()
+
+open class OpinionChoiceRef(
+ id: UUID
+) : UuidEntity(id)
\ No newline at end of file
diff --git a/src/main/kotlin/fr/dcproject/entity/Opinionable.kt b/src/main/kotlin/fr/dcproject/entity/Opinionable.kt
index 5100382..9bc42c4 100644
--- a/src/main/kotlin/fr/dcproject/entity/Opinionable.kt
+++ b/src/main/kotlin/fr/dcproject/entity/Opinionable.kt
@@ -1,5 +1,11 @@
package fr.dcproject.entity
+import fr.postgresjson.entity.EntityI
+
+class OpinionAggregation(
+ override val entries: Set> = emptySet()
+) : AbstractMap(), EntityI
+
interface Opinionable {
val opinions: MutableMap
}
diff --git a/src/main/kotlin/fr/dcproject/entity/request/ArticleOpinionRequest.kt b/src/main/kotlin/fr/dcproject/entity/request/ArticleOpinionRequest.kt
new file mode 100644
index 0000000..3b17678
--- /dev/null
+++ b/src/main/kotlin/fr/dcproject/entity/request/ArticleOpinionRequest.kt
@@ -0,0 +1,27 @@
+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 2da16d0..10d4282 100644
--- a/src/main/kotlin/fr/dcproject/entity/request/Request.kt
+++ b/src/main/kotlin/fr/dcproject/entity/request/Request.kt
@@ -1,3 +1,14 @@
package fr.dcproject.entity.request
-interface Request
\ No newline at end of file
+import fr.dcproject.entity.CitizenRef
+import fr.postgresjson.entity.EntityI
+
+interface Request
+
+interface RequestBuilder : Request {
+ fun create(): E
+}
+
+interface RequestBuilderWithCreator : Request {
+ fun create(citizen: C): E
+}
diff --git a/src/main/kotlin/fr/dcproject/repository/Opinion.kt b/src/main/kotlin/fr/dcproject/repository/Opinion.kt
new file mode 100644
index 0000000..791d31c
--- /dev/null
+++ b/src/main/kotlin/fr/dcproject/repository/Opinion.kt
@@ -0,0 +1,112 @@
+package fr.dcproject.repository
+
+import com.fasterxml.jackson.core.type.TypeReference
+import fr.dcproject.entity.Article
+import fr.dcproject.entity.OpinionAggregation
+import fr.dcproject.entity.TargetRef
+import fr.postgresjson.connexion.Paginated
+import fr.postgresjson.connexion.Requester
+import fr.postgresjson.repository.RepositoryI
+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.OpinionChoice as OpinionChoiceEntity
+
+open class OpinionChoice(override val requester: Requester) : RepositoryI {
+ /**
+ * find all opinion choices
+ * can be filtered by target compatibility
+ */
+ fun findOpinionsChoices(targets: List = emptyList()): List =
+ requester
+ .getFunction("find_opinion_choices")
+ .select(
+ "targets" to targets
+ )
+
+ /**
+ * find one opinion choices by id
+ */
+ fun findOpinionChoiceById(id: UUID): OpinionChoiceEntity? =
+ requester
+ .getFunction("find_opinion_choice_by_id")
+ .selectOne(
+ "id" to id
+ )
+}
+
+open class Opinion(requester: Requester) : OpinionChoice(requester) {
+ /**
+ * Create an Opinion on target (article,...)
+ */
+ fun opinion(opinion: OpinionEntity): OpinionAggregation {
+ return requester
+ .getFunction("opinion")
+ .selectOne(
+ "reference" to opinion.target.reference,
+ "target_id" to opinion.target.id,
+ "opinion" to opinion.id,
+ "created_by_id" to opinion.createdBy
+ )!!
+ }
+
+ /**
+ * Find opinions of one citizen filtered by target ids
+ */
+ fun findCitizenOpinionsByTargets(
+ citizen: CitizenEntity,
+ targets: List
+ ): List> {
+ val typeReference = object : TypeReference>>() {}
+ return requester.run {
+ getFunction("find_citizen_opinions_by_target_ids")
+ .select(
+ typeReference, mapOf(
+ "citizen_id" to citizen.id,
+ "ids" to targets
+ )
+ )
+ }
+ }
+
+ /**
+ * find opinion of citizen filtered by one target id
+ */
+ fun findCitizenOpinionsByTarget(
+ citizen: CitizenEntity,
+ target: UUID
+ ): List> {
+ val typeReference = object : TypeReference>>() {}
+ return requester
+ .getFunction("find_citizen_opinions_by_target_id")
+ .select(
+ typeReference, mapOf(
+ "citizen_id" to citizen.id,
+ "id" to target
+ )
+ )
+ }
+
+ /**
+ * find paginated opinion of one citizen
+ * can be sorted
+ */
+ fun findCitizenOpinions(
+ citizen: CitizenEntity,
+ page: Int = 1,
+ limit: Int = 50,
+ sort: String? = null,
+ direction: RepositoryI.Direction? = null
+ ): Paginated> {
+ return requester
+ .getFunction("find_citizen_opinions")
+ .select(page, limit,
+ "sort" to sort?.toSnakeCase(),
+ "direction" to direction,
+ "citizen" to citizen.id
+ )
+ }
+}
+
+class OpinionArticle(requester: Requester) : Opinion(requester)
\ 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
new file mode 100644
index 0000000..d9ddb53
--- /dev/null
+++ b/src/main/kotlin/fr/dcproject/routes/OpinionArticle.kt
@@ -0,0 +1,72 @@
+package fr.dcproject.routes
+
+import fr.dcproject.citizen
+import fr.dcproject.entity.request.ArticleOpinionRequest
+import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
+import fr.dcproject.security.voter.assertCan
+import fr.dcproject.utils.toUUID
+import io.ktor.application.call
+import io.ktor.http.HttpStatusCode
+import io.ktor.locations.KtorExperimentalLocationsAPI
+import io.ktor.locations.Location
+import io.ktor.locations.get
+import io.ktor.locations.put
+import io.ktor.request.receive
+import io.ktor.response.respond
+import io.ktor.routing.Route
+import io.ktor.util.KtorExperimentalAPI
+import org.koin.core.KoinComponent
+import org.koin.core.get
+import fr.dcproject.entity.Article as ArticleEntity
+import fr.dcproject.entity.Citizen as CitizenEntity
+import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
+
+@KtorExperimentalLocationsAPI
+object OpinionArticlePaths {
+ /**
+ * Get paginated opinion of citizen for one article
+ */
+ @Location("/citizens/{citizen}/opinions/articles")
+ class CitizenOpinionArticleRequest(
+ val citizen: CitizenEntity,
+ page: Int = 1,
+ limit: Int = 50
+ ) : PaginatedRequestI by PaginatedRequest(page, limit)
+
+ /**
+ * Put an opinion on one article
+ */
+ @Location("/articles/{article}/opinons")
+ class ArticleOpinion(val article: ArticleEntity)
+
+ /**
+ * Get all Opinion of citizen on targets by target ids
+ */
+ @Location("/citizen/{citizen}/opinions")
+ class CitizenOpinions(val citizen: CitizenEntity, id: List): KoinComponent {
+ val opinionsEntities = get()
+ .findCitizenOpinionsByTargets(citizen, id.toUUID())
+ }
+}
+
+@KtorExperimentalAPI
+@KtorExperimentalLocationsAPI
+fun Route.opinionArticle(repo: OpinionArticleRepository) {
+ get {
+ val opinions = repo.findCitizenOpinions(citizen, it.page, it.limit)
+ call.respond(opinions)
+ }
+
+ get {
+ assertCan(VIEW, it.opinionsEntities)
+
+ call.respond(it.opinionsEntities)
+ }
+
+ put {
+ val optionArticle = call.receive().create(citizen)
+ assertCan(VIEW, optionArticle)
+
+ call.respond(HttpStatusCode.Created, optionArticle)
+ }
+}
diff --git a/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt b/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt
index 1bac229..5b35998 100644
--- a/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt
+++ b/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt
@@ -9,6 +9,7 @@ import fr.dcproject.routes.VoteArticlePaths.CommentVoteRequest
import fr.dcproject.security.voter.VoteVoter.Action.CREATE
import fr.dcproject.security.voter.VoteVoter.Action.VIEW
import fr.dcproject.security.voter.assertCan
+import fr.dcproject.utils.toUUID
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -45,10 +46,7 @@ object VoteArticlePaths {
@Location("/citizens/{citizen}/votes")
class CitizenVotesByIdsRequest(val citizen: Citizen, id: List) {
- val id: List = id
- .map { it.trim() }
- .filter { it.isNotBlank() }
- .map { UUID.fromString(it) }
+ val id: List = id.toUUID()
}
}
diff --git a/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt b/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt
new file mode 100644
index 0000000..689f4a2
--- /dev/null
+++ b/src/main/kotlin/fr/dcproject/security/voter/OpinionVoter.kt
@@ -0,0 +1,53 @@
+package fr.dcproject.security.voter
+
+import fr.dcproject.entity.Opinion
+import io.ktor.application.ApplicationCall
+
+class OpinionVoter : Voter {
+ enum class Action : ActionI {
+ CREATE,
+ VIEW,
+ DELETE
+ }
+
+ override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
+ return (action is Action)
+ .and(subject is Opinion<*>? || subject is List<*>)
+ }
+
+ override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
+ val user = call.user
+ if (action == Action.CREATE) {
+ return if (user != null) Vote.GRANTED else Vote.DENIED
+ }
+
+ if (action == Action.VIEW) {
+ if (subject is Opinion<*>) {
+ return Vote.GRANTED
+ }
+ if (subject is List<*>) {
+ subject.forEach {
+ if (it !is Opinion<*>) {
+ return Vote.DENIED
+ }
+ }
+ return Vote.GRANTED
+ }
+ return Vote.DENIED
+ }
+
+ if (action == Action.DELETE) {
+ return if (subject is Opinion<*>
+ && user != null
+ && subject.createdBy.user.id == user.id)
+ Vote.GRANTED
+ else Vote.DENIED
+ }
+
+ if (action is Action) {
+ return Vote.DENIED
+ }
+
+ return Vote.ABSTAIN
+ }
+}
diff --git a/src/main/kotlin/fr/dcproject/utils/Uuid.kt b/src/main/kotlin/fr/dcproject/utils/Uuid.kt
new file mode 100644
index 0000000..984538e
--- /dev/null
+++ b/src/main/kotlin/fr/dcproject/utils/Uuid.kt
@@ -0,0 +1,10 @@
+package fr.dcproject.utils
+
+import java.util.*
+
+fun String.toUUID(): UUID = UUID.fromString(this.trim())
+
+fun List.toUUID(): List = this
+ .map { it.trim() }
+ .filter { it.isNotBlank() }
+ .map { UUID.fromString(it) }
\ 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 2b7170a..4cde828 100644
--- a/src/main/resources/sql/fixtures/09-opinion.sql
+++ b/src/main/resources/sql/fixtures/09-opinion.sql
@@ -2,22 +2,22 @@ do
$$
declare
article_count int = (select count(*) from article);
- _citizensIds uuid[] = (select array_agg(id) from citizen);
begin
delete from opinion_on_article;
- delete from opinion_list;
+ delete from opinion_choice;
- insert into opinion_list (id, name, target)
- select uuid_in(md5('opinion_list'||row_number() over ())::cstring), 'Opinion'||row_number() over (), '{article}'
+ insert into opinion_choice (id, name, target)
+ select uuid_in(md5('opinion_choice'||row_number() over ())::cstring), 'Opinion'||row_number() over (), '{article}'
from generate_series(0,20);
for i in 0..9 loop
- insert into opinion_on_article (id, created_by_id, target_id, opinion)
+ insert into opinion_on_article (id, created_by_id, target_id, choice_id, created_at)
select
uuid_in(md5('opinion_on_article'||rn+(article_count*i))::cstring),
z.id,
a.id,
- uuid_in(md5('opinion_list'||((rn+i) % 5 +1))::cstring)
+ uuid_in(md5('opinion_choice'||((rn+i) % 5 +1))::cstring),
+ now() + ((rn+i) || ' minute')::interval
from (select *, row_number() over ()+i+5 % 5 rn from citizen) z
join (select *, row_number() over () rn from article) a using (rn);
end loop;
diff --git a/src/main/resources/sql/functions/opinion/count_opinion.sql b/src/main/resources/sql/functions/opinion/count_opinion.sql
index fae0cd1..5675ce1 100644
--- a/src/main/resources/sql/functions/opinion/count_opinion.sql
+++ b/src/main/resources/sql/functions/opinion/count_opinion.sql
@@ -9,10 +9,10 @@ begin
into agg
from (
select
- count(o.opinion) as total,
+ count(o) as total,
ol.name as label
from opinion o
- join opinion_list ol on o.opinion = ol.id
+ join opinion_choice ol on o.choice_id = ol.id
where o.target_id = _target_id
group by ol.name
order by ol.name
diff --git a/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql b/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql
new file mode 100644
index 0000000..b38f071
--- /dev/null
+++ b/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql
@@ -0,0 +1,46 @@
+create or replace function find_citizen_opinions(
+ _citizen_id uuid,
+ direction text default 'desc',
+ sort text default 'created_at',
+ "limit" int default 50,
+ "offset" int default 0,
+ out resource json,
+ out total int
+) language plpgsql as
+$$
+begin
+ select json_agg(t), (
+ select count(o.id)
+ from opinion o
+ where o.created_by_id = _citizen_id
+ )
+ into resource, total
+ from (
+ select
+ o.*,
+ to_json(ol) as choice
+ from opinion as o
+ join opinion_choice ol on o.choice_id = ol.id
+
+ where created_by_id = _citizen_id
+
+ order by
+ case direction when 'asc' then
+ case sort
+ when 'created_at' then o.created_at::text
+ else null
+ end
+ end,
+ case direction when 'desc' then
+ case sort
+ when 'created_at' then o.created_at::text
+ end
+ end
+ desc,
+ o.created_at desc
+ limit "limit" offset "offset"
+ ) t;
+end
+$$;
+
+-- select * from find_citizen_opinions('6434f4f9-f570-f22a-c134-8668350651ff', null, null, 2, 2);
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 ff415ae..96f5ce5 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,9 +11,9 @@ begin
from (
select
o.*,
- ol.name
+ to_json(ol) as choice
from opinion as o
- join opinion_list ol on o.opinion = ol.id
+ join opinion_choice ol on o.choice_id = ol.id
where target_id = _id
and created_by_id = _citizen_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 4d3b7fe..265606c 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,9 +9,9 @@ begin
from (
select
o.*,
- ol.name
+ to_json(ol) as choice
from opinion as o
- join opinion_list ol on o.opinion = ol.id
+ join opinion_choice ol on o.choice_id = ol.id
where target_id = any(_ids)
and created_by_id = _citizen_id
diff --git a/src/main/resources/sql/functions/opinion/find_opinion_choice_by_id.sql b/src/main/resources/sql/functions/opinion/find_opinion_choice_by_id.sql
index 7d3c3bb..89acb19 100644
--- a/src/main/resources/sql/functions/opinion/find_opinion_choice_by_id.sql
+++ b/src/main/resources/sql/functions/opinion/find_opinion_choice_by_id.sql
@@ -3,7 +3,7 @@ create or replace function find_opinion_choice_by_id(_id uuid, out resource json
$$
begin
select to_json(ol) into resource
- from opinion_list ol
+ from opinion_choice ol
where (ol.deleted_at <= now()
or ol.deleted_at is null)
and (ol.id = _id);
diff --git a/src/main/resources/sql/functions/opinion/find_opinions.sql b/src/main/resources/sql/functions/opinion/find_opinion_choices.sql
similarity index 60%
rename from src/main/resources/sql/functions/opinion/find_opinions.sql
rename to src/main/resources/sql/functions/opinion/find_opinion_choices.sql
index bb9528a..7d8683a 100644
--- a/src/main/resources/sql/functions/opinion/find_opinions.sql
+++ b/src/main/resources/sql/functions/opinion/find_opinion_choices.sql
@@ -6,10 +6,9 @@ begin
into resource
from (
select ol.*
- from opinion_list ol
- where (ol.deleted_at <= now()
- or ol.deleted_at is null)
- and (ol.target is null or array_length(targets) = 0 or ol.target = any(targets))
+ 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)
order by ol.name
) t;
diff --git a/src/main/resources/sql/functions/opinion/opinion.sql b/src/main/resources/sql/functions/opinion/opinion.sql
index a983eaa..c2fe6e0 100644
--- a/src/main/resources/sql/functions/opinion/opinion.sql
+++ b/src/main/resources/sql/functions/opinion/opinion.sql
@@ -3,9 +3,9 @@ create or replace function opinion(reference regclass, _target_id uuid, _created
$$
begin
if reference = 'article'::regclass then
- insert into opinion_on_article (created_by_id, target_id, opinion)
+ 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, opinion) do nothing;
+ on conflict (created_by_id, target_id, choice_id) do nothing;
else
raise exception '% no implemented for opinion', reference::text;
end if;
diff --git a/src/main/resources/sql/migrations/0000-init_schema.down.sql b/src/main/resources/sql/migrations/0000-init_schema.down.sql
index 43c7b38..8793336 100644
--- a/src/main/resources/sql/migrations/0000-init_schema.down.sql
+++ b/src/main/resources/sql/migrations/0000-init_schema.down.sql
@@ -4,7 +4,7 @@ drop table if exists resource_view;
-- Extra resources
drop table if exists opinion_on_article;
drop table if exists opinion;
-drop table if exists opinion_list;
+drop table if exists opinion_choice;
drop table if exists follow_article;
drop table if exists follow_constitution;
diff --git a/src/main/resources/sql/migrations/0000-init_schema.up.sql b/src/main/resources/sql/migrations/0000-init_schema.up.sql
index 9552d33..d635838 100644
--- a/src/main/resources/sql/migrations/0000-init_schema.up.sql
+++ b/src/main/resources/sql/migrations/0000-init_schema.up.sql
@@ -503,7 +503,7 @@ create table resource_view
ip cidr null
);
-create table opinion_list
+create table opinion_choice
(
id uuid default uuid_generate_v4() not null primary key,
name text not null unique,
@@ -514,20 +514,20 @@ create table opinion_list
create table opinion
(
- opinion uuid not null references opinion_list (id),
+ choice_id uuid not null references opinion_choice (id),
foreign key (created_by_id) references citizen (id),
primary key (id),
- unique (created_by_id, target_id, opinion)
+ unique (created_by_id, target_id, choice_id)
) inherits (extra);
create table opinion_on_article
(
target_reference regclass default 'article'::regclass not null,
- foreign key (opinion) references opinion_list (id),
+ foreign key (choice_id) references opinion_choice (id),
foreign key (target_id) references article (id),
foreign key (created_by_id) references citizen (id),
primary key (id),
- unique (created_by_id, target_id, opinion)
+ unique (created_by_id, target_id, choice_id)
) inherits (opinion);
diff --git a/src/test/sql/opinion.sql b/src/test/sql/opinion.sql
index 9fd6ff8..1096a51 100644
--- a/src/test/sql/opinion.sql
+++ b/src/test/sql/opinion.sql
@@ -58,13 +58,13 @@ begin
select upsert_article(created_article) into created_article;
- insert into opinion_list(id, name, target)
+ insert into opinion_choice(id, name, target)
values (opinion1, 'Opinion1', '{article}');
- insert into opinion_list(id, name, target)
+ insert into opinion_choice(id, name, target)
values (opinion2, 'Opinion2', '{article}');
- insert into opinion_list(name, target)
+ insert into opinion_choice(name, target)
values ('Opinion3', '{article}');
perform opinion(
@@ -73,33 +73,51 @@ begin
_created_by_id => _citizen_id,
_opinion => opinion1
);
- assert (select count(*) = 1 from opinion_on_article), 'opinion must be inserted';
- assert (select opinion = opinion1 from opinion_on_article limit 1), 'opinion must be inserted';
+ perform opinion(
+ reference => 'article'::regclass,
+ _target_id => (created_article->>'id')::uuid,
+ _created_by_id => _citizen_id,
+ _opinion => 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 (a#>>'{opinions, Opinion1}')::int = 1
from find_article_by_id((created_article->>'id')::uuid) a), 'the article must be have a opinion';
assert(
- select (o#>>'{0, name}') = 'Opinion1'
+ select (o#>>'{0, choice, name}') = 'Opinion1'
from find_citizen_opinions_by_target_id(_citizen_id, (created_article->>'id')::uuid) o),
'The opinion must have a name';
assert(
- select (o#>>'{0, name}') = 'Opinion1'
+ select (o#>>'{0, choice, name}') = 'Opinion1'
from find_citizen_opinions_by_target_ids(_citizen_id, array[(created_article->>'id')::uuid]) o),
'The first opinion must have a name';
assert(
select find_opinion_choices()#>>'{0, name}' = 'Opinion1'
- ), 'find_opinion_choices mst be return all opinions';
+ ), 'find_opinion_choices must be return all opinions';
assert(
select (find_opinion_choice_by_id(opinion1)->>'name') = 'Opinion1'
), 'find_opinion_choice_by_id must return the opinion_choice';
+ assert(
+ select json_array_length(resource) = 1 from find_citizen_opinions(_citizen_id, null, null, 1, 1)
+ ), 'find_citizen_opinions must return only 1 result if limit is set to 1';
+
+ assert(
+ select total = 2 from find_citizen_opinions(_citizen_id, null, null, 2, 1)
+ ), 'find_citizen_opinions must return the total and it should be 2';
+
+ assert(
+ select (resource#>>'{0, choice, name}') = 'Opinion1' from find_citizen_opinions(_citizen_id, null, null, 1, 0)
+ ), 'find_citizen_opinions must return a list of opinion with name';
+
-- delete vote and context
delete from opinion;
- delete from opinion_list;
+ delete from opinion_choice;
delete from article;
delete from citizen;
delete from "user";