[http-client-csharp]: Don't include custom required literal properties in public ctor (#4783)

This PR addresses an issue where required spec properties that were
literals and then customized to another literal type were being
incorrectly included in the public constructor.

fixes: https://github.com/microsoft/typespec/issues/4747
This commit is contained in:
Jorge Rangel 2024-10-17 17:40:53 -05:00 коммит произвёл GitHub
Родитель a689659f64
Коммит f1bfe9cdf9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 130 добавлений и 28 удалений

Просмотреть файл

@ -103,20 +103,10 @@ namespace Microsoft.Generator.CSharp.Providers
}
// handle customized enums - we need to pull the type information from the spec property
if (IsCustomizedEnumProperty(specProperty, customProperty.Type, out var specType))
{
customProperty.Type = new CSharpType(
customProperty.Type.Name,
customProperty.Type.Namespace,
customProperty.Type.IsValueType,
customProperty.Type.IsNullable,
customProperty.Type.DeclaringType,
customProperty.Type.Arguments,
customProperty.Type.IsPublic,
customProperty.Type.IsStruct,
customProperty.Type.BaseType,
TypeFactory.CreatePrimitiveCSharpTypeCore(specType));
}
customProperty.Type = EnsureEnum(specProperty, customProperty.Type);
// ensure literal types are correctly represented in the custom property using the info from the spec property
customProperty.Type = EnsureLiteral(specProperty, customProperty.Type);
}
return [..generatedProperties, ..customProperties];
@ -181,20 +171,10 @@ namespace Microsoft.Generator.CSharp.Providers
}
// handle customized enums - we need to pull the type information from the spec property
if (IsCustomizedEnumProperty(specProperty, customField.Type, out var specType))
{
customField.Type = new CSharpType(
customField.Type.Name,
customField.Type.Namespace,
customField.Type.IsValueType,
customField.Type.IsNullable,
customField.Type.DeclaringType,
customField.Type.Arguments,
customField.Type.IsPublic,
customField.Type.IsStruct,
customField.Type.BaseType,
TypeFactory.CreatePrimitiveCSharpTypeCore(specType));
}
customField.Type = EnsureEnum(specProperty, customField.Type);
// ensure literal types are correctly represented in the custom field using the info from the spec property
customField.Type = EnsureLiteral(specProperty, customField.Type);
}
return [..generatedFields, ..customFields];
@ -220,6 +200,35 @@ namespace Microsoft.Generator.CSharp.Providers
return false;
}
private static CSharpType EnsureLiteral(InputModelProperty? specProperty, CSharpType customType)
{
if (specProperty?.Type is InputLiteralType inputLiteral && (customType.IsFrameworkType || customType.IsEnum))
{
return CSharpType.FromLiteral(customType, inputLiteral.Value);
}
return customType;
}
private static CSharpType EnsureEnum(InputModelProperty? specProperty, CSharpType customType)
{
if (IsCustomizedEnumProperty(specProperty, customType, out var specType))
{
return new CSharpType(
customType.Name,
customType.Namespace,
customType.IsValueType,
customType.IsNullable,
customType.DeclaringType,
customType.Arguments,
customType.IsPublic,
customType.IsStruct,
customType.BaseType,
TypeFactory.CreatePrimitiveCSharpTypeCore(specType));
}
return customType;
}
private static InputPrimitiveType? GetEnumValueType(InputType? type)
{
return type switch

Просмотреть файл

@ -500,5 +500,75 @@ namespace Microsoft.Generator.CSharp.Tests.Providers.ModelProviders
Assert.IsTrue(ctors.Any(c => c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)));
Assert.IsTrue(ctors.Any(c => c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Internal)));
}
// Validates that if a required literal property is customized, then the default ctor
// does not include the custom property as a parameter.
[Test]
public async Task DoesNotIncludeReqCustomLiteralInDefaultCtor()
{
var enumType = InputFactory.Enum(
"originalEnum",
InputPrimitiveType.String,
values: [InputFactory.EnumMember.String("bar", "bar")]);
var plugin = await MockHelpers.LoadMockPluginAsync(
inputModelTypes: [
InputFactory.Model(
"mockInputModel",
usage: InputModelTypeUsage.Input,
properties:
[
InputFactory.Property("Prop1", InputFactory.Literal.Enum(enumType, "bar"), isRequired: true),
InputFactory.Property("Prop2", InputPrimitiveType.String, isRequired: true),
])
],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
var csharpGen = new CSharpGen();
await csharpGen.ExecuteAsync();
var ctors = plugin.Object.OutputLibrary.TypeProviders.Single(t => t.Name == "MockInputModel").Constructors;
Assert.AreEqual(2, ctors.Count);
var publicCtor = ctors.FirstOrDefault(c => c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public));
Assert.IsNotNull(publicCtor);
var ctorParams = publicCtor!.Signature.Parameters;
// should not have the custom required literal property
Assert.AreEqual(1, ctorParams.Count);
Assert.AreEqual("prop2", ctorParams[0].Name);
}
[Test]
public async Task DoesNotIncludeReqCustomFieldLiteralInDefaultCtor()
{
var plugin = await MockHelpers.LoadMockPluginAsync(
inputModelTypes: [
InputFactory.Model(
"mockInputModel",
usage: InputModelTypeUsage.Input,
properties:
[
InputFactory.Property("Prop1", InputFactory.Literal.String("bar"), isRequired: true),
InputFactory.Property("Prop2", InputPrimitiveType.String, isRequired: true),
])
],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
var csharpGen = new CSharpGen();
await csharpGen.ExecuteAsync();
var ctors = plugin.Object.OutputLibrary.TypeProviders.Single(t => t.Name == "MockInputModel").Constructors;
Assert.AreEqual(2, ctors.Count);
var publicCtor = ctors.FirstOrDefault(c => c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public));
Assert.IsNotNull(publicCtor);
var ctorParams = publicCtor!.Signature.Parameters;
// should not have the custom required literal property
Assert.AreEqual(1, ctorParams.Count);
Assert.AreEqual("prop2", ctorParams[0].Name);
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using Microsoft.Generator.CSharp.Customization;
namespace Sample.Models;
public partial class MockInputModel
{
[CodeGenMember("Prop1")]
private string _prop1 = "bar";
}

Просмотреть файл

@ -0,0 +1,14 @@
using Microsoft.Generator.CSharp.Customization;
namespace Sample.Models;
public partial class MockInputModel
{
[CodeGenMember("Prop1")]
public CustomEnum Prop1 { get; } = CustomEnum.Bar;
}
public enum CustomEnum
{
Bar
}