diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs index 9cc71646129..afe3517713f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/SerializationCustomizationTests.cs @@ -8,6 +8,7 @@ using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; +using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Tests.Common; using NUnit.Framework; @@ -15,6 +16,47 @@ namespace Microsoft.TypeSpec.Generator.ClientModel.Tests.Providers.MrwSerializat { public class SerializationCustomizationTests { + [Test] + public async Task BackCompat_RenamedPropertiesKeepSerializationParameters( + [Values(false, true)] bool useLastContract) + { + var inputEnum = InputFactory.StringEnum("DestinationKind", [("VirtualNetwork", "virtualNetwork")]); + var inputModel = InputFactory.Model("MockInputModel", properties: [ + InputFactory.Property("isEnabled", InputPrimitiveType.Boolean, isRequired: true, serializedName: "enabled"), + InputFactory.Property("destinationType", inputEnum, isRequired: true, serializedName: "type"), + InputFactory.Property("enforcement", inputEnum) + ], + usage: InputModelTypeUsage.Input | InputModelTypeUsage.Output | InputModelTypeUsage.Json); + var mockGenerator = await MockHelpers.LoadMockGeneratorAsync( + inputEnums: () => [inputEnum], + inputModels: () => [inputModel], + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync("Custom"), + lastContractCompilation: useLastContract + ? async () => await Helpers.GetCompilationFromDirectoryAsync("Previous") + : null); + + var modelProvider = mockGenerator.Object.OutputLibrary.TypeProviders.OfType().Single(); + var serializationProvider = modelProvider.SerializationProviders.Single(t => t is MrwSerializationTypeDefinition); + modelProvider.EnsureBuilt(); + serializationProvider.EnsureBuilt(); + modelProvider.ProcessTypeForBackCompatibility(); + + var publicConstructor = modelProvider.Constructors.Single(c => + c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public)); + Assert.That(publicConstructor.Signature.Parameters.Select(p => p.Name), + Is.EqualTo(useLastContract ? new[] { "enabled", "type" } : new[] { "isEnabled", "destinationType" })); + Assert.That(publicConstructor.Signature.Parameters.Select(p => p.Property?.Name), + Is.EqualTo(new[] { "IsEnabled", "DestinationType" })); + Assert.That(modelProvider.FullConstructor.Signature.Parameters.Take(2).Select(p => p.Name), + Is.EqualTo(new[] { "isEnabled", "destinationType" })); + Assert.That(modelProvider.FullConstructor.Signature.Parameters.Take(2).Select(p => p.AsVariable().Declaration.RequestedName), + Is.EqualTo(new[] { "isEnabled", "destinationType" })); + + var writer = new TypeProviderWriter(new FilteredMethodsTypeProvider( + serializationProvider, name => name is "DeserializeMockInputModel" or "JsonModelWriteCore")); + Assert.AreEqual(Helpers.GetExpectedFromFile(), writer.Write().Content); + } + [Test] public async Task CanChangePropertyName() { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters(Custom)/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters(Custom)/MockInputModel.cs new file mode 100644 index 00000000000..c59abf94ed2 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters(Custom)/MockInputModel.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Sample.Models +{ + public partial class MockInputModel + { + public bool Enabled + { + get => IsEnabled; + set => IsEnabled = value; + } + + public DestinationKind Type + { + get => DestinationType; + set => DestinationType = value; + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters(Previous)/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters(Previous)/MockInputModel.cs new file mode 100644 index 00000000000..6ad9d6213bb --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters(Previous)/MockInputModel.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Sample.Models +{ + public partial class MockInputModel + { + public MockInputModel(bool enabled, DestinationKind type) + { + IsEnabled = enabled; + DestinationType = type; + } + + internal MockInputModel(bool enabled, DestinationKind type, DestinationKind? enforcement, IDictionary additionalBinaryDataProperties) + { + IsEnabled = enabled; + DestinationType = type; + Enforcement = enforcement; + } + + public bool IsEnabled { get; set; } + public bool Enabled + { + get => IsEnabled; + set => IsEnabled = value; + } + + public DestinationKind DestinationType { get; set; } + public DestinationKind? Enforcement { get; set; } + public DestinationKind Type + { + get => DestinationType; + set => DestinationType = value; + } + } + + public enum DestinationKind + { + VirtualNetwork + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters.cs new file mode 100644 index 00000000000..52c467a1645 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/MrwSerializationTypeDefinitions/TestData/SerializationCustomizationTests/BackCompat_RenamedPropertiesKeepSerializationParameters.cs @@ -0,0 +1,87 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Text.Json; +using Sample.Models; + +namespace Sample +{ + public partial class MockInputModel + { + protected virtual void JsonModelWriteCore(global::System.Text.Json.Utf8JsonWriter writer, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + string format = (options.Format == "W") ? ((global::System.ClientModel.Primitives.IPersistableModel)this).GetFormatFromOptions(options) : options.Format; + if ((format != "J")) + { + throw new global::System.FormatException($"The model {nameof(global::Sample.Models.MockInputModel)} does not support writing '{format}' format."); + } + writer.WritePropertyName("enabled"u8); + writer.WriteBooleanValue(IsEnabled); + writer.WritePropertyName("type"u8); + writer.WriteStringValue(DestinationType.ToSerialString()); + if (global::Sample.Optional.IsDefined(Enforcement)) + { + writer.WritePropertyName("enforcement"u8); + writer.WriteStringValue(Enforcement.Value.ToSerialString()); + } + if (((options.Format != "W") && (_additionalBinaryDataProperties != null))) + { + foreach (var item in _additionalBinaryDataProperties) + { + writer.WritePropertyName(item.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(item.Value); +#else + using (global::System.Text.Json.JsonDocument document = global::System.Text.Json.JsonDocument.Parse(item.Value)) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, document.RootElement); + } +#endif + } + } + } + + internal static global::Sample.Models.MockInputModel DeserializeMockInputModel(global::System.Text.Json.JsonElement element, global::System.ClientModel.Primitives.ModelReaderWriterOptions options) + { + if ((element.ValueKind == global::System.Text.Json.JsonValueKind.Null)) + { + return null; + } + bool isEnabled = default; + global::Sample.Models.DestinationKind destinationType = default; + global::Sample.Models.DestinationKind? enforcement = default; + global::System.Collections.Generic.IDictionary additionalBinaryDataProperties = new global::Sample.ChangeTrackingDictionary(); + foreach (var prop in element.EnumerateObject()) + { + if (prop.NameEquals("enabled"u8)) + { + isEnabled = prop.Value.GetBoolean(); + continue; + } + if (prop.NameEquals("type"u8)) + { + destinationType = prop.Value.GetString().ToDestinationKind(); + continue; + } + if (prop.NameEquals("enforcement"u8)) + { + if ((prop.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null)) + { + continue; + } + enforcement = prop.Value.GetString().ToDestinationKind(); + continue; + } + if ((options.Format != "W")) + { + additionalBinaryDataProperties.Add(prop.Name, prop.Value.GetUtf8Bytes()); + } + } + return new global::Sample.Models.MockInputModel(isEnabled, destinationType, enforcement, additionalBinaryDataProperties); + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ParameterProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ParameterProvider.cs index ce463bf2b7a..cf667670062 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ParameterProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ParameterProvider.cs @@ -195,7 +195,6 @@ private ParameterProvider BuildInputVariant() wireInfo: WireInfo, validation: Validation) { - _asVariable = _asVariable, SpreadSource = SpreadSource, InputParameter = InputParameter, IsExactName = IsExactName diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorNonExactParameterIsRestoredWithExactRename.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorNonExactParameterIsRestoredWithExactRename.cs index a1dd4a34ae5..6d027752c51 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorNonExactParameterIsRestoredWithExactRename.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorNonExactParameterIsRestoredWithExactRename.cs @@ -20,10 +20,10 @@ public MockInputModel(string newName, int oldCount) NewCount = oldCount; } - internal MockInputModel(string newName, int oldCount, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) + internal MockInputModel(string newName, int newCount, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) { new_name = newName; - NewCount = oldCount; + NewCount = newCount; _additionalBinaryDataProperties = additionalBinaryDataProperties; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterCasingAndRotationRestoredBySignatureMatch(Casing).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterCasingAndRotationRestoredBySignatureMatch(Casing).cs index 2ce0ded32f7..14ded0c7832 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterCasingAndRotationRestoredBySignatureMatch(Casing).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterCasingAndRotationRestoredBySignatureMatch(Casing).cs @@ -19,9 +19,9 @@ public CasingModel(string vmwareSiteId) VMwareSiteId = vmwareSiteId; } - internal CasingModel(string vmwareSiteId, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) + internal CasingModel(string vMwareSiteId, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) { - VMwareSiteId = vmwareSiteId; + VMwareSiteId = vMwareSiteId; _additionalBinaryDataProperties = additionalBinaryDataProperties; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterChainedRenameRestoredBySignatureMatch.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterChainedRenameRestoredBySignatureMatch.cs index 2722d0e2ba4..0c410e5e793 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterChainedRenameRestoredBySignatureMatch.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterChainedRenameRestoredBySignatureMatch.cs @@ -21,10 +21,10 @@ public MockInputModel(string name, string vmName) Name = vmName; } - internal MockInputModel(string name, string vmName, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) + internal MockInputModel(string skuName, string name, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) { - SkuName = name; - Name = vmName; + SkuName = skuName; + Name = name; _additionalBinaryDataProperties = additionalBinaryDataProperties; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterPreservesDateTimeSuffix.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterPreservesDateTimeSuffix.cs index 95a351ea79f..826f1eb825f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterPreservesDateTimeSuffix.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterPreservesDateTimeSuffix.cs @@ -16,9 +16,9 @@ public DateTimeModel(global::System.DateTimeOffset startTime) StartsOn = startTime; } - internal DateTimeModel(global::System.DateTimeOffset startTime, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) + internal DateTimeModel(global::System.DateTimeOffset startsOn, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) { - StartsOn = startTime; + StartsOn = startsOn; _additionalBinaryDataProperties = additionalBinaryDataProperties; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterRenameRestoredBySignatureMatch.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterRenameRestoredBySignatureMatch.cs index 3fc7eee2f25..0ce6b8353cd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterRenameRestoredBySignatureMatch.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorParameterRenameRestoredBySignatureMatch.cs @@ -21,10 +21,10 @@ public MockInputModel(string unchanged, string oldName) NewName = oldName; } - internal MockInputModel(string unchanged, string oldName, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) + internal MockInputModel(string unchanged, string newName, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) { Unchanged = unchanged; - NewName = oldName; + NewName = newName; _additionalBinaryDataProperties = additionalBinaryDataProperties; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorSameTypedNonExactParameterIsRestoredWithExactRename.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorSameTypedNonExactParameterIsRestoredWithExactRename.cs index 31dbe9fc984..7f1c2a0b6a8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorSameTypedNonExactParameterIsRestoredWithExactRename.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ModelProviders/TestData/ModelProviderTests/BackCompat_ConstructorSameTypedNonExactParameterIsRestoredWithExactRename.cs @@ -21,9 +21,9 @@ public MockInputModel(string oldUnchanged, string newName) new_name = newName; } - internal MockInputModel(string oldUnchanged, string newName, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) + internal MockInputModel(string unchanged, string newName, global::System.Collections.Generic.IDictionary additionalBinaryDataProperties) { - Unchanged = oldUnchanged; + Unchanged = unchanged; new_name = newName; _additionalBinaryDataProperties = additionalBinaryDataProperties; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ParameterProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ParameterProviderTests.cs index ceef4678495..49cacc89b94 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ParameterProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/ParameterProviderTests.cs @@ -215,7 +215,7 @@ public void IndependentlyCreatedParametersPreserveMetadata() } [Test] - public void PublicInputVariantPreservesVariableAndMetadata() + public void PublicInputVariantPreservesMetadataWithIndependentVariable() { MockHelpers.LoadMockGenerator(); var input = InputFactory.MethodParameter( @@ -226,13 +226,20 @@ public void PublicInputVariantPreservesVariableAndMetadata() Assert.AreEqual(parameter.Name, inputVariant.Name); Assert.IsTrue(inputVariant.Type.Equals(typeof(IEnumerable))); - Assert.AreSame(variable, inputVariant.AsVariable()); + Assert.AreNotSame(variable, inputVariant.AsVariable()); + Assert.AreEqual(inputVariant.Type, inputVariant.AsVariable().Type); + Assert.AreEqual(variable.Declaration.RequestedName, inputVariant.AsVariable().Declaration.RequestedName); Assert.AreSame(input, inputVariant.InputParameter); Assert.AreSame(parameter.WireInfo, inputVariant.WireInfo); Assert.AreEqual(parameter.IsExactName, inputVariant.IsExactName); Assert.AreEqual(parameter.DefaultValue, inputVariant.DefaultValue); Assert.AreEqual(parameter.Validation, inputVariant.Validation); Assert.AreSame(inputVariant, parameter.ToPublicInputParameter()); + + inputVariant.Update(name: "publishedIPv4Addresses"); + Assert.AreEqual("publishedIPv4Addresses", inputVariant.AsVariable().Declaration.RequestedName); + Assert.AreEqual("iPv4Addresses", parameter.Name); + Assert.AreEqual("iPv4Addresses", variable.Declaration.RequestedName); } private static IEnumerable ValueInputTypes() @@ -344,6 +351,23 @@ public void ToPublicInputParameterCopiesProperties() Assert.AreEqual(parameter.Type.InputType, publicParameter.Type); } + [Test] + public void ToPublicInputParameterDoesNotShareVariable() + { + var parameter = new ParameterProvider("isEnabled", $"Description", typeof(bool)); + var variable = parameter.AsVariable(); + var publicParameter = parameter.ToPublicInputParameter(); + var publicVariable = publicParameter.AsVariable(); + + publicParameter.Update(name: "enabled"); + + Assert.AreEqual("isEnabled", parameter.Name); + Assert.AreEqual("isEnabled", variable.Declaration.RequestedName); + Assert.AreEqual("enabled", publicVariable.Declaration.RequestedName); + Assert.AreSame(variable, parameter.AsVariable()); + Assert.AreSame(publicVariable, publicParameter.AsVariable()); + } + [Test] public void WithRefCopiesProperties() {