7 Commits
1.2.1 ... 2.1.2

Author SHA1 Message Date
4b28f64c43 Sort sql files on walk 2021-03-20 14:33:18 +01:00
35a8712eef fix select on query with no result 2021-02-27 01:29:21 +01:00
b200b3579a Improve gradle tasks 2021-02-27 00:19:30 +01:00
a13ca2d954 update dependencies 2021-02-26 23:00:41 +01:00
36ed678c5a Add disconnect method 2021-02-26 23:00:29 +01:00
c2c8b91dc5 Remove Mutable entities 2020-12-15 14:21:07 +01:00
9d208292a5 fix immutable entities 2020-11-26 09:29:13 +01:00
41 changed files with 247 additions and 323 deletions

View File

@@ -2,16 +2,30 @@
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS"> <option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value> <value>
<package name="java.util" withSubpackages="true" static="false" /> <package name="" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value> </value>
</option> </option>
<option name="SPACE_BEFORE_EXTEND_COLON" value="false" /> <option name="SPACE_BEFORE_EXTEND_COLON" value="false" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="IMPORT_NESTED_CLASSES" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="kotlin"> <codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CALL_PARAMETERS_WRAP" value="5" />
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_WRAP" value="5" />
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" /> <option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions> </indentOptions>

View File

@@ -1,5 +1,6 @@
<component name="ProjectRunConfigurationManager"> <component name="ProjectRunConfigurationManager">
<configuration default="false" name="Check" type="CompoundRunConfigurationType"> <configuration default="false" name="Check" type="CompoundRunConfigurationType">
<toRun name="run DB" type="docker-deploy" />
<toRun name="Lint" type="GradleRunConfiguration" /> <toRun name="Lint" type="GradleRunConfiguration" />
<toRun name="tests" type="JUnit" /> <toRun name="tests" type="JUnit" />
<method v="2" /> <method v="2" />

View File

@@ -1,34 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Test and Publish To MavenLocal" type="GradleRunConfiguration" factoryName="Gradle" singleton="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="publishToMavenLocal" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<extension name="net.ashald.envfile">
<option name="IS_ENABLED" value="false" />
<option name="IS_SUBST" value="false" />
<option name="IS_PATH_MACRO_SUPPORTED" value="false" />
<option name="IS_IGNORE_MISSING_FILES" value="false" />
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
<ENTRIES>
<ENTRY IS_ENABLED="true" PARSER="runconfig" />
</ENTRIES>
</extension>
<GradleScriptDebugEnabled>true</GradleScriptDebugEnabled>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Lint" run_configuration_type="GradleRunConfiguration" />
<option name="RunConfigurationTask" enabled="true" run_configuration_name="tests" run_configuration_type="JUnit" />
</method>
</configuration>
</component>

View File

@@ -4,11 +4,12 @@ plugins {
jacoco jacoco
id("maven-publish") id("maven-publish")
id("org.jetbrains.kotlin.jvm") version "1.3.50" kotlin("jvm") version "1.4.30"
id("org.jlleitschuh.gradle.ktlint") version "8.2.0" id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
id("org.owasp.dependencycheck") version "5.1.0" id("org.owasp.dependencycheck") version "6.1.1"
id("fr.coppernic.versioning") version "3.1.2" id("fr.coppernic.versioning") version "3.2.1"
id("com.avast.gradle.docker-compose") version "0.14.0"
} }
group = "com.github.flecomte" group = "com.github.flecomte"
@@ -27,19 +28,41 @@ tasks.withType<KotlinCompile> {
} }
} }
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
tasks.test {
useJUnit()
useJUnitPlatform()
systemProperty("junit.jupiter.execution.parallel.enabled", true)
finalizedBy(tasks.ktlintCheck)
}
tasks.publishToMavenLocal {
dependsOn(tasks.test)
}
dependencies { dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.3.31") implementation("org.jetbrains.kotlin:kotlin-reflect:1.4.31")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.9") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.9.9") implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.12.1")
implementation("com.github.jasync-sql:jasync-postgresql:1.0.7") implementation("com.github.jasync-sql:jasync-postgresql:1.1.7")
implementation("org.slf4j:slf4j-api:1.7.26") implementation("org.slf4j:slf4j-api:1.7.30")
implementation("com.avast.gradle:gradle-docker-compose-plugin:0.14.0")
testImplementation("ch.qos.logback:logback-classic:1.2.3") testImplementation("ch.qos.logback:logback-classic:1.2.3")
testImplementation("ch.qos.logback:logback-core:1.2.3") testImplementation("ch.qos.logback:logback-core:1.2.3")
testImplementation("io.mockk:mockk:1.9") testImplementation("io.mockk:mockk:1.10.6")
testImplementation("org.junit.jupiter:junit-jupiter:5.4.2") testImplementation("org.junit.jupiter:junit-jupiter:5.7.1")
testImplementation("org.amshove.kluent:kluent:1.47") testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.4.30")
testImplementation("org.amshove.kluent:kluent:1.65")
} }
val sourcesJar by tasks.creating(Jar::class) { val sourcesJar by tasks.creating(Jar::class) {
@@ -47,6 +70,14 @@ val sourcesJar by tasks.creating(Jar::class) {
from(sourceSets.getByName("main").allSource) from(sourceSets.getByName("main").allSource)
} }
apply(plugin = "docker-compose")
dockerCompose {
projectName = "postgres-json"
useComposeFiles = listOf("docker-compose.yml")
stopContainers = true
isRequiredBy(project.tasks.test)
}
publishing { publishing {
repositories { repositories {
maven { maven {

View File

@@ -13,13 +13,3 @@ services:
POSTGRES_DB: json_test POSTGRES_DB: json_test
POSTGRES_USER: test POSTGRES_USER: test
POSTGRES_PASSWORD: test POSTGRES_PASSWORD: test
pgadmin:
container_name: pgadmin4_json
image: dpage/pgadmin4
restart: always
ports:
- 8585:80
environment:
PGADMIN_DEFAULT_EMAIL: rusk23@gmail.com
PGADMIN_DEFAULT_PASSWORD: azerty

View File

@@ -1,4 +1,4 @@
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -12,7 +12,7 @@ import fr.postgresjson.entity.Serializable
import fr.postgresjson.serializer.Serializer import fr.postgresjson.serializer.Serializer
import fr.postgresjson.utils.LoggerDelegate import fr.postgresjson.utils.LoggerDelegate
import org.slf4j.Logger import org.slf4j.Logger
import java.util.concurrent.* import java.util.concurrent.CompletableFuture
typealias SelectOneCallback<T> = QueryResult.(T?) -> Unit typealias SelectOneCallback<T> = QueryResult.(T?) -> Unit
typealias SelectCallback<T> = QueryResult.(List<T>) -> Unit typealias SelectCallback<T> = QueryResult.(List<T>) -> Unit
@@ -25,17 +25,26 @@ class Connection(
private val host: String = "localhost", private val host: String = "localhost",
private val port: Int = 5432 private val port: Int = 5432
) : Executable { ) : Executable {
private lateinit var connection: ConnectionPool<PostgreSQLConnection> private var connection: ConnectionPool<PostgreSQLConnection>? = null
private val serializer = Serializer() private val serializer = Serializer()
private val logger: Logger? by LoggerDelegate() private val logger: Logger? by LoggerDelegate()
internal fun connect(): ConnectionPool<PostgreSQLConnection> { internal fun connect(): ConnectionPool<PostgreSQLConnection> {
if (!::connection.isInitialized || !connection.isConnected()) { return connection.let { connectionPool ->
connection = PostgreSQLConnectionBuilder.createConnectionPool( if (connectionPool == null || !connectionPool.isConnected()) {
"jdbc:postgresql://$host:$port/$database?user=$username&password=$password" PostgreSQLConnectionBuilder.createConnectionPool(
) "jdbc:postgresql://$host:$port/$database?user=$username&password=$password"
).also {
connection = it
}
} else {
connectionPool
}
} }
return connection }
fun disconnect() {
connection?.run { disconnect() }
} }
fun <A> inTransaction(f: (Connection) -> CompletableFuture<A>) = connect().inTransaction(f) fun <A> inTransaction(f: (Connection) -> CompletableFuture<A>) = connect().inTransaction(f)
@@ -50,7 +59,7 @@ class Connection(
it is EntityI && typeReference.type.typeName == it::class.java.name it is EntityI && typeReference.type.typeName == it::class.java.name
} as R? } as R?
val result = exec(sql, compileArgs(values)) val result = exec(sql, compileArgs(values))
val json = result.rows[0].getString(0) val json = result.rows.firstOrNull()?.getString(0)
return if (json === null) { return if (json === null) {
null null
} else { } else {
@@ -271,11 +280,14 @@ class Connection(
logger?.debug("Query executed in $duration ms \n{}", args) logger?.debug("Query executed in $duration ms \n{}", args)
return result return result
} catch (e: Throwable) { } catch (e: Throwable) {
logger?.info(""" logger?.info(
"""
Query Error: Query Error:
${sql.prependIndent()}, ${sql.prependIndent()},
${values.joinToString(", ").prependIndent()} ${values.joinToString(", ").prependIndent()}
""".trimIndent(), e) """.trimIndent(),
e
)
throw e throw e
} }
} }

View File

@@ -3,7 +3,6 @@ package fr.postgresjson.connexion
import fr.postgresjson.utils.searchSqlFiles import fr.postgresjson.utils.searchSqlFiles
import java.net.URI import java.net.URI
import fr.postgresjson.definition.Function as DefinitionFunction import fr.postgresjson.definition.Function as DefinitionFunction
import fr.postgresjson.definition.Function as FunctionDefinition
import fr.postgresjson.definition.Query as QueryDefinition import fr.postgresjson.definition.Query as QueryDefinition
class Requester( class Requester(
@@ -52,7 +51,7 @@ class Requester(
fun addFunction(functionsDirectory: URI): Requester { fun addFunction(functionsDirectory: URI): Requester {
functionsDirectory.searchSqlFiles() functionsDirectory.searchSqlFiles()
.forEach { .forEach {
if (it is FunctionDefinition) { if (it is DefinitionFunction) {
addFunction(it) addFunction(it)
} }
} }

View File

@@ -14,8 +14,8 @@ class Query(
"""-- *name ?: ?(?<name>[^ \n]+)""" """-- *name ?: ?(?<name>[^ \n]+)"""
.toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE)) .toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE))
.find(script)?.let { .find(script)?.let {
it.groups["name"]?.value?.trim() it.groups["name"]?.value?.trim()
} }
/** Try to get name from the filename */ /** Try to get name from the filename */
private fun getNameFromFile(source: Path): String = source private fun getNameFromFile(source: Path): String = source

View File

@@ -1,12 +1,7 @@
package fr.postgresjson.entity.immutable package fr.postgresjson.entity
import fr.postgresjson.entity.EntityI
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import fr.postgresjson.entity.mutable.EntityDeletedBy
import fr.postgresjson.entity.mutable.EntityDeletedByImp
import org.joda.time.DateTime import org.joda.time.DateTime
import java.util.* import java.util.UUID
interface EntityRefI<T> : EntityI { interface EntityRefI<T> : EntityI {
val id: T val id: T
@@ -37,7 +32,16 @@ interface EntityCreatedAt {
val createdAt: DateTime val createdAt: DateTime
} }
interface EntityUpdatedAt { interface EntityUpdatedAt {
var updatedAt: DateTime val updatedAt: DateTime
}
interface EntityDeletedAt {
val deletedAt: DateTime?
fun isDeleted(): Boolean {
return deletedAt?.let {
it < DateTime.now()
} ?: false
}
} }
class EntityCreatedAtImp( class EntityCreatedAtImp(
@@ -45,15 +49,23 @@ class EntityCreatedAtImp(
) : EntityCreatedAt ) : EntityCreatedAt
class EntityUpdatedAtImp( class EntityUpdatedAtImp(
override var updatedAt: DateTime = DateTime.now() override val updatedAt: DateTime = DateTime.now()
) : EntityUpdatedAt ) : EntityUpdatedAt
class EntityDeletedAtImp(
override val deletedAt: DateTime? = null
) : EntityDeletedAt
/* Author */ /* Author */
interface EntityCreatedBy<T : EntityI> { interface EntityCreatedBy<T : EntityI> {
val createdBy: T val createdBy: T
} }
interface EntityUpdatedBy<T : EntityI> { interface EntityUpdatedBy<T : EntityI> {
var updatedBy: T val updatedBy: T
}
interface EntityDeletedBy<T : EntityI> {
val deletedBy: T?
} }
class EntityCreatedByImp<UserT : EntityI>( class EntityCreatedByImp<UserT : EntityI>(
@@ -61,9 +73,13 @@ class EntityCreatedByImp<UserT : EntityI>(
) : EntityCreatedBy<UserT> ) : EntityCreatedBy<UserT>
class EntityUpdatedByImp<UserT : EntityI>( class EntityUpdatedByImp<UserT : EntityI>(
override var updatedBy: UserT override val updatedBy: UserT
) : EntityUpdatedBy<UserT> ) : EntityUpdatedBy<UserT>
class EntityDeletedByImp<UserT : EntityI>(
override val deletedBy: UserT?
) : EntityDeletedBy<UserT>
/* Mixed */ /* Mixed */
class EntityCreatedImp<UserT : EntityI>( class EntityCreatedImp<UserT : EntityI>(
override val createdAt: DateTime = DateTime.now(), override val createdAt: DateTime = DateTime.now(),
@@ -73,10 +89,22 @@ class EntityCreatedImp<UserT : EntityI>(
class EntityUpdatedImp<UserT : EntityI>( class EntityUpdatedImp<UserT : EntityI>(
updatedAt: DateTime = DateTime.now(), updatedAt: DateTime = DateTime.now(),
override var updatedBy: UserT override val updatedBy: UserT
) : EntityUpdatedBy<UserT>, ) : EntityUpdatedBy<UserT>,
EntityUpdatedAt by EntityUpdatedAtImp(updatedAt) EntityUpdatedAt by EntityUpdatedAtImp(updatedAt)
/* Published */
interface Published<UserT : EntityI> {
val publishedAt: DateTime?
val publishedBy: UserT?
}
class EntityPublishedImp<UserT : EntityI>(
override val publishedBy: UserT?
) : Published<UserT> {
override val publishedAt: DateTime? = null
}
/* Implementation */ /* Implementation */
abstract class EntityImp<T, UserT : EntityI>( abstract class EntityImp<T, UserT : EntityI>(
updatedBy: UserT, updatedBy: UserT,
@@ -88,3 +116,11 @@ abstract class EntityImp<T, UserT : EntityI>(
EntityCreatedBy<UserT> by EntityCreatedByImp(updatedBy), EntityCreatedBy<UserT> by EntityCreatedByImp(updatedBy),
EntityUpdatedBy<UserT> by EntityUpdatedByImp(updatedBy), EntityUpdatedBy<UserT> by EntityUpdatedByImp(updatedBy),
EntityDeletedBy<UserT> by EntityDeletedByImp(updatedBy) EntityDeletedBy<UserT> by EntityDeletedByImp(updatedBy)
abstract class UuidEntityExtended<T, UserT : EntityI>(
updatedBy: UserT,
publishedBy: UserT?
) :
EntityImp<T, UserT>(updatedBy),
EntityVersioning<UUID, Int> by UuidEntityVersioning(0),
Published<UserT> by EntityPublishedImp(publishedBy)

View File

@@ -1,137 +0,0 @@
package fr.postgresjson.entity.mutable
import fr.postgresjson.entity.EntityI
import org.joda.time.DateTime
import java.util.*
interface EntityRefI<T> : EntityI {
var id: T?
}
interface UuidEntityI : EntityRefI<UUID> {
override var id: UUID?
}
interface IdEntityI : EntityRefI<Int> {
override var id: Int?
}
abstract class Entity<T>(override var id: T? = null) : EntityRefI<T>
open class UuidEntity(id: UUID? = null) : UuidEntityI, Entity<UUID>(id ?: UUID.randomUUID())
open class IdEntity(override var id: Int? = null) : IdEntityI, Entity<Int>(id)
/* Version */
interface EntityVersioning<ID, NUMBER> {
var versionId: ID
var versionNumber: NUMBER?
}
class UuidEntityVersioning(
override var versionNumber: Int? = null,
versionId: UUID? = null
) : EntityVersioning<UUID, Int> {
override var versionId: UUID = versionId ?: UUID.randomUUID()
}
/* Dates */
interface EntityCreatedAt {
var createdAt: DateTime?
}
interface EntityUpdatedAt {
var updatedAt: DateTime?
}
interface EntityDeletedAt {
var deletedAt: DateTime?
fun isDeleted(): Boolean {
val deletedAt = deletedAt
return deletedAt != null && deletedAt < DateTime.now()
}
}
class EntityCreatedAtImp : EntityCreatedAt {
override var createdAt: DateTime? = null
}
class EntityUpdatedAtImp : EntityUpdatedAt {
override var updatedAt: DateTime? = null
}
class EntityDeletedAtImp : EntityDeletedAt {
override var deletedAt: DateTime? = null
}
/* Author */
interface EntityCreatedBy<T : EntityI> {
var createdBy: T?
}
interface EntityUpdatedBy<T : EntityI> {
var updatedBy: T?
}
interface EntityDeletedBy<T : EntityI> {
var deletedBy: T?
}
class EntityCreatedByImp<UserT : EntityI>(
override var createdBy: UserT?
) : EntityCreatedBy<UserT>
class EntityUpdatedByImp<UserT : EntityI>(
override var updatedBy: UserT?
) : EntityUpdatedBy<UserT>
class EntityDeletedByImp<UserT : EntityI>(
override var deletedBy: UserT?
) : EntityDeletedBy<UserT>
/* Mixed */
class EntityDeletedImp<UserT : EntityI>(
override var deletedBy: UserT? = null
) : EntityDeletedBy<UserT>,
EntityDeletedAt by EntityDeletedAtImp()
class EntityUpdatedImp<UserT : EntityI>(
override var updatedAt: DateTime? = null,
override var updatedBy: UserT? = null
) : EntityUpdatedBy<UserT>,
EntityUpdatedAt by EntityUpdatedAtImp()
class EntityCreatedImp<UserT : EntityI>(
override var createdAt: DateTime? = null,
override var createdBy: UserT? = null
) : EntityCreatedBy<UserT>,
EntityCreatedAt by EntityCreatedAtImp()
/* Published */
interface Published<UserT : EntityI> {
var publishedAt: DateTime?
var publishedBy: UserT?
}
class EntityPublishedImp<UserT : EntityI>(
override var publishedBy: UserT?
) : Published<UserT> {
override var publishedAt: DateTime? = null
}
/* Implementation */
abstract class EntityImp<T, UserT : EntityI>(
updatedBy: UserT?
) : Entity<T>(),
EntityCreatedAt by EntityCreatedAtImp(),
EntityUpdatedAt by EntityUpdatedAtImp(),
EntityDeletedAt by EntityDeletedAtImp(),
EntityCreatedBy<UserT> by EntityCreatedByImp(updatedBy),
EntityUpdatedBy<UserT> by EntityUpdatedByImp(updatedBy),
EntityDeletedBy<UserT> by EntityDeletedByImp(updatedBy)
abstract class UuidEntityExtended<T, UserT : EntityI>(
updatedBy: UserT?,
publishedBy: UserT?
) :
EntityImp<T, UserT>(updatedBy),
EntityVersioning<UUID, Int> by UuidEntityVersioning(),
Published<UserT> by EntityPublishedImp(publishedBy)

View File

@@ -4,8 +4,8 @@ import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.migration.Migration.Action import fr.postgresjson.migration.Migration.Action
import fr.postgresjson.migration.Migration.Status import fr.postgresjson.migration.Migration.Status
import java.util.* import java.util.Date
import java.util.concurrent.* import java.util.concurrent.CompletionException
import fr.postgresjson.definition.Function as DefinitionFunction import fr.postgresjson.definition.Function as DefinitionFunction
data class Function( data class Function(

View File

@@ -1,9 +1,9 @@
package fr.postgresjson.migration package fr.postgresjson.migration
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.mutable.Entity import fr.postgresjson.entity.Entity
import fr.postgresjson.migration.Migration.Action import fr.postgresjson.migration.Migration.Action
import java.util.* import java.util.Date
data class MigrationScript( data class MigrationScript(
val name: String, val name: String,

View File

@@ -2,8 +2,7 @@ package fr.postgresjson.migration
import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.core.type.TypeReference
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.definition.Migration as DefinitionMigration import fr.postgresjson.entity.Entity
import fr.postgresjson.entity.mutable.Entity
import fr.postgresjson.migration.Migration.Action import fr.postgresjson.migration.Migration.Action
import fr.postgresjson.migration.Migration.Status import fr.postgresjson.migration.Migration.Status
import fr.postgresjson.utils.LoggerDelegate import fr.postgresjson.utils.LoggerDelegate
@@ -11,8 +10,9 @@ import fr.postgresjson.utils.searchSqlFiles
import org.slf4j.Logger import org.slf4j.Logger
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.net.URI import java.net.URI
import java.util.* import java.util.Date
import fr.postgresjson.definition.Function as DefinitionFunction import fr.postgresjson.definition.Function as DefinitionFunction
import fr.postgresjson.definition.Migration as DefinitionMigration
class MigrationEntity( class MigrationEntity(
val filename: String, val filename: String,

View File

@@ -47,5 +47,5 @@ fun URI.searchSqlFiles() = sequence<Resource> {
} }
} }
private fun Path.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(this, maxDepth, *options) private fun Path.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(this, maxDepth, *options).sorted()
private fun URI.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(Path.of(this), maxDepth, *options) private fun URI.walk(maxDepth: Int = 2147483647, vararg options: FileVisitOption) = Files.walk(Path.of(this), maxDepth, *options).sorted()

View File

@@ -1,70 +1,73 @@
package fr.postgresjson package fr.postgresjson
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.entity.mutable.IdEntity
import fr.postgresjson.entity.Parameter import fr.postgresjson.entity.Parameter
import org.junit.Assert.* import fr.postgresjson.entity.UuidEntity
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ConnectionTest() : TestAbstract() { class ConnectionTest() : TestAbstract() {
private class ObjTest(var name: String) : IdEntity() private class ObjTest(val name: String, id: UUID = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00")) : UuidEntity(id)
private class ObjTest2(var title: String, var test: ObjTest?) : IdEntity() private class ObjTest2(val title: String, var test: ObjTest?) : UuidEntity()
private class ObjTest3(var first: String, var seconde: String, var third: Int) : IdEntity() private class ObjTest3(val first: String, var seconde: String, var third: Int) : UuidEntity()
private class ObjTestWithParameterObject(var first: ParameterObject, var seconde: ParameterObject) : IdEntity() private class ObjTestWithParameterObject(var first: ParameterObject, var seconde: ParameterObject) : UuidEntity()
private class ParameterObject(var third: String) : Parameter private class ParameterObject(var third: String) : Parameter
@Test @Test
fun getObject() { fun getObject() {
val obj: ObjTest? = connection.selectOne("select to_json(a) from test a limit 1") val obj: ObjTest? = connection.selectOne("select to_json(a) from test a limit 1")
assertTrue(obj is ObjTest) assertTrue(obj is ObjTest)
assertTrue(obj!!.id == 1) assertTrue(obj!!.id == UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96"))
} }
@Test @Test
fun getExistingObject() { fun getExistingObject() {
val objs: List<ObjTest2> = connection.select(""" val objs: List<ObjTest2> = connection.select(
select """
json_agg(j) select
FROM ( json_agg(j)
SELECT FROM (
t.id, t.title, SELECT
t2 as test t.id, t.title,
from test2 t t2 as test
JOIN test t2 ON t.test_id = t2.id from test2 t
) j; JOIN test t2 ON t.test_id = t2.id
""".trimIndent() ) j;
""".trimIndent()
) )
assertNotNull(objs) assertNotNull(objs)
assertEquals(objs.size, 2) assertEquals(objs.size, 2)
assertEquals(objs[0].id, 1) assertEquals(objs[0].id, UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96"))
assertEquals(objs[0].test!!.id, 1) assertEquals(objs[0].test!!.id, UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96"))
} }
@Test @Test
fun callRequestWithArgs() { fun callRequestWithArgs() {
val result: ObjTest? = connection.selectOne("select json_build_object('id', 1, 'name', ?::text)", listOf("myName")) val result: ObjTest? = connection.selectOne("select json_build_object('id', '2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00', 'name', ?::text)", listOf("myName"))
assertNotNull(result) assertNotNull(result)
assertEquals("myName", result!!.name) assertEquals("myName", result!!.name)
} }
@Test @Test
fun callRequestWithArgsEntity() { fun callRequestWithArgsEntity() {
val o = ObjTest("myName") val o = ObjTest("myName", id = UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00"))
o.id = 88 val obj: ObjTest? = connection.selectOne("select json_build_object('id', id, 'name', name) FROM json_to_record(?::json) as o(id uuid, name text);", listOf(o))
val obj: ObjTest? = connection.selectOne("select json_build_object('id', id, 'name', name) FROM json_to_record(?::json) as o(id int, name text);", listOf(o))
assertNotNull(obj) assertNotNull(obj)
assertTrue(obj is ObjTest) assertTrue(obj is ObjTest)
assertEquals(obj!!.id, 88) assertEquals(obj!!.id, UUID.fromString("2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00"))
assertEquals(obj.name, "myName") assertEquals(obj.name, "myName")
} }
@Test @Test
fun callExec() { fun callExec() {
val o = ObjTest("myName") val o = ObjTest("myName")
val result = connection.exec("select json_build_object('id', 1, 'name', ?::json->>'name')", listOf(o)) val result = connection.exec("select json_build_object('id', '2c0243ed-ff4d-4b9f-a52b-e38c71b0ed00', 'name', ?::json->>'name')", listOf(o))
Assertions.assertEquals(1, result.rowsAffected) Assertions.assertEquals(1, result.rowsAffected)
} }
@@ -141,8 +144,8 @@ class ConnectionTest() : TestAbstract() {
val result: Paginated<ObjTest> = connection.select( val result: Paginated<ObjTest> = connection.select(
""" """
SELECT json_build_array( SELECT json_build_array(
json_build_object('id', 3, 'name', :name::text), json_build_object('id', '417aaa7e-7bc6-49b7-9fe8-6c8433b3f430', 'name', :name::text),
json_build_object('id', 4, 'name', :name::text || '-2') json_build_object('id', 'abd46e7a-e749-4ce4-8361-e7b64da89da6', 'name', :name::text || '-2')
), 10 as total ), 10 as total
LIMIT :limit OFFSET :offset LIMIT :limit OFFSET :offset
""".trimIndent(), """.trimIndent(),

View File

@@ -1,15 +1,22 @@
package fr.postgresjson package fr.postgresjson
import fr.postgresjson.entity.Entity
import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.EntityI
import fr.postgresjson.entity.mutable.* import fr.postgresjson.entity.EntityUpdatedAt
import fr.postgresjson.entity.EntityUpdatedBy
import fr.postgresjson.entity.Published
import fr.postgresjson.entity.UuidEntityExtended
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class EntityTest() { class EntityTest() {
private class User(id: Int?) : Entity<Int?>(id) private class User(id: UUID = UUID.randomUUID()) : Entity<UUID>(id)
private class ObjTest(var name: String) : UuidEntityExtended<Int?, User>(User(1), User(2)) private class ObjTest(var name: String) : UuidEntityExtended<Int?, User>(User(), User())
@Test @Test
fun getObject() { fun getObject() {
@@ -17,7 +24,7 @@ class EntityTest() {
assertTrue(obj is ObjTest) assertTrue(obj is ObjTest)
assertTrue(obj is UuidEntityExtended<Int?, User>) assertTrue(obj is UuidEntityExtended<Int?, User>)
assertTrue(obj is EntityI) assertTrue(obj is EntityI)
assertTrue(obj is Entity<Int?>) assertTrue(obj is Entity<UUID>)
assertTrue(obj is Published<User>) assertTrue(obj is Published<User>)
assertTrue(obj is EntityCreatedBy<User>) assertTrue(obj is EntityCreatedBy<User>)
assertTrue(obj is EntityUpdatedBy<User>) assertTrue(obj is EntityUpdatedBy<User>)

View File

@@ -10,6 +10,7 @@ import org.amshove.kluent.shouldThrow
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MigrationTest() : TestAbstract() { class MigrationTest() : TestAbstract() {
@@ -103,7 +104,7 @@ class MigrationTest() : TestAbstract() {
.getFunction("test_function") .getFunction("test_function")
.selectOne(listOf("test", "plip")) .selectOne(listOf("test", "plip"))
Assertions.assertEquals(objTest!!.id, 3) Assertions.assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d"))
Assertions.assertEquals(objTest.name, "test") Assertions.assertEquals(objTest.name, "test")
} }
@@ -119,7 +120,7 @@ class MigrationTest() : TestAbstract() {
.getFunction("test_function_duplicate") .getFunction("test_function_duplicate")
.selectOne(listOf("test")) .selectOne(listOf("test"))
Assertions.assertEquals(objTest!!.id, 3) Assertions.assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d"))
Assertions.assertEquals(objTest.name, "test") Assertions.assertEquals(objTest.name, "test")
val resources2 = this::class.java.getResource("/sql/function/Test2").toURI() val resources2 = this::class.java.getResource("/sql/function/Test2").toURI()

View File

@@ -2,13 +2,14 @@ package fr.postgresjson
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.mutable.IdEntity import fr.postgresjson.entity.UuidEntity
import org.junit.Assert import org.junit.Assert
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.util.UUID
class RequesterTest : TestAbstract() { class RequesterTest : TestAbstract() {
class ObjTest(var name: String) : IdEntity(1) class ObjTest(var name: String, id: UUID = UUID.fromString("5623d902-3067-42f3-bfd9-095dbb12c29f")) : UuidEntity(id)
@Test @Test
fun `get query from file`() { fun `get query from file`() {
@@ -18,7 +19,7 @@ class RequesterTest : TestAbstract() {
.getQuery("selectOne") .getQuery("selectOne")
.selectOne() .selectOne()
assertEquals(objTest!!.id, 2) assertEquals(objTest!!.id, UUID.fromString("829b1a29-5db8-47f9-9562-961c561ac528"))
assertEquals(objTest.name, "test") assertEquals(objTest.name, "test")
} }
@@ -30,7 +31,7 @@ class RequesterTest : TestAbstract() {
.getFunction("test_function") .getFunction("test_function")
.selectOne(listOf("test", "plip")) .selectOne(listOf("test", "plip"))
assertEquals(objTest!!.id, 3) assertEquals(objTest!!.id, UUID.fromString("457daad5-4f1b-4eb7-80ec-6882adb8cc7d"))
assertEquals(objTest.name, "test") assertEquals(objTest.name, "test")
} }

View File

@@ -1,6 +1,6 @@
package fr.postgresjson package fr.postgresjson
import fr.postgresjson.entity.mutable.IdEntity import fr.postgresjson.entity.UuidEntity
import fr.postgresjson.serializer.Serializer import fr.postgresjson.serializer.Serializer
import fr.postgresjson.serializer.deserialize import fr.postgresjson.serializer.deserialize
import fr.postgresjson.serializer.serialize import fr.postgresjson.serializer.serialize
@@ -10,23 +10,23 @@ import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import java.util.UUID
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class SerializerTest { internal class SerializerTest {
private class ObjTest(var val1: String, var val2: Int) : IdEntity(1) private class ObjTest(var val1: String, var val2: Int, id: UUID = UUID.fromString("1e5f5d41-6d14-4007-897b-0ed2616bec96")) : UuidEntity(id)
private class ObjTestDate(var val1: DateTime) : IdEntity(2) private class ObjTestDate(var val1: DateTime, id: UUID = UUID.fromString("829b1a29-5db8-47f9-9562-961c561ac528")) : UuidEntity(id)
private val serializer = Serializer() private val serializer = Serializer()
private val objSerialized: String = """{"val1":"plop","val2":123,"id":2}""" private val objSerialized: String = """{"val1":"plop","val2":123,"id":"829b1a29-5db8-47f9-9562-961c561ac528"}"""
private val objSerializedWithExtra: String = """{"val1":"plop","val2":123,"id":2,"toto":"tata"}""" private val objSerializedWithExtra: String = """{"val1":"plop","val2":123,"id":"829b1a29-5db8-47f9-9562-961c561ac528","toto":"tata"}"""
private val objSerializedUpdate = """{"val1":"update","val2":123}""" private val objSerializedUpdate = """{"val1":"update","val2":123}"""
private lateinit var obj: ObjTest private lateinit var obj: ObjTest
@BeforeEach @BeforeEach
fun before() { fun before() {
obj = ObjTest("plop", 123) obj = ObjTest("plop", 123, UUID.fromString("829b1a29-5db8-47f9-9562-961c561ac528"))
obj.id = 2
} }
@Test @Test

View File

@@ -8,7 +8,7 @@ create schema if not exists public;
create table if not exists test create table if not exists test
( (
id serial not null id uuid not null
constraint test_pk constraint test_pk
primary key, primary key,
name text name text
@@ -16,24 +16,24 @@ create table if not exists test
create table if not exists test2 create table if not exists test2
( (
id serial not null, id uuid not null,
title text, title text,
test_id integer test_id uuid
constraint test2_test_id_fk constraint test2_test_id_fk
references test references test
); );
INSERT INTO test (id, name) VALUES (1, 'plop') ON CONFLICT DO NOTHING; INSERT INTO test (id, name) VALUES ('1e5f5d41-6d14-4007-897b-0ed2616bec96', 'plop') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES (1, 'plop', 1) ON CONFLICT DO NOTHING; INSERT INTO test2 (id, title, test_id) VALUES ('1e5f5d41-6d14-4007-897b-0ed2616bec96', 'plop', '1e5f5d41-6d14-4007-897b-0ed2616bec96') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES (2, 'plip', 1) ON CONFLICT DO NOTHING; INSERT INTO test2 (id, title, test_id) VALUES ('829b1a29-5db8-47f9-9562-961c561ac528', 'plip', '1e5f5d41-6d14-4007-897b-0ed2616bec96') ON CONFLICT DO NOTHING;
INSERT INTO test2 (id, title, test_id) VALUES (3, 'ttt', null) ON CONFLICT DO NOTHING; INSERT INTO test2 (id, title, test_id) VALUES ('457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'ttt', null) ON CONFLICT DO NOTHING;
CREATE OR REPLACE FUNCTION test_function (name text default 'plop', IN hi text default 'hello', out result json) CREATE OR REPLACE FUNCTION test_function (name text default 'plop', IN hi text default 'hello', out result json)
LANGUAGE plpgsql LANGUAGE plpgsql
AS AS
$$ $$
BEGIN BEGIN
result = json_build_object('id', 3, 'name', name); result = json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name);
END; END;
$$; $$;
@@ -44,8 +44,8 @@ AS
$$ $$
BEGIN BEGIN
result = json_build_array( result = json_build_array(
json_build_object('id', 3, 'name', name), json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name),
json_build_object('id', 4, 'name', hi) json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', hi)
); );
END; END;
$$; $$;
@@ -56,8 +56,8 @@ AS
$$ $$
BEGIN BEGIN
SELECT json_build_array( SELECT json_build_array(
json_build_object('id', 3, 'name', name::text), json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name::text),
json_build_object('id', 4, 'name', name::text || '-2') json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', name::text || '-2')
), ),
10 10
INTO result, total INTO result, total
@@ -70,7 +70,7 @@ CREATE OR REPLACE FUNCTION test_function_object (inout resource json)
AS AS
$$ $$
BEGIN BEGIN
resource = json_build_object('id', 1, 'name', 'changedName'); resource = json_build_object('id', '1e5f5d41-6d14-4007-897b-0ed2616bec96', 'name', 'changedName');
END; END;
$$; $$;

View File

@@ -3,6 +3,6 @@ LANGUAGE plpgsql
AS AS
$$ $$
BEGIN BEGIN
result = json_build_object('id', 3, 'name', name); result = json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name);
END; END;
$$ $$

View File

@@ -3,6 +3,6 @@ LANGUAGE plpgsql
AS AS
$$ $$
BEGIN BEGIN
resource = json_build_object('id', 1, 'name', 'changedName'); resource = json_build_object('id', '1e5f5d41-6d14-4007-897b-0ed2616bec96', 'name', 'changedName');
END; END;
$$ $$

View File

@@ -4,8 +4,8 @@ AS
$$ $$
BEGIN BEGIN
result = json_build_array( result = json_build_array(
json_build_object('id', 3, 'name', name), json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name),
json_build_object('id', 4, 'name', hi) json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', hi)
); );
END; END;
$$ $$

View File

@@ -4,8 +4,8 @@ AS
$$ $$
BEGIN BEGIN
SELECT json_build_array( SELECT json_build_array(
json_build_object('id', 3, 'name', name::text), json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name::text),
json_build_object('id', 4, 'name', name::text || '-2') json_build_object('id', '8d20abb0-7f77-4b6c-9991-44acd3c88faa', 'name', name::text || '-2')
), ),
10 10
INTO result, total INTO result, total

View File

@@ -3,6 +3,6 @@ CREATE OR REPLACE FUNCTION test_function_duplicate (name text default 'plop') re
AS AS
$$ $$
BEGIN BEGIN
return json_build_object('id', 3, 'name', name); return json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', name);
END; END;
$$ $$

View File

@@ -1 +1 @@
select json_build_object('id', 2, 'name', 'test'); select json_build_object('id', '829b1a29-5db8-47f9-9562-961c561ac528', 'name', 'test');

View File

@@ -1 +1 @@
select json_build_object('id', 2, 'name', :name::text), 'plop'::text as other; select json_build_object('id', '829b1a29-5db8-47f9-9562-961c561ac528', 'name', :name::text), 'plop'::text as other;

View File

@@ -1,5 +1,5 @@
SELECT json_build_array( SELECT json_build_array(
json_build_object('id', 3, 'name', :name::text), json_build_object('id', '457daad5-4f1b-4eb7-80ec-6882adb8cc7d', 'name', :name::text),
json_build_object('id', 4, 'name', :name::text || '-2') json_build_object('id', '6085c12e-e94d-4ae1-b7ad-23acc7a82a98', 'name', :name::text || '-2')
), 10 as total ), 10 as total
LIMIT :limit OFFSET :offset LIMIT :limit OFFSET :offset