Skip to content
37 changes: 36 additions & 1 deletion src/KubeOps.Transpiler/Crds.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -339,12 +340,46 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type
"System.Enum" => new V1JSONSchemaProps
{
Type = String,
EnumProperty = Enum.GetNames(type).Cast<object>().ToList(),
EnumProperty = GetEnumNames(context, type),
},
_ => throw InvalidType(type),
};
}

private static IList<object> GetEnumNames(this MetadataLoadContext context, Type type)
{
#if NET9_0_OR_GREATER
var attributeNameByFieldName = new Dictionary<string, string>();

foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
if (field.GetCustomAttributeData<JsonStringEnumMemberNameAttribute>() is { } jsonMemberNameAttribute &&
jsonMemberNameAttribute.GetCustomAttributeCtorArg<string>(context, 0) is { } jsonMemberNameAtributeName)
{
attributeNameByFieldName.Add(field.Name, jsonMemberNameAtributeName);
}
}

var enumNames = new List<object>();

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)
Expand Down
12 changes: 12 additions & 0 deletions src/KubeOps.Transpiler/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ public static class Utilities
.GetCustomAttributes(type)
.FirstOrDefault(a => a.AttributeType.Name == typeof(TAttribute).Name);

/// <summary>
/// Load a custom attribute from a read-only-reflected field.
/// </summary>
/// <param name="field">The field.</param>
/// <typeparam name="TAttribute">The type of the attribute to load.</typeparam>
/// <returns>The custom attribute data if an attribute is found.</returns>
public static CustomAttributeData? GetCustomAttributeData<TAttribute>(this FieldInfo field)
where TAttribute : Attribute
=> CustomAttributeData
.GetCustomAttributes(field)
.FirstOrDefault(a => a.AttributeType.Name == typeof(TAttribute).Name);

/// <summary>
/// Load a custom attribute from a read-only-reflected property.
/// </summary>
Expand Down
23 changes: 23 additions & 0 deletions test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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
{
Expand Down
Loading