diff --git a/src/main/kotlin/fr/dcproject/component/vote/database/Vote.kt b/src/main/kotlin/fr/dcproject/component/vote/database/Vote.kt index c27e1e4..0fc6af0 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/database/Vote.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/database/Vote.kt @@ -16,8 +16,8 @@ class VoteForView( id: UUID = UUID.randomUUID(), override val createdBy: CitizenCreator, override val target: T, - var note: Int, - var anonymous: Boolean = true + val note: Int, + val anonymous: Boolean = true ) : ExtraI, VoteRef(id), CreatedAt by CreatedAt.Imp(), diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotes.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotes.kt index ae62fca..9524a29 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotes.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotes.kt @@ -1,5 +1,6 @@ package fr.dcproject.component.vote.routes +import fr.dcproject.common.response.toOutput import fr.dcproject.common.security.assert import fr.dcproject.common.utils.toUUID import fr.dcproject.component.auth.citizenOrNull @@ -7,6 +8,7 @@ import fr.dcproject.component.citizen.database.CitizenRef import fr.dcproject.component.vote.VoteAccessControl import fr.dcproject.component.vote.database.VoteRepository import io.ktor.application.call +import io.ktor.http.HttpStatusCode import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.Location import io.ktor.locations.get @@ -28,7 +30,10 @@ object GetCitizenVotes { if (votes.isNotEmpty()) { ac.assert { canView(votes, citizenOrNull) } } - call.respond(votes) + call.respond( + HttpStatusCode.OK, + votes.map { it.toOutput() } + ) } } } diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotesOnArticle.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotesOnArticle.kt index 302e1b5..5328f1f 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotesOnArticle.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/GetCitizenVotesOnArticle.kt @@ -1,5 +1,6 @@ package fr.dcproject.component.vote.routes +import fr.dcproject.common.response.toOutput import fr.dcproject.common.security.assert import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.citizen.database.CitizenRef @@ -8,6 +9,7 @@ import fr.dcproject.component.vote.database.VoteArticleRepository import fr.dcproject.routes.PaginatedRequest import fr.dcproject.routes.PaginatedRequestI import io.ktor.application.call +import io.ktor.http.HttpStatusCode import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.Location import io.ktor.locations.get @@ -32,7 +34,10 @@ object GetCitizenVotesOnArticle { val votes = repo.findByCitizen(it.citizen, it.page, it.limit) ac.assert { canView(votes.result, citizenOrNull) } - call.respond(votes) + call.respond( + HttpStatusCode.OK, + votes.toOutput { it.toOutput() } + ) } } } diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnArticle.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnArticle.kt index c490eba..85212ad 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnArticle.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnArticle.kt @@ -38,7 +38,10 @@ object PutVoteOnArticle { ) ac.assert { canCreate(vote, citizenOrNull) } val votes = repo.vote(vote) - call.respond(HttpStatusCode.Created, votes) + call.respond( + HttpStatusCode.Created, + votes.toOutput() + ) } } } diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnComment.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnComment.kt index 70e2240..6d65e4e 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnComment.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnComment.kt @@ -35,7 +35,10 @@ object PutVoteOnComment { ) ac.assert { canCreate(vote, citizenOrNull) } val votes = voteCommentRepo.vote(vote) - call.respond(HttpStatusCode.Created, votes) + call.respond( + HttpStatusCode.Created, + votes.toOutput() + ) } } } diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/VoteConstitution.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt similarity index 93% rename from src/main/kotlin/fr/dcproject/component/vote/routes/VoteConstitution.kt rename to src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt index 3b58bf8..c643e57 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/VoteConstitution.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/PutVoteOnConstitution.kt @@ -9,7 +9,7 @@ import fr.dcproject.component.constitution.database.ConstitutionRepository import fr.dcproject.component.vote.VoteAccessControl import fr.dcproject.component.vote.database.VoteConstitutionRepository import fr.dcproject.component.vote.database.VoteForUpdate -import fr.dcproject.component.vote.routes.VoteConstitution.ConstitutionVoteRequest.Input +import fr.dcproject.component.vote.routes.PutVoteOnConstitution.ConstitutionVoteRequest.Input import io.ktor.application.call import io.ktor.features.NotFoundException import io.ktor.http.HttpStatusCode @@ -21,7 +21,7 @@ import io.ktor.routing.Route import java.util.UUID @KtorExperimentalLocationsAPI -object VoteConstitution { +object PutVoteOnConstitution { @Location("/constitutions/{constitution}/vote") class ConstitutionVoteRequest(constitution: UUID) { val constitution = ConstitutionRef(constitution) diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/install.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/install.kt index bab8803..da3a009 100644 --- a/src/main/kotlin/fr/dcproject/component/vote/routes/install.kt +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/install.kt @@ -4,7 +4,7 @@ import fr.dcproject.component.vote.routes.GetCitizenVotes.getCitizenVote import fr.dcproject.component.vote.routes.GetCitizenVotesOnArticle.getCitizenVotesOnArticle import fr.dcproject.component.vote.routes.PutVoteOnArticle.putVoteOnArticle import fr.dcproject.component.vote.routes.PutVoteOnComment.putVoteOnComment -import fr.dcproject.component.vote.routes.VoteConstitution.voteConstitution +import fr.dcproject.component.vote.routes.PutVoteOnConstitution.voteConstitution import io.ktor.auth.authenticate import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.routing.Routing diff --git a/src/main/kotlin/fr/dcproject/component/vote/routes/response.kt b/src/main/kotlin/fr/dcproject/component/vote/routes/response.kt new file mode 100644 index 0000000..113aa70 --- /dev/null +++ b/src/main/kotlin/fr/dcproject/component/vote/routes/response.kt @@ -0,0 +1,31 @@ +package fr.dcproject.component.vote.routes + +import fr.dcproject.common.response.toOutput +import fr.dcproject.component.vote.database.VoteForView +import fr.dcproject.component.vote.entity.VoteAggregation +import org.joda.time.DateTime +import java.util.UUID + +fun VoteForView<*>.toOutput(): Any = this.let { v -> + object { + val id: UUID = v.id + val note: Int = v.note + val createdAt: DateTime = v.createdAt + val createdBy: Any = v.createdBy.toOutput() + val target: Any = object { + val id: UUID = v.target.id + val reference: String = v.target.reference + } + } +} + +fun VoteAggregation.toOutput(): Any = this.let { v -> + object { + val up: Int = v.up + val neutral: Int = v.neutral + val down: Int = v.down + val total: Int = v.total + val score: Int = v.score + val updatedAt: DateTime = v.updatedAt + } +} diff --git a/src/main/resources/openapi2.yaml b/src/main/resources/openapi2.yaml index 93c3c6e..d8038c8 100644 --- a/src/main/resources/openapi2.yaml +++ b/src/main/resources/openapi2.yaml @@ -795,7 +795,6 @@ paths: type: array items: $ref: '#/components/schemas/FollowResponse' - /constitutions/{constitution}/follows: parameters: - $ref: '#/components/parameters/constitution' @@ -905,7 +904,6 @@ paths: application/json: schema: $ref: '#/components/schemas/OpinionChoice' - /citizens/{citizen}/opinions: parameters: - $ref: '#/components/parameters/citizen' @@ -961,7 +959,6 @@ paths: type: array items: $ref: '#/components/schemas/Opinion' - /articles/{article}/opinions: parameters: - $ref: '#/components/parameters/article' @@ -996,6 +993,136 @@ paths: 401: $ref: '#/components/responses/401' + /constitutions/{constitution}/vote: + parameters: + - $ref: '#/components/parameters/constitution' + put: + security: + - JWTAuth: [ ] + summary: Vote for one constitution + tags: + - vote + - constitution + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VoteRequest' + responses: + 201: + description: Return only http status 201 on success + /citizens/{citizen}/votes: + parameters: + - $ref: '#/components/parameters/citizen' + get: + security: + - JWTAuth: [ ] + summary: Get Citizen + tags: + - vote + - citizen + operationId: getCitizenVotes + parameters: + - name: id + in: query + required: true + example: + - 1329ab90-edae-cfed-f863-c8cb069fa327 + - cab54e50-ce85-bba0-da23-fc9f75feeaf5 + schema: + type: array + items: + type: string + format: uuid + responses: + 200: + description: The Votes objects + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/VoteResponse' + 404: + description: Citizen not found + 401: + $ref: '#/components/responses/401' + /comments/{comment}/vote: + parameters: + - $ref: '#/components/parameters/comment' + put: + security: + - JWTAuth: [] + summary: Vote for a comment + tags: + - vote + - comment + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VoteRequest' + responses: + 201: + description: Return votes aggregation + content: + application/json: + schema: + $ref: '#/components/schemas/VoteAggregation' + 401: + $ref: '#/components/responses/401' + /citizens/{citizen}/votes/articles: + parameters: + - $ref: '#/components/parameters/citizen' + get: + security: + - JWTAuth: [ ] + summary: all article vote for one citizen + tags: + - vote + - article + - citizen + responses: + 200: + description: Votes + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Paginated' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/VoteResponse' + 401: + $ref: '#/components/responses/401' + /articles/{article}/vote: + parameters: + - $ref: '#/components/parameters/article' + put: + security: + - JWTAuth: [ ] + summary: Vote for one article + tags: + - vote + - article + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VoteRequest' + responses: + 201: + description: Return only http status 201 on success + content: + application/json: + schema: + $ref: '#/components/schemas/VoteAggregation' + 401: + $ref: '#/components/responses/401' + components: parameters: page: @@ -1696,6 +1823,74 @@ components: type: string format: 'date-time' + VoteRequest: + type: object + additionalProperties: false + required: + - note + properties: + note: + type: integer + minimum: -1 + maximum: 1 + VoteResponse: + type: object + additionalProperties: false + required: + - id + - note + - createdAt + - createdBy + - target + properties: + id: + $ref: '#/components/schemas/UUID' + note: + type: integer + minimum: -1 + maximum: 1 + createdAt: + type: string + format: 'date-time' + createdBy: + $ref: '#/components/schemas/CitizenCreator' + target: + required: + - id + - reference + properties: + id: + $ref: '#/components/schemas/UUID' + reference: + type: string + VoteAggregation: + additionalProperties: false + type: object + required: + - up + - neutral + - down + - total + - score + - updatedAt + properties: + up: + type: number + minimum: 0 + neutral: + type: number + minimum: 0 + down: + type: number + minimum: 0 + total: + type: number + minimum: 0 + score: + type: number + updatedAt: + type: string + format: 'date-time' securitySchemes: JWTAuth: