Continue to implement opinion
improve target reference Improve Tests for Opinion fix SQL:upsert_opinion
This commit is contained in:
@@ -197,6 +197,7 @@ fun Application.module(env: Env = PROD) {
|
||||
commentConstitution(get())
|
||||
voteArticle(get(), get(), get())
|
||||
voteConstitution(get())
|
||||
opinionArticle(get())
|
||||
opinionChoice(get())
|
||||
definition()
|
||||
}
|
||||
@@ -215,6 +216,9 @@ fun Application.module(env: Env = PROD) {
|
||||
exception<NotFoundException> { e ->
|
||||
call.respond(HttpStatusCode.BadRequest, e.message!!)
|
||||
}
|
||||
exception<ForbiddenException> {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
}
|
||||
|
||||
install(CORS) {
|
||||
|
||||
@@ -32,8 +32,6 @@ open class Comment<T : TargetI>(
|
||||
target = parent.target,
|
||||
content = content
|
||||
)
|
||||
|
||||
override val reference get() = TargetI.getReference(this)
|
||||
}
|
||||
|
||||
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 java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSuperclassOf
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
interface ExtraI<T : TargetI> :
|
||||
UuidEntityI,
|
||||
@@ -26,16 +26,18 @@ interface TargetI : UuidEntityI {
|
||||
enum class TargetName(val targetReference: String) {
|
||||
Article("article"),
|
||||
Constitution("constitution"),
|
||||
Comment("comment")
|
||||
Comment("comment"),
|
||||
Opinion("opinion")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <T : TargetI> getReference(t: KClass<T>): String {
|
||||
return when {
|
||||
t.isSuperclassOf(Article::class) -> TargetName.Article.targetReference
|
||||
t.isSuperclassOf(Constitution::class) -> TargetName.Constitution.targetReference
|
||||
t.isSuperclassOf(Comment::class) -> TargetName.Comment.targetReference
|
||||
else -> throw error("target not implemented")
|
||||
t.isSubclassOf(ArticleRef::class) -> TargetName.Article.targetReference
|
||||
t.isSubclassOf(ConstitutionRef::class) -> TargetName.Constitution.targetReference
|
||||
t.isSubclassOf(CommentRef::class) -> TargetName.Comment.targetReference
|
||||
t.isSubclassOf(Opinion::class) -> TargetName.Opinion.targetReference
|
||||
else -> throw error("target not implemented: ${t.qualifiedName}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package fr.dcproject.entity
|
||||
|
||||
import fr.postgresjson.entity.immutable.*
|
||||
import fr.postgresjson.entity.immutable.EntityCreatedAt
|
||||
import fr.postgresjson.entity.immutable.EntityCreatedAtImp
|
||||
import fr.postgresjson.entity.immutable.EntityCreatedBy
|
||||
import fr.postgresjson.entity.immutable.EntityCreatedByImp
|
||||
import java.util.*
|
||||
|
||||
open class Opinion<T : TargetI>(
|
||||
@@ -9,11 +12,16 @@ open class Opinion<T : TargetI>(
|
||||
override val target: T,
|
||||
val choice: OpinionChoice
|
||||
) : ExtraI<T>,
|
||||
UuidEntity(id),
|
||||
TargetRef(id),
|
||||
EntityCreatedAt by EntityCreatedAtImp(),
|
||||
EntityCreatedBy<CitizenBasicI> by EntityCreatedByImp(createdBy) {
|
||||
|
||||
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(
|
||||
id: UUID,
|
||||
val name: String,
|
||||
val target: List<String>
|
||||
val target: List<String>?
|
||||
) : OpinionChoiceRef(id),
|
||||
EntityCreatedAt by EntityCreatedAtImp(),
|
||||
EntityDeletedAt by EntityDeletedAtImp()
|
||||
|
||||
@@ -3,13 +3,13 @@ package fr.dcproject.entity
|
||||
import fr.postgresjson.entity.EntityI
|
||||
|
||||
class OpinionAggregation(
|
||||
override val entries: Set<Map.Entry<String, Int>> = emptySet()
|
||||
) : AbstractMap<String, Int>(), EntityI
|
||||
private val underlying: MutableMap<String, Any> = mutableMapOf()
|
||||
) : MutableMap<String, Any> by underlying, EntityI
|
||||
|
||||
interface Opinionable {
|
||||
val opinions: MutableMap<String, Int>
|
||||
var opinions: MutableMap<String, Int>
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import fr.dcproject.entity.CitizenRef
|
||||
import fr.postgresjson.entity.EntityI
|
||||
import io.ktor.application.ApplicationCall
|
||||
|
||||
interface Request
|
||||
|
||||
interface RequestBuilder<E: EntityI> : Request {
|
||||
fun create(): E
|
||||
interface RequestBuilder<E> {
|
||||
suspend fun getContent(call: ApplicationCall): E
|
||||
}
|
||||
|
||||
interface RequestBuilderWithCreator<C: CitizenRef, E: EntityI> : Request {
|
||||
fun create(citizen: C): E
|
||||
}
|
||||
suspend fun <E> ApplicationCall.getContent(builder: RequestBuilder<E>) = builder.getContent(this)
|
||||
|
||||
@@ -11,6 +11,7 @@ import net.pearx.kasechange.toSnakeCase
|
||||
import java.util.*
|
||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||
import fr.dcproject.entity.Opinion as OpinionEntity
|
||||
import fr.dcproject.entity.OpinionArticle as OpinionArticleEntity
|
||||
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity
|
||||
|
||||
open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
||||
@@ -25,6 +26,14 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
||||
"targets" to targets
|
||||
)
|
||||
|
||||
/**
|
||||
* find opinion choices by name
|
||||
*/
|
||||
fun findOpinionsChoiceByName(name: String): OpinionChoiceEntity? =
|
||||
findOpinionsChoices().first {
|
||||
it.name == name
|
||||
}
|
||||
|
||||
/**
|
||||
* find one opinion choices by id
|
||||
*/
|
||||
@@ -104,9 +113,18 @@ open class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requeste
|
||||
.select(page, limit,
|
||||
"sort" to sort?.toSnakeCase(),
|
||||
"direction" to direction,
|
||||
"citizen" to citizen.id
|
||||
"citizen_id" to citizen.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class OpinionArticle(requester: Requester) : Opinion<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
|
||||
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.request.ArticleOpinionRequest
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.entity.OpinionArticle
|
||||
import fr.dcproject.entity.OpinionChoiceRef
|
||||
import fr.dcproject.entity.request.RequestBuilder
|
||||
import fr.dcproject.entity.request.getContent
|
||||
import fr.dcproject.repository.OpinionChoice
|
||||
import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
|
||||
import fr.dcproject.security.voter.assertCan
|
||||
import fr.dcproject.utils.toUUID
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.features.BadRequestException
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
@@ -17,6 +24,7 @@ import io.ktor.routing.Route
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.get
|
||||
import java.util.*
|
||||
import fr.dcproject.entity.Article as ArticleEntity
|
||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
|
||||
@@ -24,7 +32,7 @@ import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
|
||||
@KtorExperimentalLocationsAPI
|
||||
object OpinionArticlePaths {
|
||||
/**
|
||||
* Get paginated opinion of citizen for one article
|
||||
* Get paginated opinions of citizen for all articles
|
||||
*/
|
||||
@Location("/citizens/{citizen}/opinions/articles")
|
||||
class CitizenOpinionArticleRequest(
|
||||
@@ -36,16 +44,37 @@ object OpinionArticlePaths {
|
||||
/**
|
||||
* Put an opinion on one article
|
||||
*/
|
||||
@Location("/articles/{article}/opinons")
|
||||
class ArticleOpinion(val article: ArticleEntity)
|
||||
@Location("/articles/{article}/opinions")
|
||||
@KtorExperimentalAPI
|
||||
class ArticleOpinion(val article: ArticleEntity) : RequestBuilder<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
|
||||
*/
|
||||
@Location("/citizen/{citizen}/opinions")
|
||||
class CitizenOpinions(val citizen: CitizenEntity, id: List<String>): KoinComponent {
|
||||
@Location("/citizens/{citizen}/opinions")
|
||||
class CitizenOpinions(val citizen: CitizenEntity, id: List<String>) : KoinComponent {
|
||||
val id: List<UUID> = id.toUUID()
|
||||
val opinionsEntities = get<OpinionArticleRepository>()
|
||||
.findCitizenOpinionsByTargets(citizen, id.toUUID())
|
||||
.findCitizenOpinionsByTargets(citizen, this.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +93,12 @@ fun Route.opinionArticle(repo: OpinionArticleRepository) {
|
||||
}
|
||||
|
||||
put<OpinionArticlePaths.ArticleOpinion> {
|
||||
val optionArticle = call.receive<ArticleOpinionRequest>().create(citizen)
|
||||
assertCan(VIEW, optionArticle)
|
||||
|
||||
call.respond(HttpStatusCode.Created, optionArticle)
|
||||
call.getContent(it)
|
||||
.let { opinion ->
|
||||
assertCan(VIEW, opinion)
|
||||
repo.opinion(opinion)
|
||||
}.let {
|
||||
call.respond(HttpStatusCode.Created, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.entity.OpinionChoice
|
||||
import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
|
||||
import fr.dcproject.security.voter.OpinionChoiceVoter.Action.VIEW
|
||||
import fr.dcproject.security.voter.assertCan
|
||||
import io.ktor.application.call
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
@@ -17,7 +17,7 @@ object OpinionChoicePaths {
|
||||
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
|
||||
|
||||
@Location("/opinions")
|
||||
class OpinionChoicesRequest(val targets: List<String>)
|
||||
class OpinionChoicesRequest(val targets: List<String> = emptyList())
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
|
||||
@@ -37,9 +37,10 @@ class OpinionVoter : Voter {
|
||||
}
|
||||
|
||||
if (action == Action.DELETE) {
|
||||
return if (subject is Opinion<*>
|
||||
&& user != null
|
||||
&& subject.createdBy.user.id == user.id)
|
||||
return if (subject is Opinion<*> &&
|
||||
user != null &&
|
||||
subject.createdBy.user.id == user.id
|
||||
)
|
||||
Vote.GRANTED
|
||||
else Vote.DENIED
|
||||
}
|
||||
|
||||
@@ -650,6 +650,123 @@ paths:
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
|
||||
/articles/{article}/opinions:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/article'
|
||||
put:
|
||||
security:
|
||||
- JWTAuth: []
|
||||
summary: Add Opinion on one article
|
||||
tags:
|
||||
- opinion
|
||||
- article
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ArticleOpinionRequest'
|
||||
responses:
|
||||
201:
|
||||
description: Return the opinion
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Opinion'
|
||||
401:
|
||||
$ref: '#/components/responses/401'
|
||||
/opinions:
|
||||
get:
|
||||
summary: Get all opinions choices
|
||||
tags:
|
||||
- opinion
|
||||
parameters:
|
||||
- in: query
|
||||
required: false
|
||||
name: targets
|
||||
description: opinion available for defined target
|
||||
example:
|
||||
- article
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: return
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/OpinionChoices'
|
||||
/opinions/{opinion}:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/opinion'
|
||||
get:
|
||||
summary: Get one opinion Choices
|
||||
tags:
|
||||
- opinion
|
||||
responses:
|
||||
200:
|
||||
description: return
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/OpinionChoice'
|
||||
/citizens/{citizen}/opinions:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/citizen'
|
||||
get:
|
||||
security:
|
||||
- JWTAuth: []
|
||||
summary: Get all opinions of citizen filtered by target ids
|
||||
tags:
|
||||
- opinion
|
||||
- citizen
|
||||
parameters:
|
||||
- in: query
|
||||
required: true
|
||||
name: id
|
||||
description: target ids
|
||||
example:
|
||||
- 9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
200:
|
||||
description: Opinions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Opinion'
|
||||
/citizens/{citizen}/opinions/articles:
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/citizen'
|
||||
get:
|
||||
security:
|
||||
- JWTAuth: []
|
||||
summary: Get all opinions of one citizen
|
||||
tags:
|
||||
- opinion
|
||||
- citizen
|
||||
responses:
|
||||
200:
|
||||
description: Opinions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Paginated'
|
||||
- type: object
|
||||
properties:
|
||||
result:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Opinion'
|
||||
|
||||
|
||||
/citizens/{citizen}/votes/articles:
|
||||
parameters:
|
||||
@@ -789,7 +906,7 @@ components:
|
||||
name: citizen
|
||||
in: path
|
||||
description: ID of citizen
|
||||
example: 4d673bfa-eaef-4290-b52f-85a9c8a7eba5
|
||||
example: 6434f4f9-f570-f22a-c134-8668350651ff
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
@@ -804,6 +921,15 @@ components:
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
opinion:
|
||||
in: path
|
||||
required: true
|
||||
name: opinion
|
||||
description: Opinion ID
|
||||
example: 6e978eb5-3c48-0def-b093-e01f43983adb
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
constitution:
|
||||
name: constitution
|
||||
@@ -861,6 +987,13 @@ components:
|
||||
updated_at:
|
||||
type: string
|
||||
format: 'date-time'
|
||||
DeletedAt:
|
||||
properties:
|
||||
deleted_at:
|
||||
type: string
|
||||
format: 'date-time'
|
||||
deleted:
|
||||
type: boolean
|
||||
|
||||
versionId:
|
||||
properties:
|
||||
@@ -1221,7 +1354,70 @@ components:
|
||||
Opinion1: 1
|
||||
Opinion2: 55
|
||||
|
||||
ArticleOpinionRequest:
|
||||
type: object
|
||||
properties:
|
||||
opinion_choice:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 6e978eb5-3c48-0def-b093-e01f43983adb
|
||||
|
||||
OpinionChoices:
|
||||
description: Opinion Choice
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/OpinionChoice'
|
||||
|
||||
OpinionChoice:
|
||||
description: Opinion Choice
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
example: opinion1
|
||||
target:
|
||||
type: array
|
||||
required: false
|
||||
nullable: true
|
||||
items:
|
||||
type: string
|
||||
description: the name of the target
|
||||
- $ref: '#/components/schemas/CreatedAt'
|
||||
- $ref: '#/components/schemas/DeletedAt'
|
||||
|
||||
Opinion:
|
||||
description: Opinion
|
||||
allOf:
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
example: opinion1
|
||||
target:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
reference:
|
||||
type: string
|
||||
example: article
|
||||
choice:
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/OpinionChoice'
|
||||
reference:
|
||||
type: string
|
||||
example: opinion_on_article
|
||||
- $ref: '#/components/schemas/CreatedBy'
|
||||
- $ref: '#/components/schemas/CreatedAt'
|
||||
|
||||
|
||||
|
||||
@@ -1243,11 +1439,7 @@ components:
|
||||
required: true
|
||||
|
||||
servers:
|
||||
- description: localhost 80
|
||||
url: http://localhost
|
||||
- description: localhost 8080
|
||||
- description: localhost
|
||||
url: http://localhost:8080
|
||||
- description: production
|
||||
url: http://dc-project.fr
|
||||
- description: SwaggerHub API Auto Mocking
|
||||
url: https://virtserver.swaggerhub.com/flecomte/dc-project/0.1
|
||||
url: http://dc-project.fr
|
||||
@@ -7,7 +7,10 @@ begin
|
||||
delete from opinion_choice;
|
||||
|
||||
insert into opinion_choice (id, name, target)
|
||||
select uuid_in(md5('opinion_choice'||row_number() over ())::cstring), 'Opinion'||row_number() over (), '{article}'
|
||||
select
|
||||
uuid_in(md5('opinion_choice'||row_number() over ())::cstring),
|
||||
'Opinion'||row_number() over (),
|
||||
case when row_number() over () % 5 = 0 then null else '{article}'::text[] end
|
||||
from generate_series(0,20);
|
||||
|
||||
for i in 0..9 loop
|
||||
|
||||
@@ -11,11 +11,17 @@ begin
|
||||
find_article_by_id(_id)
|
||||
when 'constitution'::regclass then
|
||||
find_constitution_by_id(_id)
|
||||
when 'comment'::regclass then
|
||||
find_comment_by_id(_id)
|
||||
when 'opinion'::regclass then
|
||||
find_opinion_by_id(_id)
|
||||
else
|
||||
json_build_object('id', _id)
|
||||
json_build_object('id', _id, 'reference', _reference)
|
||||
end
|
||||
into resource;
|
||||
end;
|
||||
|
||||
resource = resource::jsonb || jsonb_build_object('reference', _reference);
|
||||
end
|
||||
$$;
|
||||
|
||||
-- drop function if exists find_reference_by_id(uuid, regclass, out json);
|
||||
|
||||
@@ -18,6 +18,8 @@ begin
|
||||
from (
|
||||
select
|
||||
o.*,
|
||||
find_reference_by_id(o.target_id, o.target_reference) as target,
|
||||
find_citizen_by_id(o.created_by_id) as created_by,
|
||||
to_json(ol) as choice
|
||||
from opinion as o
|
||||
join opinion_choice ol on o.choice_id = ol.id
|
||||
|
||||
@@ -11,6 +11,8 @@ begin
|
||||
from (
|
||||
select
|
||||
o.*,
|
||||
find_reference_by_id(o.target_id, o.target_reference) as target,
|
||||
find_citizen_by_id(o.created_by_id) as created_by,
|
||||
to_json(ol) as choice
|
||||
from opinion as o
|
||||
join opinion_choice ol on o.choice_id = ol.id
|
||||
|
||||
@@ -9,6 +9,8 @@ begin
|
||||
from (
|
||||
select
|
||||
o.*,
|
||||
find_reference_by_id(o.target_id, o.target_reference) as target,
|
||||
find_citizen_by_id(o.created_by_id) as created_by,
|
||||
to_json(ol) as choice
|
||||
from opinion as o
|
||||
join opinion_choice ol on o.choice_id = ol.id
|
||||
|
||||
@@ -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.*
|
||||
from opinion_choice ol
|
||||
where (ol.deleted_at is null or ol.deleted_at > now())
|
||||
and (ol.target is null or targets is null or array_length(targets, 1) = 0 or ol.target && targets)
|
||||
and (
|
||||
ol.target is null or array_length(ol.target, 1) is null -- if choice is compatible with all target
|
||||
or targets is null or array_length(targets, 1) is null -- if no target defined
|
||||
or (ol.target && targets) -- if target is compatible
|
||||
)
|
||||
|
||||
order by ol.name
|
||||
) t;
|
||||
@@ -17,4 +21,4 @@ $$;
|
||||
|
||||
-- drop function if exists find_opinions();
|
||||
|
||||
-- select find_opinions();
|
||||
-- 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);
|
||||
Reference in New Issue
Block a user