diff --git a/src/KubeOps.Transpiler/Crds.cs b/src/KubeOps.Transpiler/Crds.cs index 802c1d70..dece8b7f 100644 --- a/src/KubeOps.Transpiler/Crds.cs +++ b/src/KubeOps.Transpiler/Crds.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.ObjectModel; using System.Reflection; +using System.Runtime.Serialization; using System.Text.Json.Serialization; using k8s; @@ -339,12 +340,46 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type "System.Enum" => new V1JSONSchemaProps { Type = String, - EnumProperty = Enum.GetNames(type).Cast().ToList(), + EnumProperty = GetEnumNames(context, type), }, _ => throw InvalidType(type), }; } + private static IList GetEnumNames(this MetadataLoadContext context, Type type) + { +#if NET9_0_OR_GREATER + var attributeNameByFieldName = new Dictionary(); + + foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static)) + { + if (field.GetCustomAttributeData() is { } jsonMemberNameAttribute && + jsonMemberNameAttribute.GetCustomAttributeCtorArg(context, 0) is { } jsonMemberNameAtributeName) + { + attributeNameByFieldName.Add(field.Name, jsonMemberNameAtributeName); + } + } + + var enumNames = new List(); + + foreach (var value in Enum.GetNames(type)) + { + if (attributeNameByFieldName.TryGetValue(value, out var name)) + { + enumNames.Add(name); + } + else + { + enumNames.Add(value); + } + } + + return enumNames; +#else + return Enum.GetNames(type); +#endif + } + private static V1JSONSchemaProps MapObjectType(this MetadataLoadContext context, Type type) { switch (type.FullName) diff --git a/src/KubeOps.Transpiler/Utilities.cs b/src/KubeOps.Transpiler/Utilities.cs index 67c97ee2..8593a40b 100644 --- a/src/KubeOps.Transpiler/Utilities.cs +++ b/src/KubeOps.Transpiler/Utilities.cs @@ -20,6 +20,18 @@ public static class Utilities .GetCustomAttributes(type) .FirstOrDefault(a => a.AttributeType.Name == typeof(TAttribute).Name); + /// + /// Load a custom attribute from a read-only-reflected field. + /// + /// The field. + /// The type of the attribute to load. + /// The custom attribute data if an attribute is found. + public static CustomAttributeData? GetCustomAttributeData(this FieldInfo field) + where TAttribute : Attribute + => CustomAttributeData + .GetCustomAttributes(field) + .FirstOrDefault(a => a.AttributeType.Name == typeof(TAttribute).Name); + /// /// Load a custom attribute from a read-only-reflected property. /// diff --git a/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs b/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs index 0d9c1552..471329d6 100644 --- a/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs +++ b/test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs @@ -40,6 +40,7 @@ public class CrdsMlcTest(MlcProvider provider) : TranspilerTestBase(provider) [InlineData(typeof(SetIntEntity), "array", null, null)] [InlineData(typeof(InheritedEnumerableEntity), "array", null, null)] [InlineData(typeof(EnumEntity), "string", null, null)] + [InlineData(typeof(NamedEnumEntity), "string", null, null)] [InlineData(typeof(NullableEnumEntity), "string", null, true)] [InlineData(typeof(DictionaryEntity), "object", null, null)] [InlineData(typeof(EnumerableKeyPairsEntity), "object", null, null)] @@ -475,6 +476,14 @@ public void Should_Correctly_Use_Entity_Scope_Attribute() clusterCrd.Spec.Scope.Should().Be("Cluster"); } + [Fact] + public void Should_Correctly_Get_Enum_Value_From_JsonStringEnumMemberNameAttribute() + { + var crd = _mlc.Transpile(typeof(NamedEnumEntity)); + var specProperties = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["property"]; + specProperties.EnumProperty.Should().BeEquivalentTo(["enumValue1", "enumValue2"]); + } + #region Test Entity Classes [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")] @@ -659,6 +668,20 @@ public enum TestSpecEnum } } + [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")] + private class NamedEnumEntity : CustomKubernetesEntity + { + public TestSpecEnum Property { get; set; } + + public enum TestSpecEnum + { + [JsonStringEnumMemberName("enumValue1")] + Value1, + [JsonStringEnumMemberName("enumValue2")] + Value2, + } + } + [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")] private class SimpleDictionaryEntity : CustomKubernetesEntity {