feature #6: implement route for search articles
This commit is contained in:
@@ -23,20 +23,21 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
|
||||||
compile("io.ktor:ktor-server-netty:$ktor_version")
|
implementation("io.ktor:ktor-server-netty:$ktor_version")
|
||||||
compile("ch.qos.logback:logback-classic:$logback_version")
|
implementation("ch.qos.logback:logback-classic:$logback_version")
|
||||||
compile("io.ktor:ktor-server-core:$ktor_version")
|
implementation("io.ktor:ktor-server-core:$ktor_version")
|
||||||
compile("io.ktor:ktor-locations:$ktor_version")
|
implementation("io.ktor:ktor-locations:$ktor_version")
|
||||||
compile("io.ktor:ktor-auth:$ktor_version")
|
implementation("io.ktor:ktor-auth:$ktor_version")
|
||||||
compile("io.ktor:ktor-auth-jwt:$ktor_version")
|
implementation("io.ktor:ktor-auth-jwt:$ktor_version")
|
||||||
compile("io.ktor:ktor-gson:$ktor_version")
|
implementation("io.ktor:ktor-gson:$ktor_version")
|
||||||
compile("org.koin:koin-ktor:$koinVersion")
|
implementation("org.koin:koin-ktor:$koinVersion")
|
||||||
compile("io.ktor:ktor-jackson:$ktor_version")
|
implementation("io.ktor:ktor-jackson:$ktor_version")
|
||||||
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9")
|
||||||
compile("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.9")
|
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.9")
|
||||||
compile("fr.postgresjson:postgresjson:$postgresjson_version")
|
implementation("net.pearx.kasechange:kasechange-jvm:1.1.0")
|
||||||
testCompile("io.ktor:ktor-server-tests:$ktor_version")
|
implementation("fr.postgresjson:postgresjson:$postgresjson_version")
|
||||||
|
testImplementation("io.ktor:ktor-server-tests:$ktor_version")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin.sourceSets["main"].kotlin.srcDirs("src")
|
kotlin.sourceSets["main"].kotlin.srcDirs("src")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<root level="trace">
|
<root level="debug">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<logger name="org.eclipse.jetty" level="INFO"/>
|
<logger name="org.eclipse.jetty" level="INFO"/>
|
||||||
|
|||||||
@@ -16,5 +16,3 @@ end;
|
|||||||
$$;
|
$$;
|
||||||
|
|
||||||
-- drop function if exists find_article_by_id(uuid, out json);
|
-- drop function if exists find_article_by_id(uuid, out json);
|
||||||
|
|
||||||
select find_article_by_id('12fed2f3-2a7d-434b-87e6-5a9895f9d12d')
|
|
||||||
41
resources/sql/functions/article/find_articles.sql
Normal file
41
resources/sql/functions/article/find_articles.sql
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
create or replace function find_articles(
|
||||||
|
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 article)
|
||||||
|
into resource, total
|
||||||
|
from (
|
||||||
|
select
|
||||||
|
a.*,
|
||||||
|
find_citizen_by_id(a.created_by_id) as created_by
|
||||||
|
from article as a
|
||||||
|
where title ilike '%'||"search"||'%'
|
||||||
|
order by
|
||||||
|
case direction when 'asc' then
|
||||||
|
case sort
|
||||||
|
when 'title' then a.title
|
||||||
|
when 'created_at' then a.created_at::text
|
||||||
|
else null
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
case direction when 'desc' then
|
||||||
|
case sort
|
||||||
|
when 'title' then a.title
|
||||||
|
when 'created_at' then a.created_at::text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
desc,
|
||||||
|
a.created_at desc
|
||||||
|
limit "limit" offset "offset"
|
||||||
|
) as t;
|
||||||
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- drop function if exists find_articles(json, int, int);
|
||||||
@@ -6,8 +6,7 @@ begin
|
|||||||
select to_json(t) into resource
|
select to_json(t) into resource
|
||||||
from (
|
from (
|
||||||
select
|
select
|
||||||
z.*,
|
z.*
|
||||||
find_user_by_id(z.user_id) as "user"
|
|
||||||
from citizen as z
|
from citizen as z
|
||||||
where z.id = _id
|
where z.id = _id
|
||||||
) as t;
|
) as t;
|
||||||
|
|||||||
@@ -15,4 +15,4 @@ class Article(
|
|||||||
):
|
):
|
||||||
UuidEntity(id),
|
UuidEntity(id),
|
||||||
EntityCreatedAt by EntityCreatedAtImp(),
|
EntityCreatedAt by EntityCreatedAtImp(),
|
||||||
CreatedBy<User> by EntityCreatedByImp()
|
CreatedBy<Citizen> by EntityCreatedByImp()
|
||||||
@@ -7,18 +7,18 @@ import java.util.*
|
|||||||
|
|
||||||
|
|
||||||
class Citizen(
|
class Citizen(
|
||||||
id: UUID,
|
id: UUID?,
|
||||||
var name: Name,
|
var name: Name?,
|
||||||
var birthday: String,
|
var birthday: String?,
|
||||||
var userId: String,
|
var userId: String?,
|
||||||
var voteAnnonymous: Boolean,
|
var voteAnnonymous: Boolean?,
|
||||||
var followAnnonymous: Boolean,
|
var followAnnonymous: Boolean?,
|
||||||
var user: User
|
var user: User?
|
||||||
) : UuidEntity(id),
|
) : UuidEntity(id),
|
||||||
EntityCreatedAt by EntityCreatedAtImp() {
|
EntityCreatedAt by EntityCreatedAtImp() {
|
||||||
data class Name(
|
data class Name(
|
||||||
var civility: String,
|
var civility: String?,
|
||||||
var lastName: String,
|
var lastName: String?,
|
||||||
var firstName: String
|
var firstName: String?
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package fr.dcproject.entity
|
package fr.dcproject.entity
|
||||||
|
|
||||||
import fr.postgresjson.entity.*
|
import fr.postgresjson.entity.*
|
||||||
import fr.postgresjson.entity.User
|
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -13,5 +12,4 @@ class User(
|
|||||||
override var updatedAt: DateTime?
|
override var updatedAt: DateTime?
|
||||||
) : UuidEntity(id),
|
) : UuidEntity(id),
|
||||||
EntityCreatedAt by EntityCreatedAtImp(),
|
EntityCreatedAt by EntityCreatedAtImp(),
|
||||||
EntityUpdatedAt by EntityUpdatedAtImp(),
|
EntityUpdatedAt by EntityUpdatedAtImp()
|
||||||
User<UUID?>
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package fr.dcproject.repository
|
package fr.dcproject.repository
|
||||||
|
|
||||||
|
import fr.postgresjson.connexion.Paginated
|
||||||
import fr.postgresjson.connexion.Requester
|
import fr.postgresjson.connexion.Requester
|
||||||
import fr.postgresjson.entity.EntitiesCollections
|
import fr.postgresjson.entity.EntitiesCollections
|
||||||
import fr.postgresjson.repository.RepositoryI
|
import fr.postgresjson.repository.RepositoryI
|
||||||
|
import net.pearx.kasechange.toSnakeCase
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import fr.dcproject.entity.Article as ArticleEntity
|
import fr.dcproject.entity.Article as ArticleEntity
|
||||||
|
|
||||||
@@ -19,6 +21,16 @@ class Article(override var requester: Requester) : RepositoryI<ArticleEntity> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun find(page: Int = 1, limit: Int = 50, sort: String? = null, direction: Direction? = null, search: String? = null): Paginated<ArticleEntity> {
|
||||||
|
return requester
|
||||||
|
.getFunction("find_articles")
|
||||||
|
.select(page, limit,
|
||||||
|
"sort" to sort?.toSnakeCase(),
|
||||||
|
"direction" to direction,
|
||||||
|
"search" to search
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun upsert(article: ArticleEntity): ArticleEntity? {
|
fun upsert(article: ArticleEntity): ArticleEntity? {
|
||||||
return requester
|
return requester
|
||||||
.getFunction("upsert_article")
|
.getFunction("upsert_article")
|
||||||
@@ -26,4 +38,9 @@ class Article(override var requester: Requester) : RepositoryI<ArticleEntity> {
|
|||||||
EntitiesCollections().set(it)
|
EntitiesCollections().set(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Direction {
|
||||||
|
asc,
|
||||||
|
desc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package fr.dcproject.routes
|
package fr.dcproject.routes
|
||||||
|
|
||||||
import Paths
|
import Paths
|
||||||
import fr.postgresjson.serializer.serialize
|
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.get
|
import io.ktor.locations.get
|
||||||
import io.ktor.locations.post
|
import io.ktor.locations.post
|
||||||
import io.ktor.request.receive
|
import io.ktor.request.receive
|
||||||
import io.ktor.response.respond
|
import io.ktor.response.respond
|
||||||
import io.ktor.response.respondText
|
|
||||||
import io.ktor.routing.Route
|
import io.ktor.routing.Route
|
||||||
import fr.dcproject.entity.Article as ArticleEntity
|
import fr.dcproject.entity.Article as ArticleEntity
|
||||||
import fr.dcproject.repository.Article as ArticleRepository
|
import fr.dcproject.repository.Article as ArticleRepository
|
||||||
@@ -16,11 +14,14 @@ import fr.dcproject.repository.Article as ArticleRepository
|
|||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
fun Route.article(repo: ArticleRepository) {
|
fun Route.article(repo: ArticleRepository) {
|
||||||
get<Paths.ArticlesRequest> {
|
get<Paths.ArticlesRequest> {
|
||||||
call.respondText("todo")
|
val articles = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
|
||||||
|
call.respond(articles)
|
||||||
}
|
}
|
||||||
|
|
||||||
get<Paths.ArticleRequest> {
|
get<Paths.ArticleRequest> {
|
||||||
call.respondText(it.article.serialize())
|
call.respond(it.article)
|
||||||
}
|
}
|
||||||
|
|
||||||
post<Paths.PostArticleRequest>() {
|
post<Paths.PostArticleRequest>() {
|
||||||
val article = call.receive<ArticleEntity>()
|
val article = call.receive<ArticleEntity>()
|
||||||
repo.upsert(article)
|
repo.upsert(article)
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import fr.dcproject.entity.Article
|
import fr.dcproject.entity.Article
|
||||||
|
import fr.dcproject.repository.Article.Direction
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
import io.ktor.locations.Location
|
||||||
|
|
||||||
@KtorExperimentalLocationsAPI
|
@KtorExperimentalLocationsAPI
|
||||||
object Paths {
|
object Paths {
|
||||||
@Location("/articles") class ArticlesRequest
|
@Location("/articles") class ArticlesRequest(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("/articles/{article}") class ArticleRequest(val article: Article)
|
@Location("/articles/{article}") class ArticleRequest(val article: Article)
|
||||||
@Location("/articles") class PostArticleRequest
|
@Location("/articles") class PostArticleRequest
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user