Continue to implement opinion
improve target reference Improve Tests for Opinion fix SQL:upsert_opinion
This commit is contained in:
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
dcproject
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -7,7 +7,7 @@
|
|||||||
<component name="JavaScriptSettings">
|
<component name="JavaScriptSettings">
|
||||||
<option name="languageLevel" value="ES6" />
|
<option name="languageLevel" value="ES6" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="true" project-jdk-name="openjdk-13.0.2" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="adopt-openjdk-11" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
5
.idea/runConfigurations/All_Tests___Lint.xml
generated
5
.idea/runConfigurations/All_Tests___Lint.xml
generated
@@ -1,14 +1,11 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All Tests + Lint" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
|
<configuration default="false" name="All Tests + Lint" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
|
||||||
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
|
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
|
||||||
<module name="dcproject.test" />
|
|
||||||
<useClassPathOnly />
|
<useClassPathOnly />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
|
||||||
<option name="ALTERNATIVE_JRE_PATH" value="11" />
|
|
||||||
<option name="PACKAGE_NAME" value="fr.dcproject" />
|
<option name="PACKAGE_NAME" value="fr.dcproject" />
|
||||||
<option name="MAIN_CLASS_NAME" value="" />
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
<option name="METHOD_NAME" value="" />
|
<option name="METHOD_NAME" value="" />
|
||||||
<option name="TEST_OBJECT" value="directory" />
|
<option name="TEST_OBJECT" value="package" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
|||||||
26
.idea/runConfigurations/All_Tests___Lint__offline_.xml
generated
Normal file
26
.idea/runConfigurations/All_Tests___Lint__offline_.xml
generated
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All Tests + Lint (offline)" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
|
||||||
|
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
|
||||||
|
<useClassPathOnly />
|
||||||
|
<option name="PACKAGE_NAME" value="fr.dcproject" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="tags" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea -Dcucumber.options="--tags ~@online"" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="wholeProject" />
|
||||||
|
</option>
|
||||||
|
<envs>
|
||||||
|
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
|
||||||
|
</envs>
|
||||||
|
<dir value="$PROJECT_DIR$" />
|
||||||
|
<tag value="!online" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Lint" run_configuration_type="GradleRunConfiguration" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Test All SQL" run_configuration_type="RunSql" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
25
.idea/runConfigurations/All_Tests__offline_.xml
generated
Normal file
25
.idea/runConfigurations/All_Tests__offline_.xml
generated
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All Tests (offline)" type="JUnit" factoryName="JUnit" show_console_on_std_err="true">
|
||||||
|
<output_file path="$PROJECT_DIR$/var/log/test/out.log" is_save="true" />
|
||||||
|
<module name="dcproject.test" />
|
||||||
|
<useClassPathOnly />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="RunCucumberTest" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="class" />
|
||||||
|
<option name="VM_PARAMETERS" value="-ea -Dcucumber.options="--tags ~@online"" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="wholeProject" />
|
||||||
|
</option>
|
||||||
|
<envs>
|
||||||
|
<env name="SEND_GRID_KEY" value="$SEND_GRID_KEY$" />
|
||||||
|
</envs>
|
||||||
|
<dir value="$PROJECT_DIR$" />
|
||||||
|
<tag value="!online" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All Tests + Lint" run_configuration_type="JUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All Tests + Lint" run_configuration_type="JUnit" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Sonarqube" run_configuration_type="GradleRunConfiguration" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Sonarqube" run_configuration_type="GradleRunConfiguration" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Fixtures on Dev" run_configuration_type="RunSql" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Reset database schema and import Fixtures" run_configuration_type="RunSql" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
2
.idea/runConfigurations/Reset_DB___Run.xml
generated
2
.idea/runConfigurations/Reset_DB___Run.xml
generated
@@ -32,7 +32,7 @@
|
|||||||
</extension>
|
</extension>
|
||||||
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
|
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Fixtures on Dev" run_configuration_type="RunSql" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Reset database schema and import Fixtures" run_configuration_type="RunSql" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
28
.idea/runConfigurations/Reset_database_schema_and_import_Fixtures.xml
generated
Normal file
28
.idea/runConfigurations/Reset_database_schema_and_import_Fixtures.xml
generated
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Reset database schema and import Fixtures" type="RunSql" factoryName="Run SQL">
|
||||||
|
<option name="files">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/migrations/0000-init_schema.down.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/migrations/0000-init_schema.up.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/01-user.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/02-citizen.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/03-workgroup.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/04-article.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/05-constitution.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/06-follow.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/07-comment.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/08-vote.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/fixtures/09-opinion.sql" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="targets">
|
||||||
|
<list>
|
||||||
|
<Target>
|
||||||
|
<option name="dsId" value="28368159-3c2d-4612-8719-e55ce11b962a" />
|
||||||
|
<option name="namespace" value="database/"dc-project"/schema/"public"" />
|
||||||
|
</Target>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
6
.idea/runConfigurations/Test_All_SQL.xml
generated
6
.idea/runConfigurations/Test_All_SQL.xml
generated
@@ -38,7 +38,7 @@
|
|||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/helpers/find_reference_by_id.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/helpers/find_reference_by_id.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/helpers/random_between.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/helpers/random_between.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/count_opinion.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/count_opinion.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/opinion.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/user/change_user_password.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/user/change_user_password.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/user/check_user.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/user/check_user.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/user/find_user_by_id.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/user/find_user_by_id.sql" />
|
||||||
@@ -52,9 +52,11 @@
|
|||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_citizen_opinions.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_id.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_id.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_ids.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_citizen_opinions_by_target_ids.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_by_opinion.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choice_by_id.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choice_by_id.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choices.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_choices.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/opinion.sql" />
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/find_opinion_by_id.sql" />
|
||||||
|
<option value="$PROJECT_DIR$/src/main/resources/sql/functions/opinion/upsert_opinion.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/test/sql/user.sql" />
|
<option value="$PROJECT_DIR$/src/test/sql/user.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/test/sql/citizen.sql" />
|
<option value="$PROJECT_DIR$/src/test/sql/citizen.sql" />
|
||||||
<option value="$PROJECT_DIR$/src/test/sql/article.sql" />
|
<option value="$PROJECT_DIR$/src/test/sql/article.sql" />
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ logback_version=1.2.1
|
|||||||
postgresjson_version=0.1
|
postgresjson_version=0.1
|
||||||
koinVersion=2.0.1
|
koinVersion=2.0.1
|
||||||
jackson_version=2.9.9
|
jackson_version=2.9.9
|
||||||
cucumber_version=4.7.1
|
cucumber_version=5.1.3
|
||||||
systemProp.sonar.host.url=http://localhost:9000
|
systemProp.sonar.host.url=http://localhost:9000
|
||||||
systemProp.sonar.login=1196e8015c20035f1aa91e881b95ce9d6e879c8a
|
systemProp.sonar.login=1196e8015c20035f1aa91e881b95ce9d6e879c8a
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ fun Application.module(env: Env = PROD) {
|
|||||||
commentConstitution(get())
|
commentConstitution(get())
|
||||||
voteArticle(get(), get(), get())
|
voteArticle(get(), get(), get())
|
||||||
voteConstitution(get())
|
voteConstitution(get())
|
||||||
|
opinionArticle(get())
|
||||||
opinionChoice(get())
|
opinionChoice(get())
|
||||||
definition()
|
definition()
|
||||||
}
|
}
|
||||||
@@ -215,6 +216,9 @@ fun Application.module(env: Env = PROD) {
|
|||||||
exception<NotFoundException> { e ->
|
exception<NotFoundException> { e ->
|
||||||
call.respond(HttpStatusCode.BadRequest, e.message!!)
|
call.respond(HttpStatusCode.BadRequest, e.message!!)
|
||||||
}
|
}
|
||||||
|
exception<ForbiddenException> {
|
||||||
|
call.respond(HttpStatusCode.Forbidden)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
install(CORS) {
|
install(CORS) {
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ open class Comment<T : TargetI>(
|
|||||||
target = parent.target,
|
target = parent.target,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
|
|
||||||
override val reference get() = TargetI.getReference(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class CommentRef(id: UUID = UUID.randomUUID()) : CommentS(id)
|
open class CommentRef(id: UUID = UUID.randomUUID()) : CommentS(id)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import fr.postgresjson.entity.immutable.UuidEntity
|
|||||||
import fr.postgresjson.entity.immutable.UuidEntityI
|
import fr.postgresjson.entity.immutable.UuidEntityI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.isSuperclassOf
|
import kotlin.reflect.full.isSubclassOf
|
||||||
|
|
||||||
interface ExtraI<T : TargetI> :
|
interface ExtraI<T : TargetI> :
|
||||||
UuidEntityI,
|
UuidEntityI,
|
||||||
@@ -26,16 +26,18 @@ interface TargetI : UuidEntityI {
|
|||||||
enum class TargetName(val targetReference: String) {
|
enum class TargetName(val targetReference: String) {
|
||||||
Article("article"),
|
Article("article"),
|
||||||
Constitution("constitution"),
|
Constitution("constitution"),
|
||||||
Comment("comment")
|
Comment("comment"),
|
||||||
|
Opinion("opinion")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <T : TargetI> getReference(t: KClass<T>): String {
|
fun <T : TargetI> getReference(t: KClass<T>): String {
|
||||||
return when {
|
return when {
|
||||||
t.isSuperclassOf(Article::class) -> TargetName.Article.targetReference
|
t.isSubclassOf(ArticleRef::class) -> TargetName.Article.targetReference
|
||||||
t.isSuperclassOf(Constitution::class) -> TargetName.Constitution.targetReference
|
t.isSubclassOf(ConstitutionRef::class) -> TargetName.Constitution.targetReference
|
||||||
t.isSuperclassOf(Comment::class) -> TargetName.Comment.targetReference
|
t.isSubclassOf(CommentRef::class) -> TargetName.Comment.targetReference
|
||||||
else -> throw error("target not implemented")
|
t.isSubclassOf(Opinion::class) -> TargetName.Opinion.targetReference
|
||||||
|
else -> throw error("target not implemented: ${t.qualifiedName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package fr.dcproject.entity
|
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.*
|
import java.util.*
|
||||||
|
|
||||||
open class Opinion<T : TargetI>(
|
open class Opinion<T : TargetI>(
|
||||||
@@ -9,11 +12,16 @@ open class Opinion<T : TargetI>(
|
|||||||
override val target: T,
|
override val target: T,
|
||||||
val choice: OpinionChoice
|
val choice: OpinionChoice
|
||||||
) : ExtraI<T>,
|
) : ExtraI<T>,
|
||||||
UuidEntity(id),
|
TargetRef(id),
|
||||||
EntityCreatedAt by EntityCreatedAtImp(),
|
EntityCreatedAt by EntityCreatedAtImp(),
|
||||||
EntityCreatedBy<CitizenBasicI> by EntityCreatedByImp(createdBy) {
|
EntityCreatedBy<CitizenBasicI> by EntityCreatedByImp(createdBy) {
|
||||||
|
|
||||||
fun getName(): String = choice.name
|
fun getName(): String = choice.name
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias OpinionArticle = Opinion<Article>
|
class OpinionArticle(
|
||||||
|
id: UUID = UUID.randomUUID(),
|
||||||
|
createdBy: CitizenBasic,
|
||||||
|
target: ArticleRef,
|
||||||
|
choice: OpinionChoice
|
||||||
|
) : Opinion<ArticleRef>(id, createdBy, target, choice)
|
||||||
@@ -10,7 +10,7 @@ import java.util.*
|
|||||||
class OpinionChoice(
|
class OpinionChoice(
|
||||||
id: UUID,
|
id: UUID,
|
||||||
val name: String,
|
val name: String,
|
||||||
val target: List<String>
|
val target: List<String>?
|
||||||
) : OpinionChoiceRef(id),
|
) : OpinionChoiceRef(id),
|
||||||
EntityCreatedAt by EntityCreatedAtImp(),
|
EntityCreatedAt by EntityCreatedAtImp(),
|
||||||
EntityDeletedAt by EntityDeletedAtImp()
|
EntityDeletedAt by EntityDeletedAtImp()
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package fr.dcproject.entity
|
|||||||
import fr.postgresjson.entity.EntityI
|
import fr.postgresjson.entity.EntityI
|
||||||
|
|
||||||
class OpinionAggregation(
|
class OpinionAggregation(
|
||||||
override val entries: Set<Map.Entry<String, Int>> = emptySet()
|
private val underlying: MutableMap<String, Any> = mutableMapOf()
|
||||||
) : AbstractMap<String, Int>(), EntityI
|
) : MutableMap<String, Any> by underlying, EntityI
|
||||||
|
|
||||||
interface Opinionable {
|
interface Opinionable {
|
||||||
val opinions: MutableMap<String, Int>
|
var opinions: MutableMap<String, Int>
|
||||||
}
|
}
|
||||||
|
|
||||||
class OpinionableImp : Opinionable {
|
class OpinionableImp : Opinionable {
|
||||||
override val opinions: MutableMap<String, Int> = mutableMapOf()
|
override var opinions: MutableMap<String, Int> = mutableMapOf()
|
||||||
}
|
}
|
||||||
@@ -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<Citizen, OpinionArticle>, KoinComponent {
|
|
||||||
val target = TargetRef(target.toUUID())
|
|
||||||
val opinionChoice = OpinionChoiceRef(opinionChoice.toUUID())
|
|
||||||
|
|
||||||
override fun create(citizen: Citizen): OpinionArticle {
|
|
||||||
return OpinionArticle(
|
|
||||||
choice = get<OpinionChoice>().findOpinionChoiceById(opinionChoice.id)!!,
|
|
||||||
target = get<Article>().findById(target.id)!!,
|
|
||||||
createdBy = citizen
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
package fr.dcproject.entity.request
|
package fr.dcproject.entity.request
|
||||||
|
|
||||||
import fr.dcproject.entity.CitizenRef
|
import io.ktor.application.ApplicationCall
|
||||||
import fr.postgresjson.entity.EntityI
|
|
||||||
|
|
||||||
interface Request
|
interface Request
|
||||||
|
|
||||||
interface RequestBuilder<E: EntityI> : Request {
|
interface RequestBuilder<E> {
|
||||||
fun create(): E
|
suspend fun getContent(call: ApplicationCall): E
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RequestBuilderWithCreator<C: CitizenRef, E: EntityI> : Request {
|
suspend fun <E> ApplicationCall.getContent(builder: RequestBuilder<E>) = builder.getContent(this)
|
||||||
fun create(citizen: C): E
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import net.pearx.kasechange.toSnakeCase
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||||
import fr.dcproject.entity.Opinion as OpinionEntity
|
import fr.dcproject.entity.Opinion as OpinionEntity
|
||||||
|
import fr.dcproject.entity.OpinionArticle as OpinionArticleEntity
|
||||||
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity
|
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity
|
||||||
|
|
||||||
open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
||||||
@@ -25,6 +26,14 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
|||||||
"targets" to targets
|
"targets" to targets
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find opinion choices by name
|
||||||
|
*/
|
||||||
|
fun findOpinionsChoiceByName(name: String): OpinionChoiceEntity? =
|
||||||
|
findOpinionsChoices().first {
|
||||||
|
it.name == name
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find one opinion choices by id
|
* find one opinion choices by id
|
||||||
*/
|
*/
|
||||||
@@ -104,9 +113,18 @@ open class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requeste
|
|||||||
.select(page, limit,
|
.select(page, limit,
|
||||||
"sort" to sort?.toSnakeCase(),
|
"sort" to sort?.toSnakeCase(),
|
||||||
"direction" to direction,
|
"direction" to direction,
|
||||||
"citizen" to citizen.id
|
"citizen_id" to citizen.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OpinionArticle(requester: Requester) : Opinion<Article>(requester)
|
class OpinionArticle(requester: Requester) : Opinion<Article>(requester) {
|
||||||
|
/**
|
||||||
|
* Create an Opinion on Article
|
||||||
|
*/
|
||||||
|
fun opinion(opinion: OpinionArticleEntity): OpinionArticleEntity {
|
||||||
|
return requester
|
||||||
|
.getFunction("upsert_opinion")
|
||||||
|
.selectOne(opinion) ?: error("query 'upsert_opinion' return null")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
package fr.dcproject.routes
|
package fr.dcproject.routes
|
||||||
|
|
||||||
import fr.dcproject.citizen
|
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.OpinionVoter.Action.VIEW
|
||||||
import fr.dcproject.security.voter.assertCan
|
import fr.dcproject.security.voter.assertCan
|
||||||
import fr.dcproject.utils.toUUID
|
import fr.dcproject.utils.toUUID
|
||||||
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
|
import io.ktor.features.BadRequestException
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
@@ -17,6 +24,7 @@ import io.ktor.routing.Route
|
|||||||
import io.ktor.util.KtorExperimentalAPI
|
import io.ktor.util.KtorExperimentalAPI
|
||||||
import org.koin.core.KoinComponent
|
import org.koin.core.KoinComponent
|
||||||
import org.koin.core.get
|
import org.koin.core.get
|
||||||
|
import java.util.*
|
||||||
import fr.dcproject.entity.Article as ArticleEntity
|
import fr.dcproject.entity.Article as ArticleEntity
|
||||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||||
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
|
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
|
||||||
@@ -24,7 +32,7 @@ import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
|
|||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
object OpinionArticlePaths {
|
object OpinionArticlePaths {
|
||||||
/**
|
/**
|
||||||
* Get paginated opinion of citizen for one article
|
* Get paginated opinions of citizen for all articles
|
||||||
*/
|
*/
|
||||||
@Location("/citizens/{citizen}/opinions/articles")
|
@Location("/citizens/{citizen}/opinions/articles")
|
||||||
class CitizenOpinionArticleRequest(
|
class CitizenOpinionArticleRequest(
|
||||||
@@ -36,16 +44,37 @@ object OpinionArticlePaths {
|
|||||||
/**
|
/**
|
||||||
* Put an opinion on one article
|
* Put an opinion on one article
|
||||||
*/
|
*/
|
||||||
@Location("/articles/{article}/opinons")
|
@Location("/articles/{article}/opinions")
|
||||||
class ArticleOpinion(val article: ArticleEntity)
|
@KtorExperimentalAPI
|
||||||
|
class ArticleOpinion(val article: ArticleEntity) : RequestBuilder<OpinionArticle> {
|
||||||
|
|
||||||
|
private class Content(
|
||||||
|
opinionChoice: String
|
||||||
|
) : KoinComponent {
|
||||||
|
val opinionChoice = OpinionChoiceRef(opinionChoice.toUUID())
|
||||||
|
|
||||||
|
fun create(citizen: Citizen, article: ArticleEntity): OpinionArticle {
|
||||||
|
return OpinionArticle(
|
||||||
|
choice = get<OpinionChoice>().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<Content>().create(call.citizen, article)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all Opinion of citizen on targets by target ids
|
* Get all Opinion of citizen on targets by target ids
|
||||||
*/
|
*/
|
||||||
@Location("/citizen/{citizen}/opinions")
|
@Location("/citizens/{citizen}/opinions")
|
||||||
class CitizenOpinions(val citizen: CitizenEntity, id: List<String>) : KoinComponent {
|
class CitizenOpinions(val citizen: CitizenEntity, id: List<String>) : KoinComponent {
|
||||||
|
val id: List<UUID> = id.toUUID()
|
||||||
val opinionsEntities = get<OpinionArticleRepository>()
|
val opinionsEntities = get<OpinionArticleRepository>()
|
||||||
.findCitizenOpinionsByTargets(citizen, id.toUUID())
|
.findCitizenOpinionsByTargets(citizen, this.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,9 +93,12 @@ fun Route.opinionArticle(repo: OpinionArticleRepository) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
put<OpinionArticlePaths.ArticleOpinion> {
|
put<OpinionArticlePaths.ArticleOpinion> {
|
||||||
val optionArticle = call.receive<ArticleOpinionRequest>().create(citizen)
|
call.getContent(it)
|
||||||
assertCan(VIEW, optionArticle)
|
.let { opinion ->
|
||||||
|
assertCan(VIEW, opinion)
|
||||||
call.respond(HttpStatusCode.Created, optionArticle)
|
repo.opinion(opinion)
|
||||||
|
}.let {
|
||||||
|
call.respond(HttpStatusCode.Created, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package fr.dcproject.routes
|
package fr.dcproject.routes
|
||||||
|
|
||||||
import fr.dcproject.entity.OpinionChoice
|
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 fr.dcproject.security.voter.assertCan
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -17,7 +17,7 @@ object OpinionChoicePaths {
|
|||||||
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
|
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
|
||||||
|
|
||||||
@Location("/opinions")
|
@Location("/opinions")
|
||||||
class OpinionChoicesRequest(val targets: List<String>)
|
class OpinionChoicesRequest(val targets: List<String> = emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
|
|||||||
@@ -37,9 +37,10 @@ class OpinionVoter : Voter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action == Action.DELETE) {
|
if (action == Action.DELETE) {
|
||||||
return if (subject is Opinion<*>
|
return if (subject is Opinion<*> &&
|
||||||
&& user != null
|
user != null &&
|
||||||
&& subject.createdBy.user.id == user.id)
|
subject.createdBy.user.id == user.id
|
||||||
|
)
|
||||||
Vote.GRANTED
|
Vote.GRANTED
|
||||||
else Vote.DENIED
|
else Vote.DENIED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -650,6 +650,123 @@ paths:
|
|||||||
401:
|
401:
|
||||||
$ref: '#/components/responses/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:
|
/citizens/{citizen}/votes/articles:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -789,7 +906,7 @@ components:
|
|||||||
name: citizen
|
name: citizen
|
||||||
in: path
|
in: path
|
||||||
description: ID of citizen
|
description: ID of citizen
|
||||||
example: 4d673bfa-eaef-4290-b52f-85a9c8a7eba5
|
example: 6434f4f9-f570-f22a-c134-8668350651ff
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
@@ -804,6 +921,15 @@ components:
|
|||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
opinion:
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
name: opinion
|
||||||
|
description: Opinion ID
|
||||||
|
example: 6e978eb5-3c48-0def-b093-e01f43983adb
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
|
||||||
constitution:
|
constitution:
|
||||||
name: constitution
|
name: constitution
|
||||||
@@ -861,6 +987,13 @@ components:
|
|||||||
updated_at:
|
updated_at:
|
||||||
type: string
|
type: string
|
||||||
format: 'date-time'
|
format: 'date-time'
|
||||||
|
DeletedAt:
|
||||||
|
properties:
|
||||||
|
deleted_at:
|
||||||
|
type: string
|
||||||
|
format: 'date-time'
|
||||||
|
deleted:
|
||||||
|
type: boolean
|
||||||
|
|
||||||
versionId:
|
versionId:
|
||||||
properties:
|
properties:
|
||||||
@@ -1221,7 +1354,70 @@ components:
|
|||||||
Opinion1: 1
|
Opinion1: 1
|
||||||
Opinion2: 55
|
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
|
required: true
|
||||||
|
|
||||||
servers:
|
servers:
|
||||||
- description: localhost 80
|
- description: localhost
|
||||||
url: http://localhost
|
|
||||||
- description: localhost 8080
|
|
||||||
url: http://localhost:8080
|
url: http://localhost:8080
|
||||||
- description: production
|
- description: production
|
||||||
url: http://dc-project.fr
|
url: http://dc-project.fr
|
||||||
- description: SwaggerHub API Auto Mocking
|
|
||||||
url: https://virtserver.swaggerhub.com/flecomte/dc-project/0.1
|
|
||||||
@@ -7,7 +7,10 @@ begin
|
|||||||
delete from opinion_choice;
|
delete from opinion_choice;
|
||||||
|
|
||||||
insert into opinion_choice (id, name, target)
|
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);
|
from generate_series(0,20);
|
||||||
|
|
||||||
for i in 0..9 loop
|
for i in 0..9 loop
|
||||||
|
|||||||
@@ -11,11 +11,17 @@ begin
|
|||||||
find_article_by_id(_id)
|
find_article_by_id(_id)
|
||||||
when 'constitution'::regclass then
|
when 'constitution'::regclass then
|
||||||
find_constitution_by_id(_id)
|
find_constitution_by_id(_id)
|
||||||
|
when 'comment'::regclass then
|
||||||
|
find_comment_by_id(_id)
|
||||||
|
when 'opinion'::regclass then
|
||||||
|
find_opinion_by_id(_id)
|
||||||
else
|
else
|
||||||
json_build_object('id', _id)
|
json_build_object('id', _id, 'reference', _reference)
|
||||||
end
|
end
|
||||||
into resource;
|
into resource;
|
||||||
end;
|
|
||||||
|
resource = resource::jsonb || jsonb_build_object('reference', _reference);
|
||||||
|
end
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
-- drop function if exists find_reference_by_id(uuid, regclass, out json);
|
-- drop function if exists find_reference_by_id(uuid, regclass, out json);
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ begin
|
|||||||
from (
|
from (
|
||||||
select
|
select
|
||||||
o.*,
|
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
|
to_json(ol) as choice
|
||||||
from opinion as o
|
from opinion as o
|
||||||
join opinion_choice ol on o.choice_id = ol.id
|
join opinion_choice ol on o.choice_id = ol.id
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ begin
|
|||||||
from (
|
from (
|
||||||
select
|
select
|
||||||
o.*,
|
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
|
to_json(ol) as choice
|
||||||
from opinion as o
|
from opinion as o
|
||||||
join opinion_choice ol on o.choice_id = ol.id
|
join opinion_choice ol on o.choice_id = ol.id
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ begin
|
|||||||
from (
|
from (
|
||||||
select
|
select
|
||||||
o.*,
|
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
|
to_json(ol) as choice
|
||||||
from opinion as o
|
from opinion as o
|
||||||
join opinion_choice ol on o.choice_id = ol.id
|
join opinion_choice ol on o.choice_id = ol.id
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -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);
|
||||||
@@ -8,7 +8,11 @@ begin
|
|||||||
select ol.*
|
select ol.*
|
||||||
from opinion_choice ol
|
from opinion_choice ol
|
||||||
where (ol.deleted_at is null or ol.deleted_at > now())
|
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
|
order by ol.name
|
||||||
) t;
|
) t;
|
||||||
@@ -17,4 +21,4 @@ $$;
|
|||||||
|
|
||||||
-- drop function if exists find_opinions();
|
-- drop function if exists find_opinions();
|
||||||
|
|
||||||
-- select find_opinions();
|
-- select find_opinion_choices('{}');
|
||||||
|
|||||||
@@ -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);
|
|
||||||
23
src/main/resources/sql/functions/opinion/upsert_opinion.sql
Normal file
23
src/main/resources/sql/functions/opinion/upsert_opinion.sql
Normal file
@@ -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);
|
||||||
@@ -7,6 +7,7 @@ import fr.dcproject.module
|
|||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.server.testing.withTestApplication
|
import io.ktor.server.testing.withTestApplication
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
import io.ktor.util.KtorExperimentalAPI
|
||||||
|
import org.junit.jupiter.api.Tag
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import org.koin.test.AutoCloseKoinTest
|
import org.koin.test.AutoCloseKoinTest
|
||||||
@@ -18,6 +19,7 @@ import org.koin.test.get
|
|||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class MailerTest : KoinTest, AutoCloseKoinTest() {
|
class MailerTest : KoinTest, AutoCloseKoinTest() {
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("online")
|
||||||
fun `can be send an email`() {
|
fun `can be send an email`() {
|
||||||
withTestApplication({ module(Env.TEST) }) {
|
withTestApplication({ module(Env.TEST) }) {
|
||||||
get<Mailer>().sendEmail {
|
get<Mailer>().sendEmail {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import fr.dcproject.utils.LoggerDelegate
|
|||||||
import fr.postgresjson.connexion.Connection
|
import fr.postgresjson.connexion.Connection
|
||||||
import fr.postgresjson.connexion.Requester
|
import fr.postgresjson.connexion.Requester
|
||||||
import fr.postgresjson.migration.Migrations
|
import fr.postgresjson.migration.Migrations
|
||||||
import io.cucumber.core.api.Scenario
|
|
||||||
import io.cucumber.java8.En
|
import io.cucumber.java8.En
|
||||||
|
import io.cucumber.java8.Scenario
|
||||||
import io.cucumber.junit.Cucumber
|
import io.cucumber.junit.Cucumber
|
||||||
import io.cucumber.junit.CucumberOptions
|
import io.cucumber.junit.CucumberOptions
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package feature
|
|||||||
|
|
||||||
import fr.dcproject.entity.*
|
import fr.dcproject.entity.*
|
||||||
import fr.dcproject.repository.CommentArticle
|
import fr.dcproject.repository.CommentArticle
|
||||||
|
import fr.dcproject.utils.toUUID
|
||||||
|
import io.cucumber.datatable.DataTable
|
||||||
import io.cucumber.java8.En
|
import io.cucumber.java8.En
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.koin.test.KoinTest
|
import org.koin.test.KoinTest
|
||||||
@@ -16,6 +18,9 @@ import fr.dcproject.repository.Citizen as CitizenRepository
|
|||||||
|
|
||||||
class ArticleSteps : En, KoinTest {
|
class ArticleSteps : En, KoinTest {
|
||||||
init {
|
init {
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
Given("I have article with id {string}") { id: String ->
|
Given("I have article with id {string}") { id: String ->
|
||||||
var citizen = Citizen(
|
var citizen = Citizen(
|
||||||
name = CitizenI.Name("John", "Doe"),
|
name = CitizenI.Name("John", "Doe"),
|
||||||
@@ -40,6 +45,23 @@ class ArticleSteps : En, KoinTest {
|
|||||||
get<ArticleRepository>().upsert(article)
|
get<ArticleRepository>().upsert(article)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Given("I have article") { extraData: DataTable ->
|
||||||
|
extraData.asMap<String, String>(String::class.java, String::class.java).let { params ->
|
||||||
|
val username = params["createdBy"]?.toLowerCase()?.replace(' ', '-') ?: error("You must provide the 'createdBy' parameter")
|
||||||
|
val citizen = get<CitizenRepository>().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<ArticleRepository>().upsert(article)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Given("I have article with id {string} created by {string}") { id: String, username: String ->
|
Given("I have article with id {string} created by {string}") { id: String, username: String ->
|
||||||
val citizen = get<CitizenRepository>().findByUsername(username)!!
|
val citizen = get<CitizenRepository>().findByUsername(username)!!
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import fr.dcproject.JwtConfig
|
|||||||
import fr.dcproject.entity.Citizen
|
import fr.dcproject.entity.Citizen
|
||||||
import fr.dcproject.entity.CitizenI
|
import fr.dcproject.entity.CitizenI
|
||||||
import fr.dcproject.entity.User
|
import fr.dcproject.entity.User
|
||||||
|
import fr.dcproject.utils.toUUID
|
||||||
import fr.postgresjson.connexion.Requester
|
import fr.postgresjson.connexion.Requester
|
||||||
import io.cucumber.datatable.DataTable
|
import io.cucumber.datatable.DataTable
|
||||||
import io.cucumber.java8.En
|
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, String>(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<CitizenRepository>().insertWithUser(citizen)
|
||||||
|
}
|
||||||
|
|
||||||
|
Given("I am authenticated as {word} {word}") { firstName: String, lastName: String ->
|
||||||
|
val username = "$firstName-$lastName".toLowerCase()
|
||||||
|
val citizen = get<CitizenRepository>().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 ->
|
Given("I have citizen {word} {word} with id {string}") { firstName: String, lastName: String, id: String ->
|
||||||
val user = User(
|
val user = User(
|
||||||
id = UUID.randomUUID(),
|
id = UUID.randomUUID(),
|
||||||
|
|||||||
@@ -41,19 +41,19 @@ class KtorServerRestSteps : En {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findJsonElement(node: String): JsonElement {
|
private fun findJsonElement(path: String): JsonElement {
|
||||||
var jsonElement: JsonElement = responseJsonElement
|
var jsonElement: JsonElement = responseJsonElement
|
||||||
val elements = node.split(".")
|
|
||||||
|
|
||||||
elements.forEach {
|
path
|
||||||
val asArrayIndex = """\d+""".toRegex().find(it)
|
.split("].", "]", "[", ".")
|
||||||
|
.filter { it.trim().isNotBlank() }
|
||||||
jsonElement = if (asArrayIndex != null) {
|
.map { it.trim() }
|
||||||
val index = asArrayIndex.groups.first()!!
|
.forEach {
|
||||||
jsonElement.jsonArray.get(index.value.toInt())
|
jsonElement = if (jsonElement is JsonArray) {
|
||||||
|
jsonElement.jsonArray[it.toInt()]
|
||||||
} else {
|
} else {
|
||||||
jsonElement.jsonObject.get(it) ?: throw AssertionError("\"$node\" element not found on json response")
|
jsonElement.jsonObject[it]
|
||||||
}
|
} ?: throw AssertionError("\"$path\" element not found on json response")
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonElement
|
return jsonElement
|
||||||
|
|||||||
40
src/test/kotlin/feature/OpinionSteps.kt
Normal file
40
src/test/kotlin/feature/OpinionSteps.kt
Normal file
@@ -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, String>(String::class.java, String::class.java).let {
|
||||||
|
val opinion = OpinionArticle(
|
||||||
|
choice = get<OpinionChoiceRepository>().findOpinionsChoiceByName(opinionChoice) ?: error("Opinion Choice not exist"),
|
||||||
|
target = get<ArticleRepository>().findById(article.toUUID()) ?: error("Article not exist"),
|
||||||
|
createdBy = get<CitizenRepository>().findById(citizen.toUUID()) ?: error("Citizen not exist")
|
||||||
|
)
|
||||||
|
get<OpinionRepository>().opinion(opinion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Given("I have an opinion") { extraInfo: DataTable ->
|
||||||
|
extraInfo.asMap<String, String>(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<OpinionChoiceRepository>().findOpinionsChoiceByName(it) ?: error("Opinion Choice not exist")} ?: error("You must provide the 'opinion' parameter"),
|
||||||
|
target = params["article"]?.let { get<ArticleRepository>().findById(it.toUUID()) ?: error("Article not exist")} ?: error("You must provide the 'article' parameter"),
|
||||||
|
createdBy = get<CitizenRepository>().findByUsername(username) ?: error("Citizen not exist")
|
||||||
|
)
|
||||||
|
get<OpinionRepository>().opinion(opinion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ Feature: citizens routes
|
|||||||
And the response should contain object:
|
And the response should contain object:
|
||||||
| id | 64b7b379-2298-43ec-b428-ba134930cabd |
|
| id | 64b7b379-2298-43ec-b428-ba134930cabd |
|
||||||
|
|
||||||
|
@online
|
||||||
Scenario: Can be connect with SSO
|
Scenario: Can be connect with SSO
|
||||||
Given I have citizen:
|
Given I have citizen:
|
||||||
| id | c606110c-ff0e-4d09-a79e-74632d7bf7bd |
|
| id | c606110c-ff0e-4d09-a79e-74632d7bf7bd |
|
||||||
|
|||||||
59
src/test/resources/feature/opinion.feature
Normal file
59
src/test/resources/feature/opinion.feature
Normal file
@@ -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 |
|
||||||
@@ -38,8 +38,9 @@ declare
|
|||||||
"draft":false
|
"draft":false
|
||||||
}
|
}
|
||||||
$json$;
|
$json$;
|
||||||
opinion1 uuid = uuid_generate_v4();
|
opinion_choice1_id uuid = uuid_generate_v4();
|
||||||
opinion2 uuid = uuid_generate_v4();
|
opinion_choice2_id uuid = uuid_generate_v4();
|
||||||
|
opinion2 json;
|
||||||
begin
|
begin
|
||||||
-- insert user for context
|
-- insert user for context
|
||||||
select insert_user(created_user) into created_user;
|
select insert_user(created_user) into created_user;
|
||||||
@@ -59,32 +60,38 @@ begin
|
|||||||
|
|
||||||
|
|
||||||
insert into opinion_choice(id, name, target)
|
insert into opinion_choice(id, name, target)
|
||||||
values (opinion1, 'Opinion1', '{article}');
|
values (opinion_choice1_id, 'Opinion1', '{article}');
|
||||||
|
|
||||||
insert into opinion_choice(id, name, target)
|
insert into opinion_choice(id, name)
|
||||||
values (opinion2, 'Opinion2', '{article}');
|
values (opinion_choice2_id, 'Opinion2');
|
||||||
|
|
||||||
insert into opinion_choice(name, target)
|
insert into opinion_choice(name, target)
|
||||||
values ('Opinion3', '{article}');
|
values ('Opinion3', '{article}');
|
||||||
|
|
||||||
perform opinion(
|
perform upsert_opinion(
|
||||||
reference => 'article'::regclass,
|
resource => json_build_object(
|
||||||
_target_id => (created_article->>'id')::uuid,
|
'target', json_build_object('id', (created_article->'id'), 'reference', 'article'),
|
||||||
_created_by_id => _citizen_id,
|
'created_by', json_build_object('id', _citizen_id),
|
||||||
_opinion => opinion1
|
'choice', json_build_object('id', opinion_choice1_id)
|
||||||
);
|
)
|
||||||
perform opinion(
|
|
||||||
reference => 'article'::regclass,
|
|
||||||
_target_id => (created_article->>'id')::uuid,
|
|
||||||
_created_by_id => _citizen_id,
|
|
||||||
_opinion => opinion2
|
|
||||||
);
|
);
|
||||||
|
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 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
|
assert(select (a#>>'{opinions, Opinion1}')::int = 1
|
||||||
from find_article_by_id((created_article->>'id')::uuid) a), 'the article must be have a opinion';
|
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(
|
assert(
|
||||||
select (o#>>'{0, choice, name}') = 'Opinion1'
|
select (o#>>'{0, choice, name}') = 'Opinion1'
|
||||||
from find_citizen_opinions_by_target_id(_citizen_id, (created_article->>'id')::uuid) o),
|
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';
|
), 'find_opinion_choices must be return all opinions';
|
||||||
|
|
||||||
assert(
|
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';
|
), 'find_opinion_choice_by_id must return the opinion_choice';
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
|
|||||||
Reference in New Issue
Block a user