feature: get migrations from DB

This commit is contained in:
2019-07-02 17:28:52 +02:00
parent 551a6c11cd
commit f48b06b596
5 changed files with 191 additions and 77 deletions

View File

@@ -1,15 +1,16 @@
package fr.postgresjson.migration package fr.postgresjson.migration
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import java.util.*
import fr.postgresjson.definition.Function as DefinitionFunction import fr.postgresjson.definition.Function as DefinitionFunction
class Function( class Function(
val up: DefinitionFunction, val up: DefinitionFunction,
val down: DefinitionFunction, val down: DefinitionFunction,
private val connection: Connection private val connection: Connection,
override var executedAt: Date? = null
): Migration { ): Migration {
val name = up.name val name = up.name
enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) }
init { init {
if (up.name !== down.name) { if (up.name !== down.name) {
@@ -17,32 +18,51 @@ class Function(
} }
} }
override fun up(): Int { constructor(
up: String,
down: String,
connection: Connection,
executedAt: Date? = null):
this(
DefinitionFunction(up),
DefinitionFunction(down),
connection,
executedAt
)
override fun up(): Migration.Status {
connection.exec(up.script) connection.exec(up.script)
return 1 // TODO insert to migration Table
return Migration.Status.OK
} }
override fun down(): Int { override fun down(): Migration.Status {
connection.exec(down.script) connection.exec(down.script)
return 1 // TODO insert to migration Table
return Migration.Status.OK
} }
override fun test(): Int { override fun test(): Migration.Status {
connection.inTransaction { connection.inTransaction {
connection.exec(up.script) up()
connection.exec(down.script) down()
it.sendQuery("ROLLBACK"); it.sendQuery("ROLLBACK");
} }.join()
return 1
return Migration.Status.OK // TODO
} }
override fun status(): Int { override fun status(): Migration.Status {
val result = connection.inTransaction { val result = connection.inTransaction {
connection.exec(up.script) up()
connection.exec(down.script) down()
it.sendQuery("ROLLBACK") it.sendQuery("ROLLBACK")
}.join() }.join()
return result.rowsAffected.toInt() return Migration.Status.OK // TODO
}
override fun doExecute(): Boolean {
return executedAt === null
} }
} }

View File

@@ -2,24 +2,68 @@ package fr.postgresjson.migration
import com.github.jasync.sql.db.util.size import com.github.jasync.sql.db.util.size
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.Entity
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.util.*
import fr.postgresjson.definition.Function as DefinitionFunction import fr.postgresjson.definition.Function as DefinitionFunction
class MigrationEntity(
val filename: String,
val definition: String,
val executedAt: Date,
val up: String,
val down: String,
val version: Int
): Entity<String?>(filename) {
interface Migration {
fun up(): Int
fun down(): Int
fun test(): Int
fun status(): Int
} }
class Migrations(directory: File, private val connection: Connection): Migration { interface Migration {
private val queries: MutableList<Query> = mutableListOf() var executedAt: Date?
fun up(): Status
fun down(): Status
fun test(): Status
fun status(): Status
fun doExecute(): Boolean
enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) }
}
class Migrations(directory: File, private val connection: Connection) {
private val queries: MutableMap<String, Query> = mutableMapOf()
private val functions: MutableMap<String, Function> = mutableMapOf() private val functions: MutableMap<String, Function> = mutableMapOf()
private var initialized = false private var initialized = false
init { init {
initDB()
getMigrationFromDB()
getMigrationFromDirectory(directory)
}
/**
* Get all migration from DB
*/
private fun getMigrationFromDB() {
File(this::class.java.getResource("/sql/migration/findAllFunction.sql").toURI()).let {
connection.select<String, List<MigrationEntity?>>(it.readText())
.filterNotNull().map { function ->
functions[function.filename] = Function(function.up, function.down, connection, function.executedAt)
}
}
File(this::class.java.getResource("/sql/migration/findAllHistory.sql").toURI()).let {
connection.select<String, List<MigrationEntity?>>(it.readText())
.filterNotNull().map { query ->
queries[query.filename] = Query(query.filename, query.up, query.down, connection, query.executedAt)
}
}
}
/**
* Get all migration from Directory
*/
private fun getMigrationFromDirectory(directory: File) {
directory.walk().filter { directory.walk().filter {
it.isDirectory it.isDirectory
}.forEach { directory -> }.forEach { directory ->
@@ -31,7 +75,8 @@ class Migrations(directory: File, private val connection: Connection): Migration
try { try {
val down = File("$it.down.sql").readText() val down = File("$it.down.sql").readText()
val up = file.readText() val up = file.readText()
addQuery(file.name, up, down) val name = file.name.substring(0, file.name.size - 7)
addQuery(name, up, down)
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
throw DownMigrationNotDefined("$it.down.sql", e) throw DownMigrationNotDefined("$it.down.sql", e)
} }
@@ -46,22 +91,25 @@ class Migrations(directory: File, private val connection: Connection): Migration
} }
} }
enum class Direction { UP, DOWN }
class DownMigrationNotDefined(path: String, cause: FileNotFoundException): Throwable("The file $path whas not found", cause) class DownMigrationNotDefined(path: String, cause: FileNotFoundException): Throwable("The file $path whas not found", cause)
fun addFunction(definition: DefinitionFunction): Migrations { fun addFunction(definition: DefinitionFunction): Migrations {
if (functions[definition.name] === null) {
functions[definition.name] = Function(definition, definition, connection) functions[definition.name] = Function(definition, definition, connection)
}
return this return this
} }
fun addFunction(sql: String): Migrations { fun addFunction(sql: String): Migrations {
DefinitionFunction(sql).let { addFunction(DefinitionFunction(sql))
functions[it.name] = Function(it, it, connection)
}
return this return this
} }
fun addQuery(name: String, up: String, down: String): Migrations { fun addQuery(name: String, up: String, down: String): Migrations {
queries.add(Query(name, up, down, connection)) if (queries[name] === null) {
queries[name] = Query(name, up, down, connection)
}
return this return this
} }
@@ -77,42 +125,73 @@ class Migrations(directory: File, private val connection: Connection): Migration
} }
} }
override fun up(): Int { fun up(): Map<String, Migration.Status> {
initDB() val list: MutableMap<String, Migration.Status> = mutableMapOf()
var count = 0
queries.forEach { queries.forEach {
it.up() it.value.let { query ->
++count if (query.doExecute()) {
query.up().let { status ->
list[query.name] = status
}
}
}
} }
return count functions.forEach {
it.value.let { function ->
if (function.doExecute()) {
function.up().let { status ->
list[function.name] = status
}
}
}
} }
override fun down(): Int { return list.toMap()
initDB() }
var count = 0
fun down(): Map<String, Migration.Status> {
val list: MutableMap<String, Migration.Status> = mutableMapOf()
queries.forEach { queries.forEach {
it.down() it.value.let { query ->
++count if (query.doExecute()) {
query.down().let { status ->
list[query.name] = status
}
}
}
} }
return count functions.forEach {
it.value.let { function ->
if (function.doExecute()) {
function.down().let { status ->
list[function.name] = status
}
}
}
} }
override fun test(): Int { return list.toMap()
initDB() }
var count = 0
fun test(): Map<Pair<String, Direction>, Migration.Status> {
var list: MutableMap<Pair<String, Direction>, Migration.Status> = mutableMapOf()
connection.inTransaction { connection.inTransaction {
count += up() up().map {
count += down() list.set(Pair(it.key, Direction.UP), it.value)
}
down().map {
list.set(Pair(it.key, Direction.DOWN), it.value)
}
it.sendQuery("ROLLBACK"); it.sendQuery("ROLLBACK");
}.join() }.join()
return count return list.toMap()
} }
override fun status(): Int { fun status(): Map<String, Int> {
initDB()
TODO("not implemented") TODO("not implemented")
} }
} }

View File

@@ -1,41 +1,51 @@
package fr.postgresjson.migration package fr.postgresjson.migration
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.entity.Entity
import java.util.*
class Query( class Query(
val name: String, val name: String,
val up: String, val up: String,
val down: String, val down: String,
private val connection: Connection private val connection: Connection,
): Migration { override var executedAt: Date? = null
enum class Status(i: Int) { OK(2), UP_FAIL(0), DOWN_FAIL(1) } ): Migration, Entity<String?>(name) {
override fun up(): Migration.Status {
override fun up(): Int {
connection.exec(up) connection.exec(up)
return 1 // TODO insert to migration Table
return Migration.Status.OK
} }
override fun down(): Int { override fun down(): Migration.Status {
connection.exec(down) connection.exec(down)
return 1 // TODO insert to migration Table
return Migration.Status.OK
} }
override fun test(): Int { override fun test(): Migration.Status {
connection.inTransaction { connection.inTransaction {
connection.exec(up) up()
connection.exec(down) down()
it.sendQuery("ROLLBACK"); it.sendQuery("ROLLBACK");
} }.join()
return 1
return Migration.Status.OK // TODO
} }
override fun status(): Int { override fun status(): Migration.Status {
val result = connection.inTransaction { val result = connection.inTransaction {
connection.exec(up) up()
connection.exec(down) down()
it.sendQuery("ROLLBACK") it.sendQuery("ROLLBACK")
}.join() }.join()
return result.rowsAffected.toInt() return Migration.Status.OK // TODO
}
override fun doExecute(): Boolean {
return executedAt === null
} }
} }

View File

@@ -1,7 +1,9 @@
package fr.postgresjson package fr.postgresjson
import fr.postgresjson.migration.Migration
import fr.postgresjson.migration.Migrations import fr.postgresjson.migration.Migrations
import org.amshove.kluent.`should be equal to` import org.amshove.kluent.`should be equal to`
import org.amshove.kluent.`should contain`
import org.amshove.kluent.invoking import org.amshove.kluent.invoking
import org.amshove.kluent.shouldThrow import org.amshove.kluent.shouldThrow
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@@ -14,7 +16,8 @@ class MigrationTest(): TestAbstract() {
fun upQuery() { fun upQuery() {
val resources = File(this::class.java.getResource("/sql/migrations").toURI()) val resources = File(this::class.java.getResource("/sql/migrations").toURI())
val m = Migrations(resources, getConnextion()) val m = Migrations(resources, getConnextion())
m.up() `should be equal to` 1 m.up() `should contain` Pair("1", Migration.Status.OK)
m.up().size `should be equal to` 1
} }
@Test @Test
@@ -29,14 +32,15 @@ class MigrationTest(): TestAbstract() {
fun downQuery() { fun downQuery() {
val resources = File(this::class.java.getResource("/sql/migrations").toURI()) val resources = File(this::class.java.getResource("/sql/migrations").toURI())
val m = Migrations(resources, getConnextion()) val m = Migrations(resources, getConnextion())
m.down() `should be equal to` 1 m.down() `should contain` Pair("1", Migration.Status.OK)
m.down().size `should be equal to` 1
} }
@Test @Test
fun `test up and down migrations`() { fun `test up and down migrations`() {
val resources = File(this::class.java.getResource("/sql/real_migrations").toURI()) val resources = File(this::class.java.getResource("/sql/real_migrations").toURI())
val m = Migrations(resources, getConnextion()) val m = Migrations(resources, getConnextion())
m.test() `should be equal to` 2 m.test().size `should be equal to` 2
m.test() `should be equal to` 2 m.test().size `should be equal to` 2
} }
} }

View File

@@ -1,2 +1,3 @@
drop schema public cascade; drop schema if exists public cascade;
create schema if not exists public; create schema if not exists public;
drop schema if exists migration cascade;