| @@ -1,94 +0,0 @@ | |||||
| package com.ffii.fpsms.modules.common.demo | |||||
| import org.slf4j.LoggerFactory | |||||
| import org.springframework.beans.factory.annotation.Value | |||||
| import org.springframework.boot.ApplicationArguments | |||||
| import org.springframework.boot.ApplicationRunner | |||||
| import org.springframework.context.annotation.Profile | |||||
| import org.springframework.core.annotation.Order | |||||
| import org.springframework.core.io.ClassPathResource | |||||
| import org.springframework.jdbc.core.JdbcTemplate | |||||
| import org.springframework.stereotype.Component | |||||
| import java.nio.charset.StandardCharsets | |||||
| import java.sql.Connection | |||||
| import javax.sql.DataSource | |||||
| /** | |||||
| * Extra fictional rows so demo screens and report downloads have something to show. | |||||
| * Runs only after the base sandbox exists, and only against fpsms_demo. | |||||
| */ | |||||
| @Component | |||||
| @Profile("db-demo") | |||||
| @Order(2) | |||||
| class DemoCoverageSeeder( | |||||
| @Value("\${spring.datasource.jdbc-url}") private val jdbcUrl: String, | |||||
| private val dataSource: DataSource, | |||||
| private val jdbcTemplate: JdbcTemplate, | |||||
| ) : ApplicationRunner { | |||||
| private val log = LoggerFactory.getLogger(DemoCoverageSeeder::class.java) | |||||
| override fun run(args: ApplicationArguments) { | |||||
| val configuredName = databaseName(jdbcUrl) | |||||
| val connectedName = dataSource.connection.use { it.catalog } | |||||
| if (configuredName != DEMO_DATABASE || !DEMO_DATABASE.equals(connectedName, ignoreCase = true)) { | |||||
| throw IllegalStateException( | |||||
| "Demo coverage refused to start. Configured database is '$configuredName', connected database is '$connectedName'.", | |||||
| ) | |||||
| } | |||||
| val already = jdbcTemplate.queryForObject( | |||||
| "SELECT COUNT(*) FROM settings WHERE name = ?", | |||||
| Int::class.java, | |||||
| MARKER, | |||||
| ) ?: 0 | |||||
| if (already > 0) { | |||||
| log.info("Demo coverage data already present.") | |||||
| return | |||||
| } | |||||
| val script = ClassPathResource(SCRIPT).inputStream.use { it.readBytes().toString(StandardCharsets.UTF_8) } | |||||
| dataSource.connection.use { connection -> | |||||
| connection.autoCommit = false | |||||
| try { | |||||
| executeScript(connection, script) | |||||
| connection.commit() | |||||
| } catch (ex: Exception) { | |||||
| connection.rollback() | |||||
| throw IllegalStateException("Demo coverage data failed to load.", ex) | |||||
| } | |||||
| } | |||||
| log.info("Demo coverage data loaded.") | |||||
| } | |||||
| private fun executeScript(connection: Connection, script: String) { | |||||
| val statement = StringBuilder() | |||||
| connection.createStatement().use { jdbc -> | |||||
| for (rawLine in script.lineSequence()) { | |||||
| val line = rawLine.trim() | |||||
| if (line.isEmpty() || line.startsWith("--")) continue | |||||
| statement.append(rawLine).append('\n') | |||||
| if (line.endsWith(";")) { | |||||
| val sql = statement.toString().trim().removeSuffix(";").trim() | |||||
| statement.clear() | |||||
| if (sql.isNotEmpty()) jdbc.execute(sql) | |||||
| } | |||||
| } | |||||
| } | |||||
| val tail = statement.toString().trim().removeSuffix(";").trim() | |||||
| if (tail.isNotEmpty()) { | |||||
| connection.createStatement().use { it.execute(tail) } | |||||
| } | |||||
| } | |||||
| private fun databaseName(jdbcUrl: String): String? { | |||||
| val path = jdbcUrl.substringBefore('?').substringAfter("://") | |||||
| val slash = path.indexOf('/') | |||||
| if (slash < 0 || slash == path.lastIndex) return null | |||||
| return path.substring(slash + 1).trim().ifEmpty { null } | |||||
| } | |||||
| companion object { | |||||
| private const val DEMO_DATABASE = "fpsms_demo" | |||||
| private const val MARKER = "DEMO.coverage.v1" | |||||
| private const val SCRIPT = "demo/demo-coverage.sql" | |||||
| } | |||||
| } | |||||