Big refactoring and implement Function

This commit is contained in:
2019-06-13 15:26:52 +02:00
parent ed87175b3f
commit 2d494d6e3e
4 changed files with 170 additions and 41 deletions

View File

@@ -2,6 +2,7 @@ package fr.postgresjson
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
@@ -32,12 +33,20 @@ class Serializer(val mapper: ObjectMapper = jacksonObjectMapper()) {
return mapper.writeValueAsString(source) return mapper.writeValueAsString(source)
} }
fun <T, E : EntityI<T?>?> deserialize(json: String, valueTypeRef: TypeReference<E>): E {
return this.mapper.readValue(json, valueTypeRef)
}
inline fun <T, reified E : EntityI<T?>?> deserialize(json: String): E { inline fun <T, reified E : EntityI<T?>?> deserialize(json: String): E {
return this.mapper.readValue(json) return this.mapper.readValue(json)
} }
fun <E> deserializeList(json: String, valueTypeRef: TypeReference<E>): E {
return mapper.readValue(json, valueTypeRef)
}
inline fun <reified E> deserializeList(json: String): E { inline fun <reified E> deserializeList(json: String): E {
return mapper.readValue(json) return deserializeList(json, object: TypeReference<E>() {})
} }
fun <T, E : EntityI<T?>> deserialize(json: String, target: E): E { fun <T, E : EntityI<T?>> deserialize(json: String, target: E): E {

View File

@@ -1,12 +1,14 @@
package fr.postgresjson.connexion package fr.postgresjson.connexion
import com.fasterxml.jackson.core.type.TypeReference
import com.github.jasync.sql.db.pool.ConnectionPool import com.github.jasync.sql.db.pool.ConnectionPool
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder import com.github.jasync.sql.db.postgresql.PostgreSQLConnectionBuilder
import fr.postgresjson.Serializer import fr.postgresjson.Serializer
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.EntityI
import java.io.File import java.io.File
import kotlin.text.RegexOption.IGNORE_CASE
import kotlin.text.RegexOption.MULTILINE
class Connection( class Connection(
private val host: String = "localhost", private val host: String = "localhost",
@@ -14,11 +16,13 @@ class Connection(
private val database: String = "dc-project", private val database: String = "dc-project",
private val username: String = "dc-project", private val username: String = "dc-project",
private val password: String = "dc-project", private val password: String = "dc-project",
queriesDirectory: File? = null queriesDirectory: File? = null,
functionsDirectory: File? = null
) { ) {
private val queries = mutableMapOf<String, MutableMap<String, String>>() private val queries = mutableMapOf<String, Query>()
private val functions = mutableMapOf<String, Function>()
private lateinit var connection: ConnectionPool<PostgreSQLConnection> private lateinit var connection: ConnectionPool<PostgreSQLConnection>
val serializer = Serializer() private val serializer = Serializer()
init { init {
if (queriesDirectory === null) { if (queriesDirectory === null) {
@@ -29,6 +33,77 @@ class Connection(
} else { } else {
fetchQueries(queriesDirectory) fetchQueries(queriesDirectory)
} }
if (functionsDirectory === null) {
val resource = this::class.java.getResource("/sql/function")
if (resource !== null) {
fetchFunctions(File(resource.toURI()))
}
} else {
fetchFunctions(functionsDirectory)
}
}
class Query(private val sql: String, private val connection : Connection) {
override fun toString(): String {
return sql
}
fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
return connection.selectOne(this.toString(), typeReference, values)
}
inline fun <T, reified R : EntityI<T?>?> selectOne(values: List<Any?> = emptyList()): R? = selectOne(object: TypeReference<R>() {}, values)
fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
return connection.select(this.toString(), typeReference, values)
}
inline fun <T, reified R : List<EntityI<T?>?>> select(values: List<Any?> = emptyList()): R? = select(object: TypeReference<R>() {}, values)
}
class Function(val name: String, val parameters: List<Parameter>, private val connection : Connection) {
class Parameter(val name: String, val type: String, direction: Direction? = Direction.IN)
{
val direction: Direction
init {
if (direction === null) {
this.direction = Direction.IN
} else {
this.direction = direction
}
}
constructor(name: String, type: String, direction: String? = "IN") : this(
name = name,
type = type,
direction = direction?.let { Direction.valueOf(direction.toUpperCase())}
)
enum class Direction { IN, OUT, INOUT }
}
override fun toString(): String {
return name
}
fun <T, R : EntityI<T?>?> selectOne(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
val args = values.joinToString()
val sql = "SELECT * FROM $name ($args)"
return connection.selectOne(sql, typeReference, values)
}
inline fun <T, reified R: EntityI<T?>?> selectOne(values: List<Any?> = emptyList()): R? = selectOne(object: TypeReference<R>() {}, values)
fun <T, R : List<EntityI<T?>?>> select(typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
val args = values.joinToString()
val sql = "SELECT * FROM $name ($args)"
return connection.select(sql, typeReference, values)
}
inline fun <T, reified R: List<EntityI<T?>?>> select(values: List<Any?> = emptyList()): R? = select(object: TypeReference<R>() {}, values)
} }
fun connect(): ConnectionPool<PostgreSQLConnection> { fun connect(): ConnectionPool<PostgreSQLConnection> {
@@ -40,53 +115,93 @@ class Connection(
return connection return connection
} }
inline fun <T, reified R : EntityI<T?>?> selectOne(group: String, name: String, values: List<Any?> = emptyList()): R? { fun <T, R : EntityI<T?>?> selectOne(sql: String, typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R? {
val sql: String = getQuery(group, name)
return selectOne<T, R>(sql, values)
}
inline fun <T, reified R : EntityI<T?>?> selectOne(sql: String, values: List<Any?> = emptyList()): R? {
val future = connect().sendPreparedStatement(sql, values) val future = connect().sendPreparedStatement(sql, values)
val json = future.get().rows[0].getString(0) val json = future.get().rows[0].getString(0)
if (json === null) { return if (json === null) {
return null null
} else { } else {
val obj = serializer.deserialize<T, R>(json) serializer.deserialize<T, R>(json, typeReference)
return obj
} }
} }
inline fun <T, reified R : List<EntityI<T?>>> select(sql: String, values: List<Any?> = emptyList()): R { inline fun <T, reified R : EntityI<T?>?> selectOne(sql: String, values: List<Any?> = emptyList()): R? = selectOne(sql, object: TypeReference<R>() {}, values)
fun <T, R : List<EntityI<T?>?>> select(sql: String, typeReference: TypeReference<R>, values: List<Any?> = emptyList()): R {
val future = connect().sendPreparedStatement(sql, values) val future = connect().sendPreparedStatement(sql, values)
val json = future.get().rows[0].getString(0) val json = future.get().rows[0].getString(0)
if (json === null) { return if (json === null) {
return listOf<EntityI<T?>>() as R listOf<EntityI<T?>?>() as R
} else { } else {
val obj = serializer.deserializeList<R>(json) serializer.deserializeList(json, typeReference)
}
}
return obj inline fun <T, reified R : List<EntityI<T?>?>> select(sql: String, values: List<Any?> = emptyList()): R = select(sql, object : TypeReference<R>() {}, values)
}
}
private fun fetchQueries(queriesDirectory: File) { private fun fetchQueries(queriesDirectory: File) {
queriesDirectory.walk().filter { it.isDirectory }.forEach { directory -> queriesDirectory.walk().filter { it.isDirectory }.forEach { directory ->
val group = directory.name val path = directory.name
directory.walk().filter { it.isFile }.forEach { file -> directory.walk().filter { it.isFile }.forEach { file ->
val sql = file.readText() val sql = file.readText()
if (queries[group] === null) { val fullpath = "$path/${file.nameWithoutExtension}"
queries[group] = mutableMapOf() queries[fullpath] = Query(sql, this)
}
queries[group]!![file.nameWithoutExtension] = sql
} }
} }
} }
fun getQuery(group: String, name: String): String { private fun fetchFunctions(functionsDirectory: File) {
if (queries[group] === null || queries[group]!![name] === null) { functionsDirectory.walk().filter { it.isDirectory }.forEach { directory ->
throw Exception("No query defined for $group/$name") directory.walk().filter { it.isFile }.forEach { file ->
} val fileContent = file.readText()
return queries[group]!![name]!! getDefinitions(fileContent).forEach {
functions[it.name] = it
}
}
}
}
private fun getDefinitions(functionContent: String): List<Function>
{
val functionRegex = """create .*(procedure|function) *(?<name>[^(\s]+)\s*\((?<params>(\s*((IN|OUT|INOUT|VARIADIC)?\s+)?([^\s,)]+\s+)?([^\s,)]+)(\s+(?:default\s|=)\s*[^\s,)]+)?\s*(,|(?=\))))*)\) *(?<return>RETURNS *[^ ]+)?"""
.toRegex(setOf(IGNORE_CASE, MULTILINE))
val paramsRegex = """\s*(?<param>((?<direction>IN|OUT|INOUT|VARIADIC)?\s+)?(?<name>[^\s,)]+\s+)?(?<type>[^\s,)]+)(\s+(?:default\s|=)\s*[^\s,)]+)?)\s*(,|$)"""
.toRegex(setOf(IGNORE_CASE, MULTILINE))
return functionRegex.findAll(functionContent).map { queryMatch ->
val functionName = queryMatch.groups["name"]?.value?.trim()
val functionParameters = queryMatch.groups["params"]?.value?.trim()
val returns = queryMatch.groups["return"]?.value?.trim()
/* Create parameters definition */
val parameters = if (functionParameters !== null) {
val matchesParams = paramsRegex.findAll(functionParameters)
matchesParams.map { paramsMatch ->
Function.Parameter(
paramsMatch.groups["name"]!!.value.trim(),
paramsMatch.groups["type"]!!.value.trim(),
paramsMatch.groups["direction"]?.value?.trim())
}.toList()
} else {
listOf()
}
Function(functionName!!, parameters, this)
}.toList()
}
fun getFunction(name: String): Function {
if (functions[name] === null) {
throw Exception("No function defined for $name")
}
return functions[name]!!
}
fun getQuery(path: String): Query {
if (queries[path] === null) {
throw Exception("No query defined for $path")
}
return queries[path]!!
} }
} }

View File

@@ -1,17 +1,22 @@
package fr.postgresjson.repository package fr.postgresjson.repository
import com.github.jasync.sql.db.pool.ConnectionPool
import com.github.jasync.sql.db.postgresql.PostgreSQLConnection
import fr.postgresjson.Serializer import fr.postgresjson.Serializer
import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.EntityCollection import fr.postgresjson.entity.EntityCollection
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.EntityI
import kotlin.reflect.KClass
interface RepositoryI<T, E : EntityI<T>> interface RepositoryI<T, E : EntityI<T?>> {
val entityName: KClass<E>
}
abstract class Repository<T, E : EntityI<T>> : RepositoryI<T, E> { abstract class Repository<T, E : EntityI<T?>>(override val entityName: KClass<E>) : RepositoryI<T, E> {
abstract var connection: ConnectionPool<PostgreSQLConnection>
abstract var connection: Connection
abstract fun getClassName(): String
fun <T> findById(id: T): EntityI<T?>? { fun <T> findById(id: T): EntityI<T?>? {
val sql = this.connection.getQuery(entityName.toString())
return when (val e = EntityCollection().get(id)) { return when (val e = EntityCollection().get(id)) {
null -> { null -> {
// TODO create Request // TODO create Request

View File

@@ -12,7 +12,7 @@ class RequestTest {
@Test @Test
fun getRequestFromFile() { fun getRequestFromFile() {
val resources = File(this::class.java.getResource("/sql/query").toURI()) val resources = File(this::class.java.getResource("/sql/query").toURI())
val objTest: ObjTest? = Connection(queriesDirectory = resources).selectOne("Test", "test") val objTest: ObjTest? = Connection(queriesDirectory = resources).getQuery("Test/test").selectOne()
assertTrue(objTest!!.id == 2) assertTrue(objTest!!.id == 2)
assertTrue(objTest.name == "test") assertTrue(objTest.name == "test")
} }