All Downloads are FREE. Search and download functionalities are using the official Maven repository.

datamaintain.core.config.DatamaintainConfig.kt Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
package datamaintain.core.config

import datamaintain.core.config.ConfigKey.Companion.overrideBySystemProperties
import datamaintain.core.config.CoreConfigKey.*
import datamaintain.core.db.driver.DBType
import datamaintain.core.db.driver.DatamaintainDriverConfig
import datamaintain.core.exception.DatamaintainBuilderMandatoryException
import datamaintain.core.script.TagMatcher
import datamaintain.core.step.executor.ExecutionMode
import datamaintain.domain.script.ScriptAction
import datamaintain.domain.script.Tag
import mu.KotlinLogging
import java.io.File
import java.io.InputStream
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*

private val logger = KotlinLogging.logger {}

data class DatamaintainConfig @JvmOverloads constructor(val name: String? = null,
                                                        val workingDirectory: Path = Paths.get(System.getProperty("user.dir")),
                                                        val path: Path = Paths.get(SCAN_PATH.default!!),
                                                        val identifierRegex: Regex = Regex(SCAN_IDENTIFIER_REGEX.default!!),
                                                        val doesCreateTagsFromFolder: Boolean = CREATE_TAGS_FROM_FOLDER.default!!.toBoolean(),
                                                        val whitelistedTags: Set = setOf(),
                                                        val blacklistedTags: Set = setOf(),
                                                        val tagsToPlayAgain: Set = setOf(),
                                                        val overrideExecutedScripts: Boolean = PRUNE_OVERRIDE_UPDATED_SCRIPTS.default!!.toBoolean(),
                                                        val tagsMatchers: Set = setOf(),
                                                        val checkRules: Sequence = emptySequence(),
                                                        val executionMode: ExecutionMode = defaultExecutionMode,
                                                        val defaultScriptAction: ScriptAction = defaultAction,
                                                        val driverConfig: DatamaintainDriverConfig,
                                                        val filenameRegex: Regex = defaultFilenameRegex(driverConfig),
                                                        val verbose: Boolean = VERBOSE.default!!.toBoolean(),
                                                        val porcelain: Boolean = PRINT_RELATIVE_PATH_OF_SCRIPT.default!!.toBoolean(),
                                                        val flags: List = emptyList()) {

    private constructor(builder: Builder): this(
        builder.name,
        builder.workingDirectory,
        builder.path,
        builder.identifierRegex,
        builder.doesCreateTagsFromFolder,
        builder.whitelistedTags,
        builder.blacklistedTags,
        builder.tagsToPlayAgain,
        builder.overrideExecutedScripts,
        builder.tagsMatchers,
        builder.checkRules.asSequence(),
        builder.executionMode,
        builder.defaultScriptAction,
        builder.driverConfig,
        builder.filenameRegex,
        builder.verbose,
        builder.porcelain,
        builder.flags
    )

    companion object {
        private val defaultExecutionMode = ExecutionMode.NORMAL
        public val defaultAction = ScriptAction.RUN

        @JvmStatic
        fun buildConfig(configInputStream: InputStream, driverConfig: DatamaintainDriverConfig): DatamaintainConfig {
            val props = Properties()
            props.load(configInputStream)
            return buildConfig(driverConfig, props)
        }

        @JvmStatic
        @JvmOverloads
        fun buildConfig(driverConfig: DatamaintainDriverConfig, props: Properties = Properties()): DatamaintainConfig {
            overrideBySystemProperties(props, values().asList())

            val workingDirectoryPath = buildWorkingDirectoryPath(props)
            enrichFromParentConfig(workingDirectoryPath, props)

            var executionMode = ExecutionMode.fromNullable(props.getNullableProperty(EXECUTION_MODE), defaultExecutionMode)

            val scriptAction = ScriptAction.fromNullable(props.getNullableProperty(DEFAULT_SCRIPT_ACTION), defaultAction)

            val scanPath = buildAbsoluteScanPath(workingDirectoryPath, props)

            val filenameRegex = props.getNullableProperty(SCAN_FILENAME_REGEX)?.toRegex() ?: defaultFilenameRegex(driverConfig)

            return DatamaintainConfig(
                    props.getProperty(CONFIG_NAME.key),
                    workingDirectoryPath,
                    scanPath,
                    Regex(props.getProperty(SCAN_IDENTIFIER_REGEX)),
                    props.getProperty(CREATE_TAGS_FROM_FOLDER).toBoolean(),
                    extractTags(props.getNullableProperty(TAGS_WHITELISTED)),
                    extractTags(props.getNullableProperty(TAGS_BLACKLISTED)),
                    extractTags(props.getNullableProperty(PRUNE_TAGS_TO_RUN_AGAIN)),
                    props.getProperty(PRUNE_OVERRIDE_UPDATED_SCRIPTS).toBoolean(),
                    props.getStringPropertiesByPrefix(TAG.key)
                            .map { TagMatcher.parse(it.first.replace("${TAG.key}.", ""), it.second, scanPath) }
                            .toSet(),
                    extractSequence(props.getNullableProperty(CHECK_RULES)),
                    executionMode,
                    scriptAction,
                    driverConfig,
                    filenameRegex,
                    props.getProperty(VERBOSE).toBoolean(),
                    props.getProperty(PRINT_RELATIVE_PATH_OF_SCRIPT).toBoolean(),
                    extractSequence(props.getNullableProperty(FLAGS)).toList()
            )
        }

        private fun buildWorkingDirectoryPath(props: Properties): Path {
            return props.getProperty(WORKING_DIRECTORY_PATH)
                    .let { Paths.get(it) }
                    .also { it.toAbsolutePath().normalize() }
        }

        private fun buildAbsoluteScanPath(workingDirectoryPath: Path, props: Properties): Path {
            var scanPath = Paths.get(props.getProperty(SCAN_PATH))

            if (!scanPath.isAbsolute) {
                scanPath = workingDirectoryPath.resolve(scanPath)
            }

            return scanPath.toAbsolutePath().normalize()
        }

        private fun enrichFromParentConfig(workingDirectoryPath: Path, props: Properties) {
            getParentConfigFile(workingDirectoryPath, props)?.also {
                enrichFromParentProperties(workingDirectoryPath, props, it)
            }
        }

        private fun enrichFromParentProperties(workingDirectoryPath: Path, props: Properties, file: File) {
            val parentProps = Properties()

            file.inputStream().use {
                parentProps.load(it)
            }

            logger.info { ("Load new config keys from parent config located at" +
                    " ${file.toPath().toAbsolutePath().normalize()}").trimMargin() }

            parentProps.forEach {
                props.putIfAbsent(it.key, it.value)
            }

            getParentConfigFile(workingDirectoryPath, parentProps)?.also {
                enrichFromParentProperties(workingDirectoryPath, props, it)
            }
        }

        private fun getParentConfigFile(workingDirectoryPath: Path, props: Properties): File? {
            return props.getProperty(PARENT_CONFIG_PATH.key)
                    ?.let { Paths.get(it) }
                    ?.let {
                        if (!it.isAbsolute) workingDirectoryPath.resolve(it) else it
                    }
                    ?.toAbsolutePath()
                    ?.normalize()
                    ?.toFile()
        }

        private fun extractTags(tags: String?): Set {
            return tags?.split(",")
                    ?.map { Tag(it) }
                    ?.toSet()
                    ?: setOf()
        }

        private fun extractSequence(string: String?): Sequence {
            return if (string.isNullOrEmpty()) {
                sequenceOf()
            } else {
                string.splitToSequence(",")
            }
        }

        private fun defaultFilenameRegex(datamaintainDriverConfig: DatamaintainDriverConfig) =
            DBType.tryFindFromString(datamaintainDriverConfig.dbType)?.filenameRegex ?: ".*".toRegex()
    }

    fun log() {
        logger.info { "Configuration: " }

        workingDirectory.also { logger.info { "- working directory -> $it" } }
        name?.also { logger.info { "- name -> $it" } }
        path.let { logger.info { "- path -> $it" } }
        identifierRegex.let { logger.info { "- identifier regex -> ${it.pattern}" } }
        defaultScriptAction.let { logger.info { "- script action -> ${it}" } }
        executionMode.let { logger.info { "- execution mode -> $it" } }
        tagsMatchers.let { logger.info { "- tags -> $tagsMatchers" } }
        whitelistedTags.let { logger.info { "- whitelisted tags -> $it" } }
        blacklistedTags.let { logger.info { "- blacklisted tags -> $it" } }
        tagsToPlayAgain.let { logger.info { "- tags to play again -> $it" } }
        overrideExecutedScripts.let { logger.info { "- Allow override executed script -> ${it}" } }
        checkRules.let { logger.info { "- rules -> ${checkRules.toList()}" } }
        verbose.let { logger.info { "- verbose -> $it" } }
        porcelain.let { logger.info { "- porcelain -> $it" } }
        flags.let { logger.info { "- flags -> $it" } }
        logger.info { "" }
    }

    class Builder {
        // mandatory
        lateinit var driverConfig: DatamaintainDriverConfig
            private set

        lateinit var filenameRegex: Regex
            private set

        // optional
        var name: String? = null
            private set
        var workingDirectory: Path = Paths.get(System.getProperty("user.dir"))
            private set
        var path: Path = Paths.get(SCAN_PATH.default!!)
            private set
        var identifierRegex: Regex = Regex(SCAN_IDENTIFIER_REGEX.default!!)
            private set
        var doesCreateTagsFromFolder: Boolean = CREATE_TAGS_FROM_FOLDER.default!!.toBoolean()
            private set
        var whitelistedTags: MutableSet = mutableSetOf()
            private set
        var blacklistedTags: MutableSet = mutableSetOf()
            private set
        var tagsToPlayAgain: MutableSet = mutableSetOf()
            private set
        var overrideExecutedScripts: Boolean = PRUNE_OVERRIDE_UPDATED_SCRIPTS.default!!.toBoolean()
            private set
        var tagsMatchers: MutableSet = mutableSetOf()
            private set
        var checkRules: MutableList = mutableListOf()
            private set
        var executionMode: ExecutionMode = defaultExecutionMode
            private set
        var defaultScriptAction: ScriptAction = defaultAction
            private set
        var verbose: Boolean = VERBOSE.default!!.toBoolean()
            private set
        var porcelain: Boolean = PRINT_RELATIVE_PATH_OF_SCRIPT.default!!.toBoolean()
            private set
        var flags: MutableList = mutableListOf()
            private set

        fun withName(name: String) = apply { this.name = name }
        fun withWorkingDirectory(workingDirectory: Path) = apply { this.workingDirectory = workingDirectory }
        fun withPath(path: Path) = apply { this.path = path }
        fun withIdentifierRegex(identifierRegex: Regex) = apply { this.identifierRegex = identifierRegex }
        fun withDoesCreateTagsFromFolder(doesCreateTagsFromFolder: Boolean) = apply { this.doesCreateTagsFromFolder = doesCreateTagsFromFolder }
        fun withOverrideExecutedScripts(overrideExecutedScripts: Boolean) = apply { this.overrideExecutedScripts = overrideExecutedScripts }
        fun withExecutionMode(executionMode: ExecutionMode) = apply { this.executionMode = executionMode }
        fun withDefaultScriptAction(defaultScriptAction: ScriptAction) = apply { this.defaultScriptAction = defaultScriptAction }
        fun withDriverConfig(driverConfig: DatamaintainDriverConfig) = apply { this.driverConfig = driverConfig }
        fun withFilenameRegex(filenameRegex: Regex) = apply { this.filenameRegex = filenameRegex }
        fun withVerbose(verbose: Boolean) = apply { this.verbose = verbose }
        fun withPorcelain(porcelain: Boolean) = apply { this.porcelain = porcelain }

        // Collection
        fun addWhitelistedTag(whitelistedTag: Tag) = apply { this.whitelistedTags.add(whitelistedTag) }
        fun addBlacklistedTag(blacklistedTag: Tag) = apply { this.blacklistedTags.add(blacklistedTag) }
        fun addTagToPlayAgain(tagToPlayAgain: Tag) = apply { this.tagsToPlayAgain.add(tagToPlayAgain) }
        fun addTagMatcher(tagsMatcher: TagMatcher) = apply { this.tagsMatchers.add(tagsMatcher) }
        fun addCheckRule(checkRule: String) = apply { this.checkRules.add(checkRule) }
        fun addFlag(flag: String) = apply { this.flags.add(flag) }

        fun build(): DatamaintainConfig {
            if (!::driverConfig.isInitialized) {
                throw DatamaintainBuilderMandatoryException("DatamaintainConfigBuilder", "driverConfig")
            }

            if (!::filenameRegex.isInitialized) {
                filenameRegex = defaultFilenameRegex(driverConfig)
            }

            return DatamaintainConfig(this)
        }
    }
}

interface ConfigKey {
    val key: String
    val default: String?

    companion object {
        fun overrideBySystemProperties(props: Properties, configKeys: List) {
            configKeys.forEach { configKey ->
                val property: String? = System.getProperty(configKey.key)
                property?.let { props.put(configKey.key, it) }
            }
        }
    }
}


enum class CoreConfigKey(override val key: String,
                         override val default: String? = null) : ConfigKey {
    // GLOBAL
    CONFIG_NAME("name"),
    WORKING_DIRECTORY_PATH("working.directory.path", System.getProperty("user.dir")),
    PARENT_CONFIG_PATH("parent.config.path"),
    DB_TYPE("db.type", "mongo"),
    VERBOSE("verbose", "false"),
    DEFAULT_SCRIPT_ACTION("default.script.action", "RUN"),
    PRINT_RELATIVE_PATH_OF_SCRIPT("porcelain", "false"),
    FLAGS("flags"),

    // SCAN
    SCAN_PATH("scan.path", "./scripts/"),
    SCAN_IDENTIFIER_REGEX("scan.identifier.regex", "(.*)"),
    CREATE_TAGS_FROM_FOLDER("scan.tags.createFromFolder", "false"),
    TAG("tag"),

    // FILTER
    SCAN_FILENAME_REGEX("filter.filename.regex"),
    TAGS_WHITELISTED("filter.tags.whitelisted"),
    TAGS_BLACKLISTED("filter.tags.blacklisted"),

    // PRUNER
    PRUNE_TAGS_TO_RUN_AGAIN("prune.tags.to.run.again"),
    PRUNE_OVERRIDE_UPDATED_SCRIPTS("prune.scripts.override.executed", "false"),

    // CHECKER
    CHECK_RULES("check.rules"),

    // EXECUTE
    EXECUTION_MODE("execute.mode", "NORMAL")
}

fun Properties.getProperty(configKey: ConfigKey): String =
        if (configKey.default != null) {
            getProperty(configKey, configKey.default!!)
        } else {
            getNullableProperty(configKey) ?: throw IllegalArgumentException("$configKey is mandatory")
        }

fun Properties.getNullableProperty(configKey: ConfigKey): String? = this.getProperty(configKey.key)

fun Properties.getStringPropertiesByPrefix(prefix: String): Set> {
    return this.entries
            .filter { (it.key as String).startsWith(prefix) }
            .map { Pair(it.key as String, it.value as String) }
            .toSet()
}

fun Properties.getProperty(configKey: ConfigKey, defaultValue: String): String =
        this.getProperty(configKey.key, defaultValue)






© 2015 - 2024 Weber Informatics LLC | Privacy Policy