ai.digital.integration.server.common.util.YamlFileUtil.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of integration-server-gradle-plugin Show documentation
Show all versions of integration-server-gradle-plugin Show documentation
The easy way to get custom setup for Deploy up and running
package ai.digital.integration.server.common.util
import com.fasterxml.jackson.core.TreeNode
import com.fasterxml.jackson.databind.MappingIterator
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator
import java.io.File
import java.io.InputStream
import java.net.URL
import java.util.concurrent.TimeUnit
class YamlFileUtil {
companion object {
private val yamlFactory: YAMLFactory = YAMLFactory.builder().build()
private fun createMapper(minimizeQuotes: Boolean): ObjectMapper {
val factory = YAMLFactory.builder()
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
if (minimizeQuotes) {
factory.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
}
return ObjectMapper(factory.build())
}
// Creates different key chain, depends on what is the first key, contains a dot or not for a first key.
private fun calcKeyChain(objectMap: MutableMap, tokens: List): Array {
return if (tokens.size == 1) {
emptyArray()
} else if (objectMap[tokens[0] + "." + tokens[1]] != null) {
val keyChain = arrayOf(tokens[0] + "." + tokens[1])
keyChain.plus(tokens.drop(2).dropLast(1))
} else {
val keyChain = arrayOf(tokens[0])
keyChain.plus(tokens.drop(1).dropLast(1))
}
}
@Suppress("UNCHECKED_CAST")
private fun getKeyParentAndLastToken(
objectMap: MutableMap,
key: String
): Pair, String> {
val tokens: List = key.split(".")
val keyChain = calcKeyChain(objectMap, tokens)
var current: MutableMap = objectMap
keyChain.forEach { keyItem ->
val pair = if (keyItem.contains("[")) {
Pair(keyItem.substring(0, keyItem.indexOf("[")), true)
} else {
Pair(keyItem, false)
}
val pureKeyItem = pair.first
val isArray = pair.second
val value: Any? = current[pureKeyItem]
if (value == null) {
current[pureKeyItem] =
if (isArray) ArrayList>() else LinkedHashMap()
}
current = if (isArray) {
val keyItemInd = keyItem.substring(keyItem.indexOf("[") + 1, keyItem.indexOf("]")).toInt()
val array: ArrayList> =
current[pureKeyItem] as ArrayList>
while (array.size <= keyItemInd) {
array.add(LinkedHashMap())
}
array[keyItemInd]
} else {
current[pureKeyItem] as MutableMap
}
}
return Pair(current, tokens.last())
}
private fun readKey(objectMap: MutableMap, key: String): Any? {
val pair = getKeyParentAndLastToken(objectMap, key)
return pair.first[pair.second]
}
private fun addPair(initial: MutableMap, newPairs: MutableMap) {
newPairs.forEach { pair ->
val p = getKeyParentAndLastToken(initial, pair.key)
p.first[p.second] = pair.value
}
}
@Suppress("UNCHECKED_CAST")
private fun mingleValues(source: URL, pairs: MutableMap, destinationFile: File, minimizeQuotes: Boolean = true) {
val mapper = createMapper(minimizeQuotes)
val yamlParser = yamlFactory.createParser(source)
val aggregatedValue: MappingIterator> =
mapper.readValues(yamlParser, MutableMap::class.java) as MappingIterator>
val itemContents = mutableListOf()
aggregatedValue.forEach { item ->
addPair(item as MutableMap, pairs)
itemContents.add(mapper.writeValueAsString(item))
}
val fileContent = if (itemContents.size > 1) {
itemContents.joinToString(prefix = "---\n", separator = "---\n")
} else {
itemContents[0]
}.replace("file: ", "file: !file ")
destinationFile.writeText(
fileContent
)
}
private fun mingleValues(pairs: MutableMap, destinationFile: File, minimizeQuotes: Boolean = true) {
val mapper = createMapper(minimizeQuotes)
val aggregatedValue = LinkedHashMap()
addPair(aggregatedValue, pairs)
mapper.writeValue(destinationFile, aggregatedValue)
}
private fun mingleValuesWithYq(pairs: MutableMap, destinationFile: File) {
pairs
.map { entry ->
val key = if (entry.key.startsWith("(")) {
entry.key
} else {
"." + entry.key
}
Pair(key, entry.value)
}
.forEach { pair ->
if (pair.second is Collection<*>) {
val values = pair.second as Collection<*>
val value = values.joinToString(",", "[", "]") { entry -> getYqSimpleValue(entry) }
val process = ProcessBuilder(arrayListOf("yq", "-i", "${pair.first} = $value", destinationFile.absolutePath))
.inheritIO()
.start()
checkProcess(process)
} else if (pair.second is Array<*>) {
val values = pair.second as Array<*>
val value = values.joinToString(",", "[", "]") { entry -> getYqSimpleValue(entry) }
val process = ProcessBuilder(arrayListOf("yq", "-i", "${pair.first} = $value", destinationFile.absolutePath))
.inheritIO()
.start()
checkProcess(process)
} else if (pair.second is Map<*, *>) {
val map = pair.second as Map<*, *>
map.forEach{entry ->
val value = getYqSimpleValue(entry.value)
val process = ProcessBuilder(arrayListOf("yq", "-i", "${pair.first}.\"${entry.key}\" = $value", destinationFile.absolutePath))
.inheritIO()
.start()
checkProcess(process)
}
} else {
val value = getYqSimpleValue(pair.second)
val process = ProcessBuilder(arrayListOf("yq", "-i", "${pair.first} = $value", destinationFile.absolutePath))
.inheritIO()
.start()
checkProcess(process)
}
}
}
private fun checkProcess(process: Process) {
val result = process.waitFor(60, TimeUnit.SECONDS)
if (!result || process.exitValue() != 0) {
throw IllegalArgumentException("Failed to run yq with ${process.exitValue()}")
}
}
private fun getYqSimpleValue(simpleValue: Any?): String {
return if (simpleValue is Number || simpleValue is Boolean) {
simpleValue.toString()
} else if (simpleValue == null) {
"null"
} else if (simpleValue is String) {
"\"${simpleValue}\""
} else {
throw IllegalArgumentException("not supported value $simpleValue")
}
}
/**
* Creates the file if it doesn't exist
* If new pair - creates it, if it was existed - overrides it.
*
* @param sourceFle
* @param pairs
* @param destinationFile
*/
fun overlayFileWithJackson(sourceFile: File, pairs: MutableMap, destinationFile: File = sourceFile, minimizeQuotes: Boolean = true) {
if (!sourceFile.exists()) {
sourceFile.createNewFile()
mingleValues(pairs, destinationFile, minimizeQuotes)
} else {
mingleValues(sourceFile.toURI().toURL(), pairs, destinationFile, minimizeQuotes)
}
}
fun overlayFile(sourceFile: File, pairs: MutableMap, destinationFile: File = sourceFile, minimizeQuotes: Boolean = true) {
if (!sourceFile.exists()) {
sourceFile.createNewFile()
}
if (sourceFile != destinationFile) {
sourceFile.copyTo(destinationFile, overwrite = true)
}
mingleValuesWithYq(pairs, destinationFile)
}
@Suppress("UNCHECKED_CAST")
fun readFileKey(file: File, key: String, minimizeQuotes: Boolean = true): Any? {
val mapper = createMapper(minimizeQuotes)
return readKey(mapper.readValue(file, MutableMap::class.java) as MutableMap, key)
}
fun readTree(resource: InputStream, minimizeQuotes: Boolean = true): TreeNode {
val mapper = createMapper(minimizeQuotes)
return mapper.readTree(resource)
}
fun writeFileValue(sourceFle: File, value: Any, minimizeQuotes: Boolean = true) {
val mapper = createMapper(minimizeQuotes)
if (!sourceFle.exists()) {
File(sourceFle.parent).mkdirs()
sourceFle.createNewFile()
}
mapper.writeValue(sourceFle, value)
}
}
}