From 4c13d18e8de8806e5f20f79243620d60f4d0f240 Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Wed, 31 Jul 2019 23:07:58 +0200 Subject: [PATCH] feature #6: implement route for search articles --- build.gradle.kts | 29 ++++++------- resources/logback.xml | 2 +- .../functions/article/find_article_by_id.sql | 2 - .../sql/functions/article/find_articles.sql | 41 +++++++++++++++++++ .../functions/citizen/find_citizen_by_id.sql | 3 +- src/fr/dcproject/entity/Article.kt | 2 +- src/fr/dcproject/entity/Citizen.kt | 20 ++++----- src/fr/dcproject/entity/User.kt | 4 +- src/fr/dcproject/repository/Article.kt | 17 ++++++++ src/fr/dcproject/routes/Article.kt | 9 ++-- src/fr/dcproject/routes/Paths.kt | 6 ++- 11 files changed, 97 insertions(+), 38 deletions(-) create mode 100644 resources/sql/functions/article/find_articles.sql diff --git a/build.gradle.kts b/build.gradle.kts index e2d3161..c6bde77 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,20 +23,21 @@ repositories { } dependencies { - compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") - compile("io.ktor:ktor-server-netty:$ktor_version") - compile("ch.qos.logback:logback-classic:$logback_version") - compile("io.ktor:ktor-server-core:$ktor_version") - compile("io.ktor:ktor-locations:$ktor_version") - compile("io.ktor:ktor-auth:$ktor_version") - compile("io.ktor:ktor-auth-jwt:$ktor_version") - compile("io.ktor:ktor-gson:$ktor_version") - compile("org.koin:koin-ktor:$koinVersion") - compile("io.ktor:ktor-jackson:$ktor_version") - compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9") - compile("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.9") - compile("fr.postgresjson:postgresjson:$postgresjson_version") - testCompile("io.ktor:ktor-server-tests:$ktor_version") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") + implementation("io.ktor:ktor-server-netty:$ktor_version") + implementation("ch.qos.logback:logback-classic:$logback_version") + implementation("io.ktor:ktor-server-core:$ktor_version") + implementation("io.ktor:ktor-locations:$ktor_version") + implementation("io.ktor:ktor-auth:$ktor_version") + implementation("io.ktor:ktor-auth-jwt:$ktor_version") + implementation("io.ktor:ktor-gson:$ktor_version") + implementation("org.koin:koin-ktor:$koinVersion") + implementation("io.ktor:ktor-jackson:$ktor_version") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.9") + implementation("net.pearx.kasechange:kasechange-jvm:1.1.0") + implementation("fr.postgresjson:postgresjson:$postgresjson_version") + testImplementation("io.ktor:ktor-server-tests:$ktor_version") } kotlin.sourceSets["main"].kotlin.srcDirs("src") diff --git a/resources/logback.xml b/resources/logback.xml index bdbb64e..2f06544 100644 --- a/resources/logback.xml +++ b/resources/logback.xml @@ -4,7 +4,7 @@ %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + diff --git a/resources/sql/functions/article/find_article_by_id.sql b/resources/sql/functions/article/find_article_by_id.sql index 40cd693..b462a38 100644 --- a/resources/sql/functions/article/find_article_by_id.sql +++ b/resources/sql/functions/article/find_article_by_id.sql @@ -16,5 +16,3 @@ end; $$; -- drop function if exists find_article_by_id(uuid, out json); - -select find_article_by_id('12fed2f3-2a7d-434b-87e6-5a9895f9d12d') \ No newline at end of file diff --git a/resources/sql/functions/article/find_articles.sql b/resources/sql/functions/article/find_articles.sql new file mode 100644 index 0000000..143ac7a --- /dev/null +++ b/resources/sql/functions/article/find_articles.sql @@ -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); diff --git a/resources/sql/functions/citizen/find_citizen_by_id.sql b/resources/sql/functions/citizen/find_citizen_by_id.sql index c2f5b57..07a2672 100644 --- a/resources/sql/functions/citizen/find_citizen_by_id.sql +++ b/resources/sql/functions/citizen/find_citizen_by_id.sql @@ -6,8 +6,7 @@ begin select to_json(t) into resource from ( select - z.*, - find_user_by_id(z.user_id) as "user" + z.* from citizen as z where z.id = _id ) as t; diff --git a/src/fr/dcproject/entity/Article.kt b/src/fr/dcproject/entity/Article.kt index 24c0e9a..65374e7 100644 --- a/src/fr/dcproject/entity/Article.kt +++ b/src/fr/dcproject/entity/Article.kt @@ -15,4 +15,4 @@ class Article( ): UuidEntity(id), EntityCreatedAt by EntityCreatedAtImp(), - CreatedBy by EntityCreatedByImp() \ No newline at end of file + CreatedBy by EntityCreatedByImp() \ No newline at end of file diff --git a/src/fr/dcproject/entity/Citizen.kt b/src/fr/dcproject/entity/Citizen.kt index bb72769..28bda14 100644 --- a/src/fr/dcproject/entity/Citizen.kt +++ b/src/fr/dcproject/entity/Citizen.kt @@ -7,18 +7,18 @@ import java.util.* class Citizen( - id: UUID, - var name: Name, - var birthday: String, - var userId: String, - var voteAnnonymous: Boolean, - var followAnnonymous: Boolean, - var user: User + id: UUID?, + var name: Name?, + var birthday: String?, + var userId: String?, + var voteAnnonymous: Boolean?, + var followAnnonymous: Boolean?, + var user: User? ) : UuidEntity(id), EntityCreatedAt by EntityCreatedAtImp() { data class Name( - var civility: String, - var lastName: String, - var firstName: String + var civility: String?, + var lastName: String?, + var firstName: String? ) } \ No newline at end of file diff --git a/src/fr/dcproject/entity/User.kt b/src/fr/dcproject/entity/User.kt index 1258c74..8e88d2c 100644 --- a/src/fr/dcproject/entity/User.kt +++ b/src/fr/dcproject/entity/User.kt @@ -1,7 +1,6 @@ package fr.dcproject.entity import fr.postgresjson.entity.* -import fr.postgresjson.entity.User import org.joda.time.DateTime import java.util.* @@ -13,5 +12,4 @@ class User( override var updatedAt: DateTime? ) : UuidEntity(id), EntityCreatedAt by EntityCreatedAtImp(), - EntityUpdatedAt by EntityUpdatedAtImp(), - User + EntityUpdatedAt by EntityUpdatedAtImp() diff --git a/src/fr/dcproject/repository/Article.kt b/src/fr/dcproject/repository/Article.kt index d895d67..aa2188c 100644 --- a/src/fr/dcproject/repository/Article.kt +++ b/src/fr/dcproject/repository/Article.kt @@ -1,8 +1,10 @@ package fr.dcproject.repository +import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Requester import fr.postgresjson.entity.EntitiesCollections import fr.postgresjson.repository.RepositoryI +import net.pearx.kasechange.toSnakeCase import java.util.* import fr.dcproject.entity.Article as ArticleEntity @@ -19,6 +21,16 @@ class Article(override var requester: Requester) : RepositoryI { } } + fun find(page: Int = 1, limit: Int = 50, sort: String? = null, direction: Direction? = null, search: String? = null): Paginated { + return requester + .getFunction("find_articles") + .select(page, limit, + "sort" to sort?.toSnakeCase(), + "direction" to direction, + "search" to search + ) + } + fun upsert(article: ArticleEntity): ArticleEntity? { return requester .getFunction("upsert_article") @@ -26,4 +38,9 @@ class Article(override var requester: Requester) : RepositoryI { EntitiesCollections().set(it) } } + + enum class Direction { + asc, + desc + } } diff --git a/src/fr/dcproject/routes/Article.kt b/src/fr/dcproject/routes/Article.kt index 3143d86..36cbc04 100644 --- a/src/fr/dcproject/routes/Article.kt +++ b/src/fr/dcproject/routes/Article.kt @@ -1,14 +1,12 @@ package fr.dcproject.routes import Paths -import fr.postgresjson.serializer.serialize import io.ktor.application.call import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.get import io.ktor.locations.post import io.ktor.request.receive import io.ktor.response.respond -import io.ktor.response.respondText import io.ktor.routing.Route import fr.dcproject.entity.Article as ArticleEntity import fr.dcproject.repository.Article as ArticleRepository @@ -16,11 +14,14 @@ import fr.dcproject.repository.Article as ArticleRepository @KtorExperimentalLocationsAPI fun Route.article(repo: ArticleRepository) { get { - call.respondText("todo") + val articles = repo.find(it.page, it.limit, it.sort, it.direction, it.search) + call.respond(articles) } + get { - call.respondText(it.article.serialize()) + call.respond(it.article) } + post() { val article = call.receive() repo.upsert(article) diff --git a/src/fr/dcproject/routes/Paths.kt b/src/fr/dcproject/routes/Paths.kt index 74f7131..085b779 100644 --- a/src/fr/dcproject/routes/Paths.kt +++ b/src/fr/dcproject/routes/Paths.kt @@ -1,10 +1,14 @@ import fr.dcproject.entity.Article +import fr.dcproject.repository.Article.Direction import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.Location @KtorExperimentalLocationsAPI 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") class PostArticleRequest } \ No newline at end of file