Skip to content

Commit c9beb6d

Browse files
authored
feat: Add EnumMemberAttribute and .NET 9's JsonStringEnumMemberNameAttribute (#833)
Adds support for deriving the proper possible values for enum members from EnumMemberAttribute and JsonStringEnumMemberNameAttribute.
1 parent 2ea5719 commit c9beb6d

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

src/KubeOps.Transpiler/Crds.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections;
22
using System.Collections.ObjectModel;
33
using System.Reflection;
4+
using System.Runtime.Serialization;
45
using System.Text.Json.Serialization;
56

67
using k8s;
@@ -339,12 +340,46 @@ private static V1JSONSchemaProps Map(this MetadataLoadContext context, Type type
339340
"System.Enum" => new V1JSONSchemaProps
340341
{
341342
Type = String,
342-
EnumProperty = Enum.GetNames(type).Cast<object>().ToList(),
343+
EnumProperty = GetEnumNames(context, type),
343344
},
344345
_ => throw InvalidType(type),
345346
};
346347
}
347348

349+
private static IList<object> GetEnumNames(this MetadataLoadContext context, Type type)
350+
{
351+
#if NET9_0_OR_GREATER
352+
var attributeNameByFieldName = new Dictionary<string, string>();
353+
354+
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
355+
{
356+
if (field.GetCustomAttributeData<JsonStringEnumMemberNameAttribute>() is { } jsonMemberNameAttribute &&
357+
jsonMemberNameAttribute.GetCustomAttributeCtorArg<string>(context, 0) is { } jsonMemberNameAtributeName)
358+
{
359+
attributeNameByFieldName.Add(field.Name, jsonMemberNameAtributeName);
360+
}
361+
}
362+
363+
var enumNames = new List<object>();
364+
365+
foreach (var value in Enum.GetNames(type))
366+
{
367+
if (attributeNameByFieldName.TryGetValue(value, out var name))
368+
{
369+
enumNames.Add(name);
370+
}
371+
else
372+
{
373+
enumNames.Add(value);
374+
}
375+
}
376+
377+
return enumNames;
378+
#else
379+
return Enum.GetNames(type);
380+
#endif
381+
}
382+
348383
private static V1JSONSchemaProps MapObjectType(this MetadataLoadContext context, Type type)
349384
{
350385
switch (type.FullName)

src/KubeOps.Transpiler/Utilities.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ public static class Utilities
2020
.GetCustomAttributes(type)
2121
.FirstOrDefault(a => a.AttributeType.Name == typeof(TAttribute).Name);
2222

23+
/// <summary>
24+
/// Load a custom attribute from a read-only-reflected field.
25+
/// </summary>
26+
/// <param name="field">The field.</param>
27+
/// <typeparam name="TAttribute">The type of the attribute to load.</typeparam>
28+
/// <returns>The custom attribute data if an attribute is found.</returns>
29+
public static CustomAttributeData? GetCustomAttributeData<TAttribute>(this FieldInfo field)
30+
where TAttribute : Attribute
31+
=> CustomAttributeData
32+
.GetCustomAttributes(field)
33+
.FirstOrDefault(a => a.AttributeType.Name == typeof(TAttribute).Name);
34+
2335
/// <summary>
2436
/// Load a custom attribute from a read-only-reflected property.
2537
/// </summary>

test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class CrdsMlcTest(MlcProvider provider) : TranspilerTestBase(provider)
4040
[InlineData(typeof(SetIntEntity), "array", null, null)]
4141
[InlineData(typeof(InheritedEnumerableEntity), "array", null, null)]
4242
[InlineData(typeof(EnumEntity), "string", null, null)]
43+
[InlineData(typeof(NamedEnumEntity), "string", null, null)]
4344
[InlineData(typeof(NullableEnumEntity), "string", null, true)]
4445
[InlineData(typeof(DictionaryEntity), "object", null, null)]
4546
[InlineData(typeof(EnumerableKeyPairsEntity), "object", null, null)]
@@ -475,6 +476,14 @@ public void Should_Correctly_Use_Entity_Scope_Attribute()
475476
clusterCrd.Spec.Scope.Should().Be("Cluster");
476477
}
477478

479+
[Fact]
480+
public void Should_Correctly_Get_Enum_Value_From_JsonStringEnumMemberNameAttribute()
481+
{
482+
var crd = _mlc.Transpile(typeof(NamedEnumEntity));
483+
var specProperties = crd.Spec.Versions.First().Schema.OpenAPIV3Schema.Properties["property"];
484+
specProperties.EnumProperty.Should().BeEquivalentTo(["enumValue1", "enumValue2"]);
485+
}
486+
478487
#region Test Entity Classes
479488

480489
[KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")]
@@ -659,6 +668,20 @@ public enum TestSpecEnum
659668
}
660669
}
661670

671+
[KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")]
672+
private class NamedEnumEntity : CustomKubernetesEntity
673+
{
674+
public TestSpecEnum Property { get; set; }
675+
676+
public enum TestSpecEnum
677+
{
678+
[JsonStringEnumMemberName("enumValue1")]
679+
Value1,
680+
[JsonStringEnumMemberName("enumValue2")]
681+
Value2,
682+
}
683+
}
684+
662685
[KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")]
663686
private class SimpleDictionaryEntity : CustomKubernetesEntity
664687
{

0 commit comments

Comments
 (0)