fixs and move files

This commit is contained in:
2019-08-03 00:57:00 +02:00
parent 63a50dcb92
commit 7c3028eca2
60 changed files with 171 additions and 105 deletions

View File

@@ -0,0 +1,88 @@
package fr.dcproject
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
import com.fasterxml.jackson.databind.DeserializationFeature
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.routes.article
import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.auth.Authentication
import io.ktor.features.AutoHeadResponse
import io.ktor.features.ContentNegotiation
import io.ktor.features.DataConversion
import io.ktor.jackson.jackson
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Locations
import io.ktor.routing.Routing
import io.ktor.util.KtorExperimentalAPI
import org.koin.ktor.ext.Koin
import org.koin.ktor.ext.get
import java.util.*
import fr.dcproject.repository.Article as RepositoryArticle
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
@Suppress("unused") // Referenced in application.conf
fun Application.module() {
install(Koin) {
// Slf4jLog()
modules(Module)
}
install(DataConversion) {
convert<UUID> {
decode { values, _ ->
values.singleOrNull()?.let { UUID.fromString(it) }
}
encode { value ->
when (value) {
null -> listOf()
is UUID -> listOf(value.toString())
else -> throw InternalError("Cannot convert $value as UUID")
}
}
}
convert<Article> {
decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID")
get<RepositoryArticle>().findById(id) ?: throw InternalError("Article $values not found")
}
}
}
install(Locations) {
}
install(Authentication) {
}
install(AutoHeadResponse)
install(ContentNegotiation) {
// TODO move to postgresJson lib
jackson {
propertyNamingStrategy = PropertyNamingStrategy.SNAKE_CASE
registerModule(JodaModule())
disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
configure(SerializationFeature.INDENT_OUTPUT, true)
setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
indentObjectsWith(DefaultIndenter(" ", "\n"))
})
}
}
install(Routing) {
article(get())
}
}

View File

@@ -0,0 +1,16 @@
package fr.dcproject
import com.typesafe.config.ConfigFactory
import java.io.File
class Config {
private var config = ConfigFactory.load()
val sqlFiles = File(this::class.java.getResource("/sql").toURI())
val envName: String = config.getString("app.envName")
val host: String = config.getString("db.host")
var database: String = config.getString("db.database")
var username: String = config.getString("db.username")
var password: String = config.getString("db.password")
val port: Int = config.getInt("db.port")
}

View File

@@ -0,0 +1,26 @@
package fr.dcproject
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
val config = Config()
@KtorExperimentalAPI
val Module = module {
single { config }
single { Connection(host = config.host, port = config.port, database = config.database, username = config.username, password = config.password) }
single { Requester.RequesterFactory(
connection = get(),
functionsDirectory = config.sqlFiles.resolve("functions")
).createRequester() }
single { ArticleRepository(get()) }
single { Migrations(connection = get(), directory = config.sqlFiles) }
}

View File

@@ -0,0 +1,18 @@
package fr.dcproject.entity
import fr.postgresjson.entity.*
import java.util.*
class Article(
id: UUID?,
var versionId: UUID?,
var versionNumber: Int?,
var title: String?,
var annonymous: Boolean?,
var content: String?,
var description: String?,
var tags: List<String>
):
UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(),
CreatedBy<Citizen> by EntityCreatedByImp()

View File

@@ -0,0 +1,25 @@
package fr.dcproject.entity
import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedAtImp
import fr.postgresjson.entity.UuidEntity
import org.joda.time.DateTime
import java.util.*
class Citizen(
id: UUID = UUID.randomUUID(),
var name: Name?,
var birthday: DateTime?,
var userId: String? = null,
var voteAnnonymous: Boolean? = null,
var followAnnonymous: Boolean? = null,
var user: User?
) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp() {
data class Name(
var firstName: String?,
var lastName: String?,
var civility: String? = null
)
}

View File

@@ -0,0 +1,14 @@
package fr.dcproject.entity
import fr.postgresjson.entity.*
import org.joda.time.DateTime
import java.util.*
class User(
id: UUID? = UUID.randomUUID(),
var username: String?,
var blockedAt: DateTime? = null,
var plainPassword: String?
) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityUpdatedAt by EntityUpdatedAtImp()

View File

@@ -0,0 +1,53 @@
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
class Article(override var requester: Requester) : RepositoryI<ArticleEntity> {
override val entityName = ArticleEntity::class
fun findById(id: UUID): ArticleEntity? {
val function = requester.getFunction("find_article_by_id")
return when (val e = EntitiesCollections().get(id) as ArticleEntity?) {
null -> {
function.selectOne("id" to id)
}
else -> e
}
}
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? {
return requester
.getFunction("upsert_article")
.selectOne<ArticleEntity>("resource" to article)?.also {
EntitiesCollections().set(it)
}
}
enum class Direction {
asc,
desc
}
}

View File

@@ -0,0 +1,30 @@
package fr.dcproject.routes
import Paths
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.routing.Route
import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.repository.Article as ArticleRepository
@KtorExperimentalLocationsAPI
fun Route.article(repo: ArticleRepository) {
get<Paths.ArticlesRequest> {
val articles = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
call.respond(articles)
}
get<Paths.ArticleRequest> {
call.respond(it.article)
}
post<Paths.PostArticleRequest>() {
val article = call.receive<ArticleEntity>()
repo.upsert(article)
call.respond(article)
}
}

View File

@@ -0,0 +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(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
}