From c55437432cb814509603c43dbaa74cd4c7b688c6 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Mon, 1 Dec 2025 12:37:37 -0800 Subject: [PATCH 1/6] adding gradle task to allow parsing of v2 format --- .../ParseV2SupportedConfigurationsTask.kt | 196 ++++++++++++++++++ .../inversion/SupportedConfiguration.java | 44 ++++ 2 files changed, 240 insertions(+) create mode 100644 buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt create mode 100644 utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt new file mode 100644 index 00000000000..ba72a20877c --- /dev/null +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt @@ -0,0 +1,196 @@ +package datadog.gradle.plugin.config + +import org.gradle.api.DefaultTask +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.ObjectMapper +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import java.io.File +import java.io.FileInputStream +import java.io.PrintWriter +import javax.inject.Inject + +@CacheableTask +abstract class ParseV2SupportedConfigurationsTask @Inject constructor( + private val objects: ObjectFactory +) : DefaultTask() { + @InputFile + @PathSensitive(PathSensitivity.NONE) + val jsonFile = objects.fileProperty() + + @get:OutputDirectory + val destinationDirectory = objects.directoryProperty() + + @Input + val className = objects.property(String::class.java) + + @TaskAction + fun generate() { + val input = jsonFile.get().asFile + val outputDir = destinationDirectory.get().asFile + val finalClassName = className.get() + outputDir.mkdirs() + + // Read JSON (directly from the file, not classpath) + val mapper = ObjectMapper() + val fileData: Map = FileInputStream(input).use { inStream -> + mapper.readValue(inStream, object : TypeReference>() {}) + } + + // Fetch top-level keys of JSON file + @Suppress("UNCHECKED_CAST") + val supportedRaw = fileData["supportedConfigurations"] as Map>> + @Suppress("UNCHECKED_CAST") + val deprecated = (fileData["deprecations"] as? Map) ?: emptyMap() + + // Generate alias map and reverse alias mapping + val supported: Map> = supportedRaw.mapValues { (_, configList) -> + configList.map { configMap -> + SupportedConfiguration( + configMap["version"] as? String, + configMap["type"] as? String, + configMap["default"] as? String, + (configMap["aliases"] as? List) ?: emptyList(), + (configMap["propertyKeys"] as? List) ?: emptyList() + ) + } + } + + // Top-level mapping from config -> list of aliases. + // Note: This top-level alias mapping will be deprecated once Config Registry is mature enough to understand which version of a config a customer is using + val aliases: Map> = supported.mapValues { (_, configList) -> + configList.flatMap { it.aliases }.distinct() + } + + val aliasMapping = mutableMapOf() + for ((canonical, alist) in aliases) { + for (alias in alist) aliasMapping[alias] = canonical + } + + // Build the output .java path from the fully-qualified class name + val pkgName = finalClassName.substringBeforeLast('.', "") + val pkgPath = pkgName.replace('.', File.separatorChar) + val simpleName = finalClassName.substringAfterLast('.') + val pkgDir = if (pkgPath.isEmpty()) outputDir else File(outputDir, pkgPath).also { it.mkdirs() } + val generatedFile = File(pkgDir, "$simpleName.java").absolutePath + + // Call your existing generator (same signature as in your Java code) + generateJavaFile( + generatedFile, + simpleName, + pkgName, + supported, + aliases, + aliasMapping, + deprecated + ) + } + + private fun generateJavaFile( + outputPath: String, + className: String, + packageName: String, + supported: Map>, + aliases: Map>, + aliasMapping: Map, + deprecated: Map + ) { + val outFile = File(outputPath) + outFile.parentFile?.mkdirs() + + PrintWriter(outFile).use { out -> + // NOTE: adjust these if you want to match task's className + out.println("package $packageName;") + out.println() + out.println("import java.util.*;") + out.println() + out.println("public final class $className {") + out.println() + out.println(" public static final Map> SUPPORTED;") + out.println() + out.println(" public static final Map> ALIASES;") + out.println() + out.println(" public static final Map ALIAS_MAPPING;") + out.println() + out.println(" public static final Map DEPRECATED;") + out.println() + out.println(" static {") + out.println() + + // SUPPORTED + out.println(" Map> supportedMap = new HashMap<>();") + for ((key, configList) in supported.toSortedMap()) { + out.print(" supportedMap.put(\"${esc(key)}\", Collections.unmodifiableList(Arrays.asList(") + val configIter = configList.iterator() + while (configIter.hasNext()) { + val config = configIter.next() + out.print("new SupportedConfiguration(") + out.print("${escNullableString(config.version)}, ") + out.print("${escNullableString(config.type)}, ") + out.print("${escNullableString(config.default)}, ") + out.print("Arrays.asList(${quoteList(config.aliases)}), ") + out.print("Arrays.asList(${quoteList(config.propertyKeys)})") + out.print(")") + if (configIter.hasNext()) out.print(", ") + } + out.println(")));\n") + } + out.println(" SUPPORTED = Collections.unmodifiableMap(supportedMap);") + out.println() + + // ALIASES + out.println(" // Note: This top-level alias mapping will be deprecated once Config Registry is mature enough to understand which version of a config a customer is using") + out.println(" Map> aliasesMap = new HashMap<>();") + for ((canonical, list) in aliases.toSortedMap()) { + out.printf( + " aliasesMap.put(\"%s\", Collections.unmodifiableList(Arrays.asList(%s)));\n", + esc(canonical), + quoteList(list) + ) + } + out.println(" ALIASES = Collections.unmodifiableMap(aliasesMap);") + out.println() + + // ALIAS_MAPPING + out.println(" Map aliasMappingMap = new HashMap<>();") + for ((alias, target) in aliasMapping.toSortedMap()) { + out.printf(" aliasMappingMap.put(\"%s\", \"%s\");\n", esc(alias), esc(target)) + } + out.println(" ALIAS_MAPPING = Collections.unmodifiableMap(aliasMappingMap);") + out.println() + + // DEPRECATED + out.println(" Map deprecatedMap = new HashMap<>();") + for ((oldKey, note) in deprecated.toSortedMap()) { + out.printf(" deprecatedMap.put(\"%s\", \"%s\");\n", esc(oldKey), esc(note)) + } + out.println(" DEPRECATED = Collections.unmodifiableMap(deprecatedMap);") + out.println() + out.println(" }") + out.println("}") + } + } + + private fun quoteList(list: List): String = + list.joinToString(", ") { "\"${esc(it)}\"" } + + private fun esc(s: String): String = + s.replace("\\", "\\\\").replace("\"", "\\\"") + + private fun escNullableString(s: String?): String = + if (s == null) "null" else "\"${esc(s)}\"" +} + +data class SupportedConfiguration( + val version: String?, + val type: String?, + val default: String?, + val aliases: List, + val propertyKeys: List +) diff --git a/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java b/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java new file mode 100644 index 00000000000..50088bf9815 --- /dev/null +++ b/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java @@ -0,0 +1,44 @@ +package datadog.trace.config.inversion; + +import java.util.List; + +public class SupportedConfiguration { + private final String version; + private final String type; + private final String defaultValue; + private final List aliases; + private final List propertyKeys; + + public SupportedConfiguration( + String version, + String type, + String defaultValue, + List aliases, + List propertyKeys) { + this.version = version; + this.type = type; + this.defaultValue = defaultValue; + this.aliases = aliases; + this.propertyKeys = propertyKeys; + } + + public String getVersion() { + return version; + } + + public String getType() { + return type; + } + + public String getDefaultValue() { + return defaultValue; + } + + public List getAliases() { + return aliases; + } + + public List getPropertyKeys() { + return propertyKeys; + } +} From 1a1c744c2bccdd8bbea59241f63045f9154eaf10 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Mon, 1 Dec 2025 12:52:06 -0800 Subject: [PATCH 2/6] updating comments --- .../plugin/config/ParseV2SupportedConfigurationsTask.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt index ba72a20877c..223e8fcd941 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt @@ -49,7 +49,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( @Suppress("UNCHECKED_CAST") val deprecated = (fileData["deprecations"] as? Map) ?: emptyMap() - // Generate alias map and reverse alias mapping + // Parse supportedConfigurations key to into a V2 format val supported: Map> = supportedRaw.mapValues { (_, configList) -> configList.map { configMap -> SupportedConfiguration( @@ -62,7 +62,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( } } - // Top-level mapping from config -> list of aliases. + // Generate top-level mapping from config -> list of aliases and reverse alias mapping from alias -> top-level config // Note: This top-level alias mapping will be deprecated once Config Registry is mature enough to understand which version of a config a customer is using val aliases: Map> = supported.mapValues { (_, configList) -> configList.flatMap { it.aliases }.distinct() From da4da8e1dd72c23f57153186b37503873597dda8 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Wed, 3 Dec 2025 11:02:45 -0800 Subject: [PATCH 3/6] respond to PR comments --- .../config/ParseV2SupportedConfigurationsTask.kt | 1 - .../trace/config/inversion/SupportedConfiguration.java | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt index 223e8fcd941..a49def866b2 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt @@ -105,7 +105,6 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( outFile.parentFile?.mkdirs() PrintWriter(outFile).use { out -> - // NOTE: adjust these if you want to match task's className out.println("package $packageName;") out.println() out.println("import java.util.*;") diff --git a/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java b/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java index 50088bf9815..dfee6583d55 100644 --- a/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java +++ b/utils/config-utils/src/main/java/datadog/trace/config/inversion/SupportedConfiguration.java @@ -22,23 +22,23 @@ public SupportedConfiguration( this.propertyKeys = propertyKeys; } - public String getVersion() { + public String version() { return version; } - public String getType() { + public String type() { return type; } - public String getDefaultValue() { + public String defaultValue() { return defaultValue; } - public List getAliases() { + public List aliases() { return aliases; } - public List getPropertyKeys() { + public List propertyKeys() { return propertyKeys; } } From 3830eebb1236872e958d675a72bc8819656a311d Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Wed, 3 Dec 2025 11:51:57 -0800 Subject: [PATCH 4/6] adding reverse mapping for property key --- .../ParseV2SupportedConfigurationsTask.kt | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt index a49def866b2..d015fe66d6c 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt @@ -73,6 +73,12 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( for (alias in alist) aliasMapping[alias] = canonical } + val reversePropertyKeysMap: Map = supported.flatMap { (canonical, configList) -> + configList.flatMap { config -> + config.propertyKeys.map { propertyKey -> propertyKey to canonical } + } + }.toMap() + // Build the output .java path from the fully-qualified class name val pkgName = finalClassName.substringBeforeLast('.', "") val pkgPath = pkgName.replace('.', File.separatorChar) @@ -88,7 +94,8 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( supported, aliases, aliasMapping, - deprecated + deprecated, + reversePropertyKeysMap ) } @@ -99,7 +106,8 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( supported: Map>, aliases: Map>, aliasMapping: Map, - deprecated: Map + deprecated: Map, + reversePropertyKeysMap: Map ) { val outFile = File(outputPath) outFile.parentFile?.mkdirs() @@ -119,6 +127,8 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( out.println() out.println(" public static final Map DEPRECATED;") out.println() + out.println(" public static final Map REVERSE_PROPERTY_KEYS_MAP;") + out.println() out.println(" static {") out.println() @@ -171,6 +181,15 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( } out.println(" DEPRECATED = Collections.unmodifiableMap(deprecatedMap);") out.println() + + // REVERSE_PROPERTY_KEYS_MAP + out.println(" Map reversePropertyKeysMapping = new HashMap<>();") + for ((propertyKey, config) in reversePropertyKeysMap.toSortedMap()) { + out.printf(" reversePropertyKeysMapping.put(\"%s\", \"%s\");\n", esc(propertyKey), esc(config)) + } + out.println(" REVERSE_PROPERTY_KEYS_MAP = Collections.unmodifiableMap(reversePropertyKeysMapping);") + out.println() + out.println(" }") out.println("}") } From 0efb7ccfd5328fc3331a6f91bb4e0c59f44dae3a Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Mon, 8 Dec 2025 10:34:57 -0500 Subject: [PATCH 5/6] respond to PR comments --- .../config/ParseV2SupportedConfigurationsTask.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt index d015fe66d6c..760bacb50c7 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt @@ -1,6 +1,7 @@ package datadog.gradle.plugin.config import org.gradle.api.DefaultTask +import org.gradle.kotlin.dsl.property import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile @@ -28,7 +29,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( val destinationDirectory = objects.directoryProperty() @Input - val className = objects.property(String::class.java) + val className = objects.property() @TaskAction fun generate() { @@ -50,9 +51,9 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( val deprecated = (fileData["deprecations"] as? Map) ?: emptyMap() // Parse supportedConfigurations key to into a V2 format - val supported: Map> = supportedRaw.mapValues { (_, configList) -> + val supported: Map> = supportedRaw.mapValues { (_, configList) -> configList.map { configMap -> - SupportedConfiguration( + SupportedConfigurationObject( configMap["version"] as? String, configMap["type"] as? String, configMap["default"] as? String, @@ -103,7 +104,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( outputPath: String, className: String, packageName: String, - supported: Map>, + supported: Map>, aliases: Map>, aliasMapping: Map, deprecated: Map, @@ -205,7 +206,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( if (s == null) "null" else "\"${esc(s)}\"" } -data class SupportedConfiguration( +data class SupportedConfigurationObject( val version: String?, val type: String?, val default: String?, From 8b895b2536c04d09e81a268398590d62bc2cbc35 Mon Sep 17 00:00:00 2001 From: Matthew Li Date: Tue, 9 Dec 2025 11:49:01 -0500 Subject: [PATCH 6/6] pr comments --- .../config/ParseV2SupportedConfigurationsTask.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt index 760bacb50c7..56082bc95c8 100644 --- a/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt +++ b/buildSrc/src/main/kotlin/datadog/gradle/plugin/config/ParseV2SupportedConfigurationsTask.kt @@ -51,9 +51,9 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( val deprecated = (fileData["deprecations"] as? Map) ?: emptyMap() // Parse supportedConfigurations key to into a V2 format - val supported: Map> = supportedRaw.mapValues { (_, configList) -> + val supported: Map> = supportedRaw.mapValues { (_, configList) -> configList.map { configMap -> - SupportedConfigurationObject( + SupportedConfigurationItem( configMap["version"] as? String, configMap["type"] as? String, configMap["default"] as? String, @@ -104,7 +104,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( outputPath: String, className: String, packageName: String, - supported: Map>, + supported: Map>, aliases: Map>, aliasMapping: Map, deprecated: Map, @@ -149,7 +149,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( out.print(")") if (configIter.hasNext()) out.print(", ") } - out.println(")));\n") + out.println(")));") } out.println(" SUPPORTED = Collections.unmodifiableMap(supportedMap);") out.println() @@ -206,7 +206,7 @@ abstract class ParseV2SupportedConfigurationsTask @Inject constructor( if (s == null) "null" else "\"${esc(s)}\"" } -data class SupportedConfigurationObject( +private data class SupportedConfigurationItem( val version: String?, val type: String?, val default: String?,