feature #23: create route for citizen
This commit is contained in:
@@ -7,8 +7,10 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategy
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import com.fasterxml.jackson.datatype.joda.JodaModule
|
||||
import fr.dcproject.entity.Article
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.entity.Constitution
|
||||
import fr.dcproject.routes.article
|
||||
import fr.dcproject.routes.citizen
|
||||
import fr.dcproject.routes.constitution
|
||||
import fr.dcproject.routes.followArticle
|
||||
import fr.postgresjson.migration.Migrations
|
||||
@@ -30,6 +32,7 @@ import org.koin.ktor.ext.get
|
||||
import org.slf4j.event.Level
|
||||
import java.util.*
|
||||
import fr.dcproject.repository.Article as RepositoryArticle
|
||||
import fr.dcproject.repository.Citizen as RepositoryCitizen
|
||||
import fr.dcproject.repository.Constitution as RepositoryConstitution
|
||||
|
||||
fun main(args: Array<String>): Unit = io.ktor.server.jetty.EngineMain.main(args)
|
||||
@@ -63,7 +66,7 @@ fun Application.module() {
|
||||
}
|
||||
}
|
||||
|
||||
// create generic convert for entityI
|
||||
// TODO: create generic convert for entityI
|
||||
convert<Article> {
|
||||
decode { values, _ ->
|
||||
val id = values.singleOrNull()?.let { UUID.fromString(it) }
|
||||
@@ -71,6 +74,7 @@ fun Application.module() {
|
||||
get<RepositoryArticle>().findById(id) ?: throw InternalError("Article $values not found")
|
||||
}
|
||||
}
|
||||
|
||||
convert<Constitution> {
|
||||
decode { values, _ ->
|
||||
val id = values.singleOrNull()?.let { UUID.fromString(it) }
|
||||
@@ -78,6 +82,14 @@ fun Application.module() {
|
||||
get<RepositoryConstitution>().findById(id) ?: throw InternalError("Constitution $values not found")
|
||||
}
|
||||
}
|
||||
|
||||
convert<Citizen> {
|
||||
decode { values, _ ->
|
||||
val id = values.singleOrNull()?.let { UUID.fromString(it) }
|
||||
?: throw InternalError("Cannot convert $values to UUID")
|
||||
get<RepositoryCitizen>().findById(id) ?: throw InternalError("Citizen $values not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
install(Locations) {
|
||||
@@ -106,6 +118,7 @@ fun Application.module() {
|
||||
|
||||
install(Routing) {
|
||||
article(get())
|
||||
citizen(get())
|
||||
constitution(get())
|
||||
followArticle(get())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package fr.dcproject
|
||||
|
||||
import fr.dcproject.repository.FollowArticleRepository
|
||||
import fr.dcproject.repository.FollowConstitutionRepository
|
||||
import fr.postgresjson.connexion.Connection
|
||||
import fr.postgresjson.connexion.Requester
|
||||
import fr.postgresjson.migration.Migrations
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.koin.dsl.module
|
||||
import fr.dcproject.repository.Article as ArticleRepository
|
||||
import fr.dcproject.repository.Citizen as CitizenRepository
|
||||
import fr.dcproject.repository.Constitution as ConstitutionRepository
|
||||
|
||||
val config = Config()
|
||||
@@ -25,6 +27,7 @@ val Module = module {
|
||||
|
||||
// TODO: create generic declaration
|
||||
single { ArticleRepository(get()) }
|
||||
single { CitizenRepository(get()) }
|
||||
single { ConstitutionRepository(get()) }
|
||||
single { FollowArticleRepository(get()) }
|
||||
|
||||
|
||||
41
src/main/kotlin/fr/dcproject/repository/Citizen.kt
Normal file
41
src/main/kotlin/fr/dcproject/repository/Citizen.kt
Normal file
@@ -0,0 +1,41 @@
|
||||
package fr.dcproject.repository
|
||||
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
import fr.postgresjson.repository.RepositoryI
|
||||
import fr.postgresjson.repository.RepositoryI.Direction
|
||||
import net.pearx.kasechange.toSnakeCase
|
||||
import java.util.*
|
||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||
|
||||
class Citizen(override var requester: Requester) : RepositoryI<CitizenEntity> {
|
||||
override val entityName = CitizenEntity::class
|
||||
|
||||
fun findById(id: UUID): CitizenEntity? {
|
||||
val function = requester.getFunction("find_citizen_by_id")
|
||||
return function.selectOne("id" to id)
|
||||
}
|
||||
|
||||
fun find(
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
sort: String? = null,
|
||||
direction: Direction? = null,
|
||||
search: String? = null
|
||||
): Paginated<CitizenEntity> {
|
||||
return requester
|
||||
.getFunction("find_citizens")
|
||||
.select(
|
||||
page, limit,
|
||||
"sort" to sort?.toSnakeCase(),
|
||||
"direction" to direction,
|
||||
"search" to search
|
||||
)
|
||||
}
|
||||
|
||||
fun upsert(citizen: CitizenEntity): CitizenEntity? {
|
||||
return requester
|
||||
.getFunction("upsert_citizen")
|
||||
.selectOne("resource" to citizen)
|
||||
}
|
||||
}
|
||||
21
src/main/kotlin/fr/dcproject/routes/Citizen.kt
Normal file
21
src/main/kotlin/fr/dcproject/routes/Citizen.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import Paths
|
||||
import io.ktor.application.call
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.routing.Route
|
||||
import fr.dcproject.repository.Citizen as CitizenRepository
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
fun Route.citizen(repo: CitizenRepository) {
|
||||
get<Paths.CitizensRequest> {
|
||||
val citizens = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
||||
call.respond(citizens)
|
||||
}
|
||||
|
||||
get<Paths.CitizenRequest> {
|
||||
call.respond(it.citizen)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import fr.dcproject.entity.Article
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.entity.Constitution
|
||||
import fr.dcproject.entity.Follow
|
||||
import fr.postgresjson.repository.RepositoryI.Direction
|
||||
@@ -22,4 +23,11 @@ object Paths {
|
||||
}
|
||||
@Location("/constitutions/{constitution}") class ConstitutionRequest(val constitution: Constitution)
|
||||
@Location("/constitutions") class PostConstitutionRequest
|
||||
|
||||
|
||||
@Location("/citizens") class CitizensRequest(page: Int = 1, limit: Int = 50, val sort: String? = null, val direction: Direction? = null, val search: String? = null) {
|
||||
val page: Int = if (page < 1) 1 else page
|
||||
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
@Location("/citizens/{citizen}") class CitizenRequest(val citizen: Citizen)
|
||||
}
|
||||
43
src/main/resources/sql/functions/citizen/find_citizens.sql
Normal file
43
src/main/resources/sql/functions/citizen/find_citizens.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
create or replace function find_citizens(
|
||||
search text default null,
|
||||
direction text default 'desc',
|
||||
sort text default 'created_at',
|
||||
"limit" int default 50,
|
||||
"offset" int default 0,
|
||||
out resource json,
|
||||
out total int
|
||||
) language plpgsql as
|
||||
$$
|
||||
begin
|
||||
select json_agg(t), (select count(id) from citizen)
|
||||
into resource, total
|
||||
from (
|
||||
select
|
||||
z.*
|
||||
from citizen as z
|
||||
where "search" is null or (
|
||||
(name->'first_name')::text ilike '%'||"search"||'%' or
|
||||
(name->'last_name')::text ilike '%'||"search"||'%'
|
||||
)
|
||||
order by
|
||||
case direction when 'asc' then
|
||||
case sort
|
||||
when 'name' then (z.name->'first_name')::text
|
||||
when 'created_at' then z.created_at::text
|
||||
else null
|
||||
end
|
||||
end,
|
||||
case direction when 'desc' then
|
||||
case sort
|
||||
when 'name' then (z.name->'first_name')::text
|
||||
when 'created_at' then z.created_at::text
|
||||
end
|
||||
end
|
||||
desc,
|
||||
z.created_at desc
|
||||
limit "limit" offset "offset"
|
||||
) as t;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- drop function if exists find_citizens(text, text, text, int, int);
|
||||
11
src/test/resources/feature/citizen.feature
Normal file
11
src/test/resources/feature/citizen.feature
Normal file
@@ -0,0 +1,11 @@
|
||||
Feature: citizens routes
|
||||
|
||||
Scenario: The route for get citizens must response a 200
|
||||
When I send a "GET" request to "/citizens"
|
||||
Then the response status code should be 200
|
||||
|
||||
Scenario: The route for get one citizen must response a 200 and return citizen
|
||||
When I send a "GET" request to "/citizens/6434f4f9-f570-f22a-c134-8668350651ff"
|
||||
Then the response status code should be 200
|
||||
And the response should contain object:
|
||||
| id | 6434f4f9-f570-f22a-c134-8668350651ff |
|
||||
Reference in New Issue
Block a user