feature #6: implement route for search articles

This commit is contained in:
2019-07-31 23:07:58 +02:00
parent 3bf7ee1d13
commit 4c13d18e8d
11 changed files with 97 additions and 38 deletions

View File

@@ -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")

View File

@@ -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"/>

View File

@@ -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')

View 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);

View File

@@ -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;

View File

@@ -15,4 +15,4 @@ class Article(
): ):
UuidEntity(id), UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(), EntityCreatedAt by EntityCreatedAtImp(),
CreatedBy<User> by EntityCreatedByImp() CreatedBy<Citizen> by EntityCreatedByImp()

View File

@@ -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?
) )
} }

View File

@@ -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?>

View File

@@ -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
}
} }

View File

@@ -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)

View File

@@ -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
} }