diff --git a/.idea/runConfigurations/Opinion_Tests.xml b/.idea/runConfigurations/Opinion_Tests.xml index 327b968..9cb45f5 100644 --- a/.idea/runConfigurations/Opinion_Tests.xml +++ b/.idea/runConfigurations/Opinion_Tests.xml @@ -7,7 +7,7 @@ - + diff --git a/.idea/runConfigurations/Test_All_SQL.xml b/.idea/runConfigurations/Test_All_SQL.xml index 0c1b39f..593c78c 100644 --- a/.idea/runConfigurations/Test_All_SQL.xml +++ b/.idea/runConfigurations/Test_All_SQL.xml @@ -1,5 +1,5 @@ - + @@ -53,6 +53,7 @@ + diff --git a/.idea/runConfigurations/Test_Opinions.xml b/.idea/runConfigurations/Test_Opinions.xml index 735f7a7..b148158 100644 --- a/.idea/runConfigurations/Test_Opinions.xml +++ b/.idea/runConfigurations/Test_Opinions.xml @@ -9,6 +9,7 @@ + FILE diff --git a/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt b/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt index 94b671f..c25632c 100644 --- a/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt +++ b/src/main/kotlin/fr/dcproject/entity/OpinionChoice.kt @@ -8,7 +8,7 @@ import fr.postgresjson.entity.mutable.EntityDeletedAtImp import java.util.* class OpinionChoice( - id: UUID, + id: UUID? = null, val name: String, val target: List? ) : OpinionChoiceRef(id), @@ -16,5 +16,5 @@ class OpinionChoice( EntityDeletedAt by EntityDeletedAtImp() open class OpinionChoiceRef( - id: UUID -) : UuidEntity(id) \ No newline at end of file + id: UUID? +) : UuidEntity(id ?: UUID.randomUUID()) \ No newline at end of file diff --git a/src/main/kotlin/fr/dcproject/repository/Opinion.kt b/src/main/kotlin/fr/dcproject/repository/Opinion.kt index e777541..c652888 100644 --- a/src/main/kotlin/fr/dcproject/repository/Opinion.kt +++ b/src/main/kotlin/fr/dcproject/repository/Opinion.kt @@ -43,6 +43,12 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI { .selectOne( "id" to id ) + + fun upsertOpinionChoice(opinionChoice: OpinionChoiceEntity): OpinionChoiceEntity = requester + .getFunction("upsert_opinion_choice") + .selectOne( + "resource" to opinionChoice + )!! } open class Opinion(requester: Requester) : OpinionChoice(requester) { diff --git a/src/main/resources/sql/functions/opinion/upsert_opinion_choice.sql b/src/main/resources/sql/functions/opinion/upsert_opinion_choice.sql new file mode 100644 index 0000000..496ae37 --- /dev/null +++ b/src/main/resources/sql/functions/opinion/upsert_opinion_choice.sql @@ -0,0 +1,20 @@ +create or replace function upsert_opinion_choice(inout resource json) + language plpgsql as +$$ +declare + _id uuid = coalesce((resource->>'id')::uuid, uuid_generate_v4()); +begin + insert into opinion_choice (id, name, target) + select + _id, + name, + target + from json_populate_record(null::opinion_choice, resource) + on conflict (name) do update set + target = excluded.target; + + select find_opinion_choice_by_id(_id) into resource; +end; +$$; + +-- drop function if exists upsert_opinion_choice(json); \ No newline at end of file diff --git a/src/test/kotlin/feature/OpinionSteps.kt b/src/test/kotlin/feature/OpinionSteps.kt index 95afff3..22e7cb9 100644 --- a/src/test/kotlin/feature/OpinionSteps.kt +++ b/src/test/kotlin/feature/OpinionSteps.kt @@ -1,11 +1,13 @@ package feature import fr.dcproject.entity.OpinionArticle +import fr.dcproject.entity.OpinionChoice 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 java.util.* import fr.dcproject.repository.Article as ArticleRepository import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.OpinionArticle as OpinionRepository @@ -13,33 +15,60 @@ 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 choice {string}") { name: String -> + val opinionChoice = OpinionChoice( + name = name, + target = listOf() + ) + get().upsertOpinionChoice(opinionChoice) + } + + Given("I have an opinion choice {string} with ID {string}") { name: String, id: String -> + val opinionChoice = OpinionChoice( + id = id.toUUID(), + name = name, + target = listOf() + ) + get().upsertOpinionChoice(opinionChoice) + } + + Given("I have an opinion {string} on article {string} created by {word} {word}") { opinionChoiceName: String, articleId: String, firstName: String, lastName: String -> + createOpinion(opinionChoiceName, articleId, firstName, lastName) + } + + Given("I have an opinion {string} on article {string} created by {word} {word} with ID {string}") { opinionChoiceName: String, articleId: String, firstName: String, lastName: String, id: String -> + createOpinion(opinionChoiceName, articleId, firstName, lastName, id) } 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) - } + createOpinionOnArticle(extraInfo) } } + + private fun createOpinion(opinionChoiceName: String, articleId: String, firstName: String, lastName: String, id: String? = null) { + val opinion = OpinionArticle( + id = id?.toUUID() ?: UUID.randomUUID(), + choice = get().findOpinionsChoiceByName(opinionChoiceName) + ?: error("Opinion Choice not exist"), + target = get().findById(articleId.toUUID()) ?: error("Article not exist"), + createdBy = get().findByUsername("$firstName-$lastName".toLowerCase().replace(' ', '-')) ?: error("Citizen not exist") + ) + get().opinion(opinion) + } + + private fun createOpinionOnArticle(extraInfo: DataTable? = null) { + val params = extraInfo?.asMap(String::class.java, String::class.java) + val username = params?.get("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/opinion.feature b/src/test/resources/feature/opinion.feature index 98eff39..d3a59f5 100644 --- a/src/test/resources/feature/opinion.feature +++ b/src/test/resources/feature/opinion.feature @@ -1,63 +1,73 @@ @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 + Given I have an opinion choice "Opinion1" + And I have an opinion choice "Opinion2" When I send a GET request to "/opinions" Then the response status code should be 200 And the JSON should contain: | [0]name | Opinion1 | + | [1]name | Opinion2 | + + Scenario: Can get one opinion Choices + Given I have an opinion choice "Opinion3" with ID "347ec243-0e76-4ab5-9884-7bd503cf5ab5" + When I send a GET request to "/opinions/347ec243-0e76-4ab5-9884-7bd503cf5ab5" + Then the response status code should be 200 + And the JSON should contain: + | name | Opinion3 | Scenario: Can create opinion on article - Given I have citizen: - | id | 2f414045-95d9-42ca-a3a9-8cdde52ad253 | - | firstName | Isaac | - | lastName | Newton | + Given I have citizen Isaac Newton with 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 | + | id | 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b | | createdBy | Isaac Newton | + And I have an opinion choice "Opinion4" with ID "0f4f1721-3136-44f1-9f31-1459f3317b15" + And I have an opinion "Opinion4" on article "9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" created by Isaac Newton with ID "74e93e12-556b-4399-95a6-04f93a4dd66c" When I send a PUT request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/opinions" with body: """ { - "opinion_choice": "6e978eb5-3c48-0def-b093-e01f43983adb" + "opinion_choice": "0f4f1721-3136-44f1-9f31-1459f3317b15" } """ 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" + Given I have article with ID "4eb8a2c6-ba63-4c1e-919f-72017132a54e" + And I have citizen Albert Jacquard with ID "c6392fc4-56f5-461b-8015-953a4da3029f" + And I am authenticated as Albert Jacquard + And I have an opinion choice "Opinion5" with ID "74d6e105-9aa6-4589-8776-82ce260bb6f4" + And I have an opinion "Opinion5" on article "4eb8a2c6-ba63-4c1e-919f-72017132a54e" created by Albert Jacquard with ID "994660e7-e9f4-4ae9-9290-b34d78663c7a" + When I send a GET request to "/citizens/c6392fc4-56f5-461b-8015-953a4da3029f/opinions?id=4eb8a2c6-ba63-4c1e-919f-72017132a54e" Then the response status code should be 200 And the JSON should contain: - | [0].name | Opinion2 | + | [0].name | Opinion5 | Scenario: Can recieve opinion aggregation with article - When I send a GET request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b" + Given I have an opinion choice "Opinion6" + And I have an opinion choice "Opinion7" + And I have an opinion choice "Opinion8" + And I have citizen James Watt + And I have citizen Paul Langevin + And I have article with ID "bda8940a-6792-4f2b-936a-ba5c805c8487" + And I have an opinion "Opinion6" on article "bda8940a-6792-4f2b-936a-ba5c805c8487" created by James Watt + And I have an opinion "Opinion7" on article "bda8940a-6792-4f2b-936a-ba5c805c8487" created by James Watt + And I have an opinion "Opinion7" on article "bda8940a-6792-4f2b-936a-ba5c805c8487" created by Paul Langevin + When I send a GET request to "/articles/bda8940a-6792-4f2b-936a-ba5c805c8487" Then the response status code should be 200 And the JSON should contain: - | opinions.Opinion2 | 1 | + | opinions.Opinion6 | 1 | + | opinions.Opinion7 | 2 | Scenario: Can get all opinion of one citizen - Given I have citizen: - | id | c1542096-3431-432d-8e35-9dc071d4c818 | - | firstName | Albert | - | lastName | Einstein | + Given I have citizen Albert Einstein with 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 | + And I have an opinion choice "Opinion9" + And I have article with ID "8651b530-ac1b-4214-a784-706781371074" + And I have an opinion "Opinion9" on article "8651b530-ac1b-4214-a784-706781371074" created by 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 | + | result[0].name | Opinion9 | diff --git a/src/test/sql/opinion.sql b/src/test/sql/opinion.sql index 730186c..41b6fe2 100644 --- a/src/test/sql/opinion.sql +++ b/src/test/sql/opinion.sql @@ -58,15 +58,10 @@ begin -- upsert article select upsert_article(created_article) into created_article; - - insert into opinion_choice(id, name, target) - values (opinion_choice1_id, 'Opinion1', '{article}'); - - insert into opinion_choice(id, name) - values (opinion_choice2_id, 'Opinion2'); - - insert into opinion_choice(name, target) - values ('Opinion3', '{article}'); + select (h->>'id')::uuid into opinion_choice1_id from upsert_opinion_choice('{"name": "Opinion1", "target":["article"]}') h; + assert opinion_choice1_id is not null, 'Opinion choice must be return json with id'; + select (h->>'id')::uuid into opinion_choice2_id from upsert_opinion_choice('{"name": "Opinion2"}') h; + perform upsert_opinion_choice('{"name": "Opinion3", "target":["article"]}') h; perform upsert_opinion( resource => json_build_object(