Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate 'Result' enum and replace with 'MetricResult' due to conflict with Kotlin stdlib #808

Merged
merged 12 commits into from
Oct 29, 2024
5 changes: 4 additions & 1 deletion telemetry/jetbrains/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ dependencies {

tasks {
withType<KotlinCompile> {
compilerOptions.jvmTarget = JvmTarget.JVM_17
compilerOptions {
jvmTarget = JvmTarget.JVM_17
freeCompilerArgs.add("-Xcontext-receivers")
}
}

val validatePackagedSchema by registering {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import com.squareup.kotlinpoet.DOUBLE
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.MemberName
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.STRING
import com.squareup.kotlinpoet.TypeAliasSpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.TypeVariableName
import java.io.File
import java.time.Instant

Expand All @@ -28,6 +29,7 @@ val CONNECTION_SETTINGS = ClassName("software.aws.toolkits.core", "ConnectionSet

const val RESULT = "result"
const val SUCCESS = "success"
const val SANITIZED_RESULT_TYPE_FORMAT = "MetricResult"

fun String.filterInvalidCharacters() = this.replace(".", "")
fun String.toTypeFormat() = this.filterInvalidCharacters().split("_", "-").joinToString(separator = "") { it.capitalize() }
Expand Down Expand Up @@ -73,7 +75,16 @@ private fun FileSpec.Builder.generateTelemetryEnumTypes(items: List<TelemetryMet
}

private fun FileSpec.Builder.generateTelemetryEnumType(item: TelemetryMetricType): FileSpec.Builder {
val enum = TypeSpec.enumBuilder(item.name.toTypeFormat())
val clazz = item.name.toTypeFormat()
val existsInKotlinStdlib = try {
Class.forName("kotlin.$clazz")
true
} catch (_: ClassNotFoundException) {
false
}

val typeName = if (existsInKotlinStdlib) "Metric$clazz" else clazz
val enum = TypeSpec.enumBuilder(typeName)
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("value", String::class)
Expand Down Expand Up @@ -104,7 +115,7 @@ private fun FileSpec.Builder.generateTelemetryEnumType(item: TelemetryMetricType
val companion = TypeSpec.companionObjectBuilder()
.addFunction(
FunSpec.builder("from")
.returns(ClassName("", item.name.toTypeFormat()))
.returns(ClassName("", typeName))
.addParameter("type", String::class)
.addStatement("return values().firstOrNull·{·it.value·==·type·} ?:·$unknownType")
.build()
Expand All @@ -118,6 +129,24 @@ private fun FileSpec.Builder.generateTelemetryEnumType(item: TelemetryMetricType
return this
}

private fun FileSpec.Builder.generateDeprecatedOverloads(name: String): FileSpec.Builder {
val oldName = name.toTypeFormat()
val replacementType = TypeVariableName("Metric$oldName")
val replacementFqn = TypeVariableName("$PACKAGE_NAME.$replacementType")
val alias = TypeAliasSpec.builder(oldName, replacementType)
.addAnnotation(
AnnotationSpec.builder(Deprecated::class)
.addMember("message = %S", "Name conflicts with the Kotlin standard library")
.addMember("replaceWith = ReplaceWith(%S, %S)", replacementType, replacementFqn)
.build()
)
.build()

addTypeAlias(alias)

return this
}

private fun FileSpec.Builder.generateTelemetryObjects(schema: List<MetricSchema>): FileSpec.Builder {
schema.groupBy { it.namespace() }
.toSortedMap()
Expand All @@ -136,6 +165,7 @@ private fun FileSpec.Builder.generateNamespaces(namespace: String, metrics: List
return this
}

context(FileSpec.Builder)
private fun TypeSpec.Builder.generateRecordFunctions(metric: MetricSchema) {
// metric.name.split("_")[1] is guaranteed to work at this point because the schema requires the metric name to have at least 1 underscore
val functionName = metric.name.split("_")[1]
Expand All @@ -148,6 +178,7 @@ private fun TypeSpec.Builder.generateRecordFunctions(metric: MetricSchema) {
if (metric.metadata.none { it.type.name == RESULT }) {
return
}
generateDeprecatedOverloads(RESULT)

addFunction(buildProjectOverloadFunction(functionName, metric))
addFunction(buildConnectionSettingsOverloadFunction(functionName, metric))
Expand Down Expand Up @@ -198,7 +229,7 @@ private fun buildMetricParameters(metric: MetricSchema): List<ParameterSpec> {
private fun MetadataSchema.metadataToParameter(): ParameterSpec {
// Allowed values indicates an enum
val typeName = if (type.allowedValues != null) {
ClassName(PACKAGE_NAME, type.name.toTypeFormat())
ClassName(PACKAGE_NAME, type.name.toTypeFormat().replace("Result", SANITIZED_RESULT_TYPE_FORMAT))
} else {
type.type.kotlinType()
}.copy(nullable = required == false)
Expand All @@ -211,11 +242,11 @@ private fun MetadataSchema.metadataToParameter(): ParameterSpec {
}

private fun FunSpec.Builder.generateFunctionBody(metadataParameter: ParameterSpec, metric: MetricSchema): FunSpec.Builder {
val metricUnit = MemberName("software.amazon.awssdk.services.toolkittelemetry.model", "Unit")
val metricUnit = ClassName("software.amazon.awssdk.services.toolkittelemetry.model", "Unit")
beginControlFlow("%T.getInstance().record(${metadataParameter.name})", TELEMETRY_SERVICE)
beginControlFlow("datum(%S)", metric.name)
addStatement("createTime(createTime)")
addStatement("unit(%M.${(metric.unit ?: MetricUnit.NONE).name})", metricUnit)
addStatement("unit(%T.${(metric.unit ?: MetricUnit.NONE).name})", metricUnit)
addStatement("value(value)")
addStatement("passive(passive)")
metric.metadata.forEach {
Expand Down Expand Up @@ -269,7 +300,7 @@ private fun FunSpec.Builder.generateResultOverloadFunctionBody(functionName: Str
if (it.name != SUCCESS) {
it.name
} else {
"if($SUCCESS) Result.Succeeded else Result.Failed"
"if($SUCCESS) $SANITIZED_RESULT_TYPE_FORMAT.Succeeded else $SANITIZED_RESULT_TYPE_FORMAT.Failed"
}
})

Expand Down
5 changes: 3 additions & 2 deletions telemetry/jetbrains/src/test/resources/testOverrideOutput
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
/**
* The result of the operation
*/
public enum class Result(
public enum class MetricResult(
private val `value`: String,
) {
Succeeded("Succeeded"),
Expand All @@ -29,7 +29,8 @@ public enum class Result(
override fun toString(): String = value

public companion object {
public fun from(type: String): Result = values().firstOrNull { it.value == type } ?: Unknown
public fun from(type: String): MetricResult = values().firstOrNull { it.value == type }
?: Unknown
}
}

Expand Down
30 changes: 19 additions & 11 deletions telemetry/jetbrains/src/test/resources/testResultOutput
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package software.aws.toolkits.telemetry
import com.intellij.openapi.project.Project
import java.time.Instant
import kotlin.Boolean
import kotlin.Deprecated
import kotlin.Double
import kotlin.String
import kotlin.Suppress
Expand All @@ -19,7 +20,7 @@ import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
/**
* The result of the operation
*/
public enum class Result(
public enum class MetricResult(
private val `value`: String,
) {
Succeeded("Succeeded"),
Expand All @@ -34,17 +35,24 @@ public enum class Result(
override fun toString(): String = value

public companion object {
public fun from(type: String): Result = values().firstOrNull { it.value == type } ?: Unknown
public fun from(type: String): MetricResult = values().firstOrNull { it.value == type }
?: Unknown
}
}

@Deprecated(
message = "Name conflicts with the Kotlin standard library",
replaceWith = ReplaceWith("MetricResult", "software.aws.toolkits.telemetry.MetricResult"),
)
public typealias Result = MetricResult

public object MetadataTelemetry {
/**
* It has a result
*/
public fun hasResult(
project: Project?,
result: Result,
result: MetricResult,
passive: Boolean = false,
`value`: Double = 1.0,
createTime: Instant = Instant.now(),
Expand All @@ -65,7 +73,7 @@ public object MetadataTelemetry {
*/
public fun hasResult(
connectionSettings: ConnectionSettings? = null,
result: Result,
result: MetricResult,
passive: Boolean = false,
`value`: Double = 1.0,
createTime: Instant = Instant.now(),
Expand All @@ -86,7 +94,7 @@ public object MetadataTelemetry {
*/
public fun hasResult(
metadata: MetricEventMetadata,
result: Result,
result: MetricResult,
passive: Boolean = false,
`value`: Double = 1.0,
createTime: Instant = Instant.now(),
Expand All @@ -112,8 +120,8 @@ public object MetadataTelemetry {
`value`: Double = 1.0,
createTime: Instant = Instant.now(),
) {
hasResult(project, if(success) Result.Succeeded else Result.Failed, passive, value,
createTime)
hasResult(project, if(success) MetricResult.Succeeded else MetricResult.Failed, passive,
value, createTime)
}

/**
Expand All @@ -126,8 +134,8 @@ public object MetadataTelemetry {
`value`: Double = 1.0,
createTime: Instant = Instant.now(),
) {
hasResult(connectionSettings, if(success) Result.Succeeded else Result.Failed, passive,
value, createTime)
hasResult(connectionSettings, if(success) MetricResult.Succeeded else MetricResult.Failed,
passive, value, createTime)
}

/**
Expand All @@ -140,7 +148,7 @@ public object MetadataTelemetry {
`value`: Double = 1.0,
createTime: Instant = Instant.now(),
) {
hasResult(metadata, if(success) Result.Succeeded else Result.Failed, passive, value,
createTime)
hasResult(metadata, if(success) MetricResult.Succeeded else MetricResult.Failed, passive,
value, createTime)
}
}
Loading