Compare commits

...

9 Commits

Author SHA1 Message Date
Ilia Ravin 8b9643b60a indent fix 2022-11-10 17:20:05 +03:00
Ilia Ravin d0c7ca03fb fix fs 2 2022-11-03 14:06:09 +03:00
Ilia Ravin 18248a2773 fix functional style 2022-11-03 13:47:18 +03:00
Ilia Ravin 4a32a1c09d styleguide and refactor processor 2022-11-02 15:38:43 +03:00
Илья Равин 6865e3fdb8 styleguide 2022-11-01 15:00:37 +03:00
Ilia Ravin 0e381ff70c Naming fix 2022-10-31 18:45:02 +03:00
Ilia Ravin 5770faf7db update module for multiple datasource 2022-10-31 18:37:40 +03:00
Ilia Ravin 6b16393ccb gradle build 2022-10-31 14:57:18 +03:00
Ilia Ravin 301c952ef9 smart migrations module 2022-10-31 14:56:28 +03:00
13 changed files with 245 additions and 1 deletions

View File

@ -0,0 +1,9 @@
package ru.touchin.common.spring.annotations
import org.springframework.context.annotation.Import
import ru.touchin.common.spring.processors.RequiredByBeanDefinitionPostProcessor
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS)
@Import(RequiredByBeanDefinitionPostProcessor::class)
annotation class RequiredBy(vararg val value: String)

View File

@ -0,0 +1,38 @@
package ru.touchin.common.spring.processors
import org.springframework.beans.BeansException
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
import org.springframework.stereotype.Component
import ru.touchin.common.spring.annotations.RequiredBy
@Component
class RequiredByBeanDefinitionPostProcessor : BeanDefinitionRegistryPostProcessor {
@Throws(BeansException::class)
override fun postProcessBeanDefinitionRegistry(registry: BeanDefinitionRegistry) {
for (beanName in registry.beanDefinitionNames) {
val beanClassName = registry.getBeanDefinition(beanName).beanClassName?:continue
getDependantBeanNames(beanClassName).forEach { dependantBeanName ->
registry.getBeanDefinition(dependantBeanName).setDependsOn(beanName)
}
}
}
@Throws(BeansException::class)
override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) {
}
private fun getDependantBeanNames(beanClassName: String): List<String> {
val beanClass = Class.forName(beanClassName)
var dependantBeans = emptyList<String>()
if (beanClass.isAnnotationPresent(RequiredBy::class.java)) {
dependantBeans = beanClass.getAnnotation(RequiredBy::class.java).value.toList()
}
return dependantBeans
}
}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -58,3 +58,4 @@ include("s3-storage")
include("server-info-spring-web")
include("geoip-core")
include("user-agent")
include("smart-migration")

View File

@ -0,0 +1,12 @@
plugins {
id("kotlin")
id("kotlin-spring")
id("maven-publish")
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation project(":common-spring")
}

View File

@ -0,0 +1,59 @@
@file:Suppress("unused")
package ru.touchin.smartmigration
import org.springframework.stereotype.Component
import ru.touchin.common.spring.annotations.RequiredBy
import ru.touchin.smartmigration.logic.DataSourceSQL
import ru.touchin.smartmigration.logic.factory.DataSourceSqlFactoryImpl
import java.sql.Date
import java.text.SimpleDateFormat
import javax.annotation.PostConstruct
import javax.sql.DataSource
private val SQL_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
private val CURRENT_TIME_SQL: String
get() = SQL_DATE_FORMAT.format(Date(System.currentTimeMillis()))
@Component
@RequiredBy("liquibase")
class BeforeLiquibase(
private val dataSource: DataSource
) {
val dataSourceSql: DataSourceSQL = DataSourceSqlFactoryImpl()
.getDataSourceSql(dataSource.connection.metaData.databaseProductName)
@PostConstruct
fun doAction() {
val buildNumber = System.getenv("BUILD_NUMBER")
?: return
checkMigrationTable()
if (checkBuildMigrationExecuted(buildNumber)) {
System.setProperty("spring.liquibase.enabled", "false")
} else {
insertMigration(buildNumber)
}
}
private fun checkBuildMigrationExecuted(buildNumber: String): Boolean {
return dataSourceSql.getMigrationCheckSQL(buildNumber).let {
dataSource.connection.createStatement().executeQuery(it).next()
}
}
private fun checkMigrationTable() {
dataSourceSql.getTableCheckSQL().let {
dataSource.connection.createStatement().execute(it)
}
}
private fun insertMigration(buildNumber: String) {
dataSourceSql.getInsertMigrationSQL(buildNumber, CURRENT_TIME_SQL).let {
dataSource.connection.createStatement().execute(it)
}
}
}

View File

@ -0,0 +1,8 @@
package ru.touchin.smartmigration
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
@Configuration
@ComponentScan("ru.touchin.smartmigration")
open class SmartMigrationConfig

View File

@ -0,0 +1,7 @@
package ru.touchin.smartmigration.annotation
import org.springframework.context.annotation.Import
import ru.touchin.smartmigration.SmartMigrationConfig
@Import(SmartMigrationConfig::class)
annotation class EnableSmartMigration

View File

@ -0,0 +1,11 @@
package ru.touchin.smartmigration.logic
interface DataSourceSQL {
fun getTableCheckSQL(): String
fun getMigrationCheckSQL(buildNumber: String): String
fun getInsertMigrationSQL(buildNumber: String, formattedTime: String): String
}

View File

@ -0,0 +1,33 @@
package ru.touchin.smartmigration.logic
import org.intellij.lang.annotations.Language
private const val MIGRATION_TABLE_NAME = "SMART_MIGRATION"
class PostgresDataSourceImpl: DataSourceSQL {
@Language("SQL")
override fun getTableCheckSQL(): String {
return """
CREATE TABLE IF NOT EXISTS smart_migration (
ID BIGSERIAL PRIMARY KEY,
BUILD_NUMBER VARCHAR(255) NOT NULL,
DATE timestamp NOT NULL
);
"""
}
@Language("SQL")
override fun getMigrationCheckSQL(buildNumber: String): String {
return "SELECT * FROM $MIGRATION_TABLE_NAME WHERE BUILD_NUMBER = '$buildNumber'"
}
@Language("SQL")
override fun getInsertMigrationSQL(buildNumber: String, formattedTime: String): String {
return """
INSERT INTO $MIGRATION_TABLE_NAME (BUILD_NUMBER, DATE)
VALUES ('$buildNumber', '$formattedTime');
"""
}
}

View File

@ -0,0 +1,38 @@
package ru.touchin.smartmigration.logic
import org.intellij.lang.annotations.Language
private const val MIGRATION_TABLE_NAME = "SMART_MIGRATION"
class SqlDatasourceImpl:DataSourceSQL {
@Language("TSQL")
override fun getTableCheckSQL(): String {
return """
IF NOT EXISTS (
SELECT * FROM sysobjects WHERE name = '$MIGRATION_TABLE_NAME' and xtype='U'
)
CREATE TABLE SMART_MIGRATION (
ID BIGINT PRIMARY KEY IDENTITY ,
BUILD_NUMBER VARCHAR(255) NOT NULL,
DATE DATETIME NOT NULL
);
"""
}
@Language("TSQL")
override fun getMigrationCheckSQL(buildNumber: String): String {
return "SELECT * FROM $MIGRATION_TABLE_NAME WHERE BUILD_NUMBER = '$buildNumber'"
}
@Language("TSQL")
override fun getInsertMigrationSQL(buildNumber: String, formattedTime: String): String {
return """
INSERT INTO $MIGRATION_TABLE_NAME (BUILD_NUMBER, DATE)
VALUES ('$buildNumber', '$formattedTime');
"""
}
}

View File

@ -0,0 +1,9 @@
package ru.touchin.smartmigration.logic.factory
import ru.touchin.smartmigration.logic.DataSourceSQL
interface DataSourceSqlFactory {
fun getDataSourceSql(driverName: String): DataSourceSQL
}

View File

@ -0,0 +1,19 @@
package ru.touchin.smartmigration.logic.factory
import ru.touchin.smartmigration.logic.DataSourceSQL
import ru.touchin.smartmigration.logic.PostgresDataSourceImpl
import ru.touchin.smartmigration.logic.SqlDatasourceImpl
class DataSourceSqlFactoryImpl: DataSourceSqlFactory {
override fun getDataSourceSql(driverName: String): DataSourceSQL {
return when(driverName){
"Microsoft SQL Server" -> SqlDatasourceImpl()
"PostgresSQL" -> PostgresDataSourceImpl()
else -> {
PostgresDataSourceImpl()
}
}
}
}