diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/CanonicalTypeProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/CanonicalTypeProvider.cs index c9beea0b31..2989bb1d5b 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/CanonicalTypeProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/CanonicalTypeProvider.cs @@ -103,20 +103,10 @@ protected override PropertyProvider[] BuildProperties() } // 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 @@ protected override FieldProvider[] BuildFields() } // 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 @@ private static bool IsCustomizedEnumProperty( 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 diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs index 39dca8e7b7..d57c375c7e 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs @@ -500,5 +500,75 @@ public async Task DoesNotReplaceDefaultConstructorIfNotCustomized() 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); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/DoesNotIncludeReqCustomFieldLiteralInDefaultCtor/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/DoesNotIncludeReqCustomFieldLiteralInDefaultCtor/MockInputModel.cs new file mode 100644 index 0000000000..6ae452e803 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/DoesNotIncludeReqCustomFieldLiteralInDefaultCtor/MockInputModel.cs @@ -0,0 +1,9 @@ +using Microsoft.Generator.CSharp.Customization; + +namespace Sample.Models; + +public partial class MockInputModel +{ + [CodeGenMember("Prop1")] + private string _prop1 = "bar"; +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/DoesNotIncludeReqCustomLiteralInDefaultCtor/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/DoesNotIncludeReqCustomLiteralInDefaultCtor/MockInputModel.cs new file mode 100644 index 0000000000..71ee6883f9 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/DoesNotIncludeReqCustomLiteralInDefaultCtor/MockInputModel.cs @@ -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 +}