Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Shared/RoslynUtils/WellKnownTypeData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public enum WellKnownType
System_AttributeUsageAttribute,
System_Text_Json_Serialization_JsonDerivedTypeAttribute,
System_Text_Json_Serialization_JsonIgnoreAttribute,
System_Text_Json_Serialization_JsonPropertyNameAttribute,
System_ComponentModel_DataAnnotations_DisplayAttribute,
System_ComponentModel_DataAnnotations_ValidationAttribute,
System_ComponentModel_DataAnnotations_RequiredAttribute,
Expand Down Expand Up @@ -243,6 +244,7 @@ public enum WellKnownType
"System.AttributeUsageAttribute",
"System.Text.Json.Serialization.JsonDerivedTypeAttribute",
"System.Text.Json.Serialization.JsonIgnoreAttribute",
"System.Text.Json.Serialization.JsonPropertyNameAttribute",
"System.ComponentModel.DataAnnotations.DisplayAttribute",
"System.ComponentModel.DataAnnotations.ValidationAttribute",
"System.ComponentModel.DataAnnotations.RequiredAttribute",
Expand Down
20 changes: 17 additions & 3 deletions src/Validation/gen/Extensions/ISymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Validation;

internal static class ISymbolExtensions
{
public static string GetDisplayName(this ISymbol property, INamedTypeSymbol displayAttribute)
public static string? GetDisplayName(this ISymbol property, INamedTypeSymbol displayAttribute)
{
var displayNameAttribute = property.GetAttributes()
.FirstOrDefault(attribute =>
Expand All @@ -19,19 +19,33 @@ attribute.AttributeClass is { } attributeClass &&

if (displayNameAttribute is not null)
{
// For the [Foo(Name = "bar")] case
if (!displayNameAttribute.NamedArguments.IsDefaultOrEmpty)
{
foreach (var namedArgument in displayNameAttribute.NamedArguments)
{
if (string.Equals(namedArgument.Key, "Name", StringComparison.Ordinal))
{
return namedArgument.Value.Value?.ToString() ?? property.Name;
if (namedArgument.Value.Value?.ToString() is { } name)
{
return name;
}
}
}
}

// For the [Foo("bar")] case
if (displayNameAttribute.ConstructorArguments.Length is 1)
{
var arg = displayNameAttribute.ConstructorArguments[0];
if (arg.Kind == TypedConstantKind.Primitive && arg.Value is string name)
{
return name;
}
}
}

return property.Name;
return null;
}

public static bool IsEqualityContract(this IPropertySymbol prop, WellKnownTypes wellKnownTypes) =>
Expand Down
19 changes: 15 additions & 4 deletions src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Immutable;
using System.Linq;
using Microsoft.AspNetCore.Analyzers.Infrastructure;
using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
using Microsoft.AspNetCore.App.Analyzers.Infrastructure;
using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -195,12 +194,19 @@ internal ImmutableArray<ValidatableProperty> ExtractValidatableMembers(ITypeSymb
ref validatableTypes,
ref visitedTypes);

var displayName =
parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ??
parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ??
correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ??
correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ??
parameter.Name ??
correspondingProperty.Name;

members.Add(new ValidatableProperty(
ContainingType: correspondingProperty.ContainingType,
Type: correspondingProperty.Type,
Name: correspondingProperty.Name,
DisplayName: parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ??
correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)),
DisplayName: displayName,
Attributes: []));
}
}
Expand Down Expand Up @@ -252,11 +258,16 @@ internal ImmutableArray<ValidatableProperty> ExtractValidatableMembers(ITypeSymb
continue;
}

var displayName =
member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ??
member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ??
member.Name;

members.Add(new ValidatableProperty(
ContainingType: member.ContainingType,
Type: member.Type,
Name: member.Name,
DisplayName: member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)),
DisplayName: displayName,
Attributes: attributes));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public async Task CanValidateComplexTypesWithJsonIgnore()

app.MapPost("/complex-type-with-json-ignore", (ComplexTypeWithJsonIgnore complexType) => Results.Ok("Passed"!));
app.MapPost("/record-type-with-json-ignore", (RecordTypeWithJsonIgnore recordType) => Results.Ok("Passed"!));
app.MapPost("/complex-type-with-json-property-name", (ComplexTypeWithJsonPropertyName complexType) => Results.Ok("Passed"!));

app.Run();

Expand Down Expand Up @@ -76,6 +77,21 @@ public record CircularReferenceRecord

public string Name { get; set; } = "test";
}

public class ComplexTypeWithJsonPropertyName
{
[Range(10, 100)]
public int DefaultPropertyName { get; set; } = 10;

[Range(10, 100)]
[JsonPropertyName("custom-property-name")]
public int CustomJsonPropertyName { get; set; } = 20;

[Display(Name = "display-name")]
[Range(10, 100)]
[JsonPropertyName("custom-property-name-with-display-name")]
public int CustomJsonPropertyNameWithDisplayName { get; set; } = 30;
}
""";
await Verify(source, out var compilation);
await VerifyEndpoint(compilation, "/complex-type-with-json-ignore", async (endpoint, serviceProvider) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System.
);
return true;
}
if (type == typeof(global::ComplexTypeWithJsonPropertyName))
{
validatableInfo = new GeneratedValidatableTypeInfo(
type: typeof(global::ComplexTypeWithJsonPropertyName),
members: [
new GeneratedValidatablePropertyInfo(
containingType: typeof(global::ComplexTypeWithJsonPropertyName),
propertyType: typeof(int),
name: "DefaultPropertyName",
displayName: "DefaultPropertyName"
),
new GeneratedValidatablePropertyInfo(
containingType: typeof(global::ComplexTypeWithJsonPropertyName),
propertyType: typeof(int),
name: "CustomJsonPropertyName",
displayName: "custom-property-name"
),
new GeneratedValidatablePropertyInfo(
containingType: typeof(global::ComplexTypeWithJsonPropertyName),
propertyType: typeof(int),
name: "CustomJsonPropertyNameWithDisplayName",
displayName: "display-name"
),
]
);
return true;
}

return false;
}
Expand Down
Loading