From fbd34d12e5a1867c7ff37dd979b16945c9e7f469 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 14:47:42 +0000 Subject: [PATCH 01/10] Initial plan From d250f4796df65cae469606b9e847ff52916db2da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:09:24 +0000 Subject: [PATCH 02/10] feat: only use full namespace for api version enum when there are collisions Use last namespace segment for api version enum names in multi-service scenarios by default. Fall back to full namespace only when two or more services share the same last namespace segment, avoiding unnecessary prefix noise while still guaranteeing uniqueness. Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ClientOptionsProvider.cs | 42 ++++++++++++-- .../ClientProviders/ClientProviderTests.cs | 8 +-- ...ceClient_GeneratesExpectedClientOptions.cs | 38 ++++++------ ...Services_GeneratesExpectedClientOptions.cs | 58 +++++++++---------- ...edClient_GeneratesExpectedClientOptions.cs | 38 ++++++------ ...Services_GeneratesExpectedClientOptions.cs | 58 +++++++++---------- .../src/Providers/ApiVersionEnumProvider.cs | 20 ++++++- .../ApiVersionEnumProviderTests.cs | 45 +++++++++++++- 8 files changed, 196 insertions(+), 111 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index 975c2492146..a3a74991fa8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -131,13 +131,39 @@ private static bool UseSingletonInstance(InputClient inputClient) } var properties = new Dictionary(_serviceVersionsEnums.Count); + + // Precompute which last namespace segments have collisions + HashSet? collidingSegments = null; + if (_inputClient.IsMultiServiceClient) + { + var segmentCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var (inputEnum, _) in _serviceVersionsEnums) + { + var segment = GetLastNamespaceSegment(inputEnum.Namespace); + segmentCounts[segment] = segmentCounts.TryGetValue(segment, out var count) ? count + 1 : 1; + } + collidingSegments = new HashSet( + segmentCounts.Where(kv => kv.Value > 1).Select(kv => kv.Key), + StringComparer.OrdinalIgnoreCase); + } + foreach (var (inputEnum, enumProvider) in _serviceVersionsEnums) { - // For multi-service clients, use the full namespace to guarantee uniqueness - // (the last segment alone can collide when services share a namespace). - var versionPropertyName = _inputClient.IsMultiServiceClient - ? $"{inputEnum.Namespace.ToIdentifierName()}{ApiVersionSuffix}" - : VersionSuffix; + string versionPropertyName; + if (_inputClient.IsMultiServiceClient) + { + var ns = inputEnum.Namespace; + var lastSegment = GetLastNamespaceSegment(ns); + // Only use the full namespace when the last segment collides + // with another service's last segment. + versionPropertyName = collidingSegments!.Contains(lastSegment) + ? $"{ns.ToIdentifierName()}{ApiVersionSuffix}" + : $"{lastSegment.ToIdentifierName()}{ApiVersionSuffix}"; + } + else + { + versionPropertyName = VersionSuffix; + } var versionProperty = new PropertyProvider( null, @@ -151,6 +177,12 @@ private static bool UseSingletonInstance(InputClient inputClient) return properties; } + + private static string GetLastNamespaceSegment(string ns) + { + int lastDot = ns.LastIndexOf('.'); + return lastDot >= 0 ? ns.Substring(lastDot + 1) : ns; + } private IReadOnlyDictionary? LatestVersionsFields => field ??= BuildLatestVersionsFields(); private Dictionary? BuildLatestVersionsFields() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs index 127e1420d06..5276928178a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs @@ -3678,12 +3678,12 @@ public void GetApiVersionFieldForService_MultiService_ReturnsMatchingField() // Should return the matching field for ServiceA var fieldA = clientProvider!.GetApiVersionFieldForService("Sample.ServiceA"); Assert.IsNotNull(fieldA); - Assert.AreEqual("_sampleServiceAApiVersion", fieldA!.Name); + Assert.AreEqual("_serviceAApiVersion", fieldA!.Name); // Should return the matching field for ServiceB var fieldB = clientProvider.GetApiVersionFieldForService("Sample.ServiceB"); Assert.IsNotNull(fieldB); - Assert.AreEqual("_sampleServiceBApiVersion", fieldB!.Name); + Assert.AreEqual("_serviceBApiVersion", fieldB!.Name); } [Test] @@ -3814,11 +3814,11 @@ public void GetApiVersionFieldForService_MultiService_CaseInsensitiveMatch() // Should match case-insensitively var fieldLowerCase = clientProvider!.GetApiVersionFieldForService("sample.serviceA"); Assert.IsNotNull(fieldLowerCase); - Assert.AreEqual("_sampleServiceAApiVersion", fieldLowerCase!.Name); + Assert.AreEqual("_serviceAApiVersion", fieldLowerCase!.Name); var fieldUpperCase = clientProvider.GetApiVersionFieldForService("SAMPLE.SERVICEa"); Assert.IsNotNull(fieldUpperCase); - Assert.AreEqual("_sampleServiceAApiVersion", fieldUpperCase!.Name); + Assert.AreEqual("_serviceAApiVersion", fieldUpperCase!.Name); } [Test] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs index 30c5361bdaf..1eb960fa71f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs @@ -11,21 +11,21 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.SampleServiceAVersion LatestSampleServiceAVersion = global::Sample.TestClientOptions.SampleServiceAVersion.V2_0; - private const global::Sample.TestClientOptions.SampleServiceBVersion LatestSampleServiceBVersion = global::Sample.TestClientOptions.SampleServiceBVersion.V4_0; + private const global::Sample.TestClientOptions.ServiceAVersion LatestServiceAVersion = global::Sample.TestClientOptions.ServiceAVersion.V2_0; + private const global::Sample.TestClientOptions.ServiceBVersion LatestServiceBVersion = global::Sample.TestClientOptions.ServiceBVersion.V4_0; - public TestClientOptions(global::Sample.TestClientOptions.SampleServiceAVersion sampleServiceAVersion = LatestSampleServiceAVersion, global::Sample.TestClientOptions.SampleServiceBVersion sampleServiceBVersion = LatestSampleServiceBVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) { - SampleServiceAApiVersion = sampleServiceAVersion switch + ServiceAApiVersion = serviceAVersion switch { - global::Sample.TestClientOptions.SampleServiceAVersion.V1_0 => "1.0", - global::Sample.TestClientOptions.SampleServiceAVersion.V2_0 => "2.0", + global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", + global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - SampleServiceBApiVersion = sampleServiceBVersion switch + ServiceBApiVersion = serviceBVersion switch { - global::Sample.TestClientOptions.SampleServiceBVersion.V3_0 => "3.0", - global::Sample.TestClientOptions.SampleServiceBVersion.V4_0 => "4.0", + global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", + global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", _ => throw new global::System.NotSupportedException() }; } @@ -33,33 +33,33 @@ public TestClientOptions(global::Sample.TestClientOptions.SampleServiceAVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleServiceAApiVersion = "2.0"; - SampleServiceBApiVersion = "4.0"; + ServiceAApiVersion = "2.0"; + ServiceBApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleServiceAApiVersion"] is string sampleServiceAApiVersion)) + if ((section["ServiceAApiVersion"] is string serviceAApiVersion)) { - this.SampleServiceAApiVersion = sampleServiceAApiVersion; + this.ServiceAApiVersion = serviceAApiVersion; } - if ((section["SampleServiceBApiVersion"] is string sampleServiceBApiVersion)) + if ((section["ServiceBApiVersion"] is string serviceBApiVersion)) { - this.SampleServiceBApiVersion = sampleServiceBApiVersion; + this.ServiceBApiVersion = serviceBApiVersion; } } - internal string SampleServiceAApiVersion { get; } + internal string ServiceAApiVersion { get; } - internal string SampleServiceBApiVersion { get; } + internal string ServiceBApiVersion { get; } - public enum SampleServiceAVersion + public enum ServiceAVersion { V1_0 = 1, V2_0 = 2 } - public enum SampleServiceBVersion + public enum ServiceBVersion { V3_0 = 1, V4_0 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 740d3a43b3a..7dd1ff9bf30 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,29 +11,29 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.SampleComputeVersion LatestSampleComputeVersion = global::Sample.TestClientOptions.SampleComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.SampleKeyVaultVersion LatestSampleKeyVaultVersion = global::Sample.TestClientOptions.SampleKeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.SampleStorageVersion LatestSampleStorageVersion = global::Sample.TestClientOptions.SampleStorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ComputeVersion.V2024_07_01; + private const global::Sample.TestClientOptions.KeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.KeyVaultVersion.V7_5; + private const global::Sample.TestClientOptions.StorageVersion LatestStorageVersion = global::Sample.TestClientOptions.StorageVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.SampleKeyVaultVersion sampleKeyVaultVersion = LatestSampleKeyVaultVersion, global::Sample.TestClientOptions.SampleStorageVersion sampleStorageVersion = LatestSampleStorageVersion, global::Sample.TestClientOptions.SampleComputeVersion sampleComputeVersion = LatestSampleComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.KeyVaultVersion keyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.StorageVersion storageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ComputeVersion computeVersion = LatestComputeVersion) { - SampleKeyVaultApiVersion = sampleKeyVaultVersion switch + KeyVaultApiVersion = keyVaultVersion switch { - global::Sample.TestClientOptions.SampleKeyVaultVersion.V7_4 => "7.4", - global::Sample.TestClientOptions.SampleKeyVaultVersion.V7_5 => "7.5", + global::Sample.TestClientOptions.KeyVaultVersion.V7_4 => "7.4", + global::Sample.TestClientOptions.KeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - SampleStorageApiVersion = sampleStorageVersion switch + StorageApiVersion = storageVersion switch { - global::Sample.TestClientOptions.SampleStorageVersion.V2023_01_01 => "2023-01-01", - global::Sample.TestClientOptions.SampleStorageVersion.V2024_01_01 => "2024-01-01", + global::Sample.TestClientOptions.StorageVersion.V2023_01_01 => "2023-01-01", + global::Sample.TestClientOptions.StorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - SampleComputeApiVersion = sampleComputeVersion switch + ComputeApiVersion = computeVersion switch { - global::Sample.TestClientOptions.SampleComputeVersion.V2023_07_01 => "2023-07-01", - global::Sample.TestClientOptions.SampleComputeVersion.V2024_03_01 => "2024-03-01", - global::Sample.TestClientOptions.SampleComputeVersion.V2024_07_01 => "2024-07-01", + global::Sample.TestClientOptions.ComputeVersion.V2023_07_01 => "2023-07-01", + global::Sample.TestClientOptions.ComputeVersion.V2024_03_01 => "2024-03-01", + global::Sample.TestClientOptions.ComputeVersion.V2024_07_01 => "2024-07-01", _ => throw new global::System.NotSupportedException() }; } @@ -41,47 +41,47 @@ public TestClientOptions(global::Sample.TestClientOptions.SampleKeyVaultVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleComputeApiVersion = "2024-07-01"; - SampleKeyVaultApiVersion = "7.5"; - SampleStorageApiVersion = "2024-01-01"; + ComputeApiVersion = "2024-07-01"; + KeyVaultApiVersion = "7.5"; + StorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleComputeApiVersion"] is string sampleComputeApiVersion)) + if ((section["ComputeApiVersion"] is string computeApiVersion)) { - this.SampleComputeApiVersion = sampleComputeApiVersion; + this.ComputeApiVersion = computeApiVersion; } - if ((section["SampleKeyVaultApiVersion"] is string sampleKeyVaultApiVersion)) + if ((section["KeyVaultApiVersion"] is string keyVaultApiVersion)) { - this.SampleKeyVaultApiVersion = sampleKeyVaultApiVersion; + this.KeyVaultApiVersion = keyVaultApiVersion; } - if ((section["SampleStorageApiVersion"] is string sampleStorageApiVersion)) + if ((section["StorageApiVersion"] is string storageApiVersion)) { - this.SampleStorageApiVersion = sampleStorageApiVersion; + this.StorageApiVersion = storageApiVersion; } } - internal string SampleComputeApiVersion { get; } + internal string ComputeApiVersion { get; } - internal string SampleKeyVaultApiVersion { get; } + internal string KeyVaultApiVersion { get; } - internal string SampleStorageApiVersion { get; } + internal string StorageApiVersion { get; } - public enum SampleComputeVersion + public enum ComputeVersion { V2023_07_01 = 1, V2024_03_01 = 2, V2024_07_01 = 3 } - public enum SampleKeyVaultVersion + public enum KeyVaultVersion { V7_4 = 1, V7_5 = 2 } - public enum SampleStorageVersion + public enum StorageVersion { V2023_01_01 = 1, V2024_01_01 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs index 30c5361bdaf..1eb960fa71f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs @@ -11,21 +11,21 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.SampleServiceAVersion LatestSampleServiceAVersion = global::Sample.TestClientOptions.SampleServiceAVersion.V2_0; - private const global::Sample.TestClientOptions.SampleServiceBVersion LatestSampleServiceBVersion = global::Sample.TestClientOptions.SampleServiceBVersion.V4_0; + private const global::Sample.TestClientOptions.ServiceAVersion LatestServiceAVersion = global::Sample.TestClientOptions.ServiceAVersion.V2_0; + private const global::Sample.TestClientOptions.ServiceBVersion LatestServiceBVersion = global::Sample.TestClientOptions.ServiceBVersion.V4_0; - public TestClientOptions(global::Sample.TestClientOptions.SampleServiceAVersion sampleServiceAVersion = LatestSampleServiceAVersion, global::Sample.TestClientOptions.SampleServiceBVersion sampleServiceBVersion = LatestSampleServiceBVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) { - SampleServiceAApiVersion = sampleServiceAVersion switch + ServiceAApiVersion = serviceAVersion switch { - global::Sample.TestClientOptions.SampleServiceAVersion.V1_0 => "1.0", - global::Sample.TestClientOptions.SampleServiceAVersion.V2_0 => "2.0", + global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", + global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - SampleServiceBApiVersion = sampleServiceBVersion switch + ServiceBApiVersion = serviceBVersion switch { - global::Sample.TestClientOptions.SampleServiceBVersion.V3_0 => "3.0", - global::Sample.TestClientOptions.SampleServiceBVersion.V4_0 => "4.0", + global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", + global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", _ => throw new global::System.NotSupportedException() }; } @@ -33,33 +33,33 @@ public TestClientOptions(global::Sample.TestClientOptions.SampleServiceAVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleServiceAApiVersion = "2.0"; - SampleServiceBApiVersion = "4.0"; + ServiceAApiVersion = "2.0"; + ServiceBApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleServiceAApiVersion"] is string sampleServiceAApiVersion)) + if ((section["ServiceAApiVersion"] is string serviceAApiVersion)) { - this.SampleServiceAApiVersion = sampleServiceAApiVersion; + this.ServiceAApiVersion = serviceAApiVersion; } - if ((section["SampleServiceBApiVersion"] is string sampleServiceBApiVersion)) + if ((section["ServiceBApiVersion"] is string serviceBApiVersion)) { - this.SampleServiceBApiVersion = sampleServiceBApiVersion; + this.ServiceBApiVersion = serviceBApiVersion; } } - internal string SampleServiceAApiVersion { get; } + internal string ServiceAApiVersion { get; } - internal string SampleServiceBApiVersion { get; } + internal string ServiceBApiVersion { get; } - public enum SampleServiceAVersion + public enum ServiceAVersion { V1_0 = 1, V2_0 = 2 } - public enum SampleServiceBVersion + public enum ServiceBVersion { V3_0 = 1, V4_0 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 740d3a43b3a..7dd1ff9bf30 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,29 +11,29 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.SampleComputeVersion LatestSampleComputeVersion = global::Sample.TestClientOptions.SampleComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.SampleKeyVaultVersion LatestSampleKeyVaultVersion = global::Sample.TestClientOptions.SampleKeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.SampleStorageVersion LatestSampleStorageVersion = global::Sample.TestClientOptions.SampleStorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ComputeVersion.V2024_07_01; + private const global::Sample.TestClientOptions.KeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.KeyVaultVersion.V7_5; + private const global::Sample.TestClientOptions.StorageVersion LatestStorageVersion = global::Sample.TestClientOptions.StorageVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.SampleKeyVaultVersion sampleKeyVaultVersion = LatestSampleKeyVaultVersion, global::Sample.TestClientOptions.SampleStorageVersion sampleStorageVersion = LatestSampleStorageVersion, global::Sample.TestClientOptions.SampleComputeVersion sampleComputeVersion = LatestSampleComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.KeyVaultVersion keyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.StorageVersion storageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ComputeVersion computeVersion = LatestComputeVersion) { - SampleKeyVaultApiVersion = sampleKeyVaultVersion switch + KeyVaultApiVersion = keyVaultVersion switch { - global::Sample.TestClientOptions.SampleKeyVaultVersion.V7_4 => "7.4", - global::Sample.TestClientOptions.SampleKeyVaultVersion.V7_5 => "7.5", + global::Sample.TestClientOptions.KeyVaultVersion.V7_4 => "7.4", + global::Sample.TestClientOptions.KeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - SampleStorageApiVersion = sampleStorageVersion switch + StorageApiVersion = storageVersion switch { - global::Sample.TestClientOptions.SampleStorageVersion.V2023_01_01 => "2023-01-01", - global::Sample.TestClientOptions.SampleStorageVersion.V2024_01_01 => "2024-01-01", + global::Sample.TestClientOptions.StorageVersion.V2023_01_01 => "2023-01-01", + global::Sample.TestClientOptions.StorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - SampleComputeApiVersion = sampleComputeVersion switch + ComputeApiVersion = computeVersion switch { - global::Sample.TestClientOptions.SampleComputeVersion.V2023_07_01 => "2023-07-01", - global::Sample.TestClientOptions.SampleComputeVersion.V2024_03_01 => "2024-03-01", - global::Sample.TestClientOptions.SampleComputeVersion.V2024_07_01 => "2024-07-01", + global::Sample.TestClientOptions.ComputeVersion.V2023_07_01 => "2023-07-01", + global::Sample.TestClientOptions.ComputeVersion.V2024_03_01 => "2024-03-01", + global::Sample.TestClientOptions.ComputeVersion.V2024_07_01 => "2024-07-01", _ => throw new global::System.NotSupportedException() }; } @@ -41,47 +41,47 @@ public TestClientOptions(global::Sample.TestClientOptions.SampleKeyVaultVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleComputeApiVersion = "2024-07-01"; - SampleKeyVaultApiVersion = "7.5"; - SampleStorageApiVersion = "2024-01-01"; + ComputeApiVersion = "2024-07-01"; + KeyVaultApiVersion = "7.5"; + StorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleComputeApiVersion"] is string sampleComputeApiVersion)) + if ((section["ComputeApiVersion"] is string computeApiVersion)) { - this.SampleComputeApiVersion = sampleComputeApiVersion; + this.ComputeApiVersion = computeApiVersion; } - if ((section["SampleKeyVaultApiVersion"] is string sampleKeyVaultApiVersion)) + if ((section["KeyVaultApiVersion"] is string keyVaultApiVersion)) { - this.SampleKeyVaultApiVersion = sampleKeyVaultApiVersion; + this.KeyVaultApiVersion = keyVaultApiVersion; } - if ((section["SampleStorageApiVersion"] is string sampleStorageApiVersion)) + if ((section["StorageApiVersion"] is string storageApiVersion)) { - this.SampleStorageApiVersion = sampleStorageApiVersion; + this.StorageApiVersion = storageApiVersion; } } - internal string SampleComputeApiVersion { get; } + internal string ComputeApiVersion { get; } - internal string SampleKeyVaultApiVersion { get; } + internal string KeyVaultApiVersion { get; } - internal string SampleStorageApiVersion { get; } + internal string StorageApiVersion { get; } - public enum SampleComputeVersion + public enum ComputeVersion { V2023_07_01 = 1, V2024_03_01 = 2, V2024_07_01 = 3 } - public enum SampleKeyVaultVersion + public enum KeyVaultVersion { V7_4 = 1, V7_5 = 2 } - public enum SampleStorageVersion + public enum StorageVersion { V2023_01_01 = 1, V2024_01_01 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs index 836497737a6..346284286c2 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs @@ -38,15 +38,29 @@ protected override string BuildName() var serviceNamespace = _inputEnum.Namespace; if (!string.IsNullOrEmpty(serviceNamespace)) { - // Use the full namespace to guarantee uniqueness when services - // have different namespaces but the same last segment. - return $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}"; + var lastSegment = GetLastNamespaceSegment(serviceNamespace); + bool hasCollision = apiVersionEnums.Any(e => + e != _inputEnum && + !string.IsNullOrEmpty(e.Namespace) && + string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); + + // Only use the full namespace when there is a collision in the + // last segment; otherwise, use just the last segment. + return hasCollision + ? $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}" + : $"{lastSegment.ToIdentifierName()}{VersionSuffix}"; } } return ApiVersionEnumName; } + private static string GetLastNamespaceSegment(string ns) + { + int lastDot = ns.LastIndexOf('.'); + return lastDot >= 0 ? ns.Substring(lastDot + 1) : ns; + } + protected override FormattableString BuildDescription() => $"{ApiVersionEnumDescription}"; protected override IReadOnlyList BuildEnumValues() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs index 49ba16c2aad..8289cae49e3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs @@ -178,9 +178,9 @@ public void MultiServiceClient_WithMultipleApiVersionEnums_GeneratesCorrectEnumN Assert.IsTrue(storageEnumType is ApiVersionEnumProvider); var storageProvider = (ApiVersionEnumProvider)storageEnumType; - // Verify enum names use the full namespace for uniqueness: {FullNamespace}Version - Assert.AreEqual("SampleKeyVaultVersion", keyVaultProvider.Name); - Assert.AreEqual("SampleStorageVersion", storageProvider.Name); + // Verify enum names use only the last namespace segment when there are no collisions: {LastSegment}Version + Assert.AreEqual("KeyVaultVersion", keyVaultProvider.Name); + Assert.AreEqual("StorageVersion", storageProvider.Name); } [Test] @@ -211,5 +211,44 @@ public void MultiServiceClient_WithOneApiVersionEnums_GeneratesCorrectEnumNames( // Verify enum names follow the multiservice naming pattern: Service{ServiceName}Version Assert.AreEqual("ServiceVersion", keyVaultProvider.Name); } + + [Test] + public void MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace() + { + // When two services have different full namespaces but the same last segment, + // the full namespace should be used to avoid collisions. + var serviceOneEnum = InputFactory.StringEnum( + "ServiceOneVersion", + [("1.0", "1.0"), ("2.0", "2.0")], + usage: InputModelTypeUsage.ApiVersionEnum, + clientNamespace: "Azure.ServiceOne.Tests"); + var serviceTwoEnum = InputFactory.StringEnum( + "ServiceTwoVersion", + [("3.0", "3.0"), ("4.0", "4.0")], + usage: InputModelTypeUsage.ApiVersionEnum, + clientNamespace: "Azure.ServiceTwo.Tests"); + + var client = InputFactory.Client("TestClient", isMultiServiceClient: true); + + MockHelpers.LoadMockGenerator( + inputEnumTypes: [serviceOneEnum, serviceTwoEnum], + inputClients: [client]); + + var mockDeclaringType = new Mock(); + mockDeclaringType.Protected().Setup("BuildName").Returns("TestClientOptions"); + mockDeclaringType.Protected().Setup("BuildNamespace").Returns("Azure"); + + var serviceOneEnumType = EnumProvider.Create(serviceOneEnum, mockDeclaringType.Object); + Assert.IsTrue(serviceOneEnumType is ApiVersionEnumProvider); + var serviceOneProvider = (ApiVersionEnumProvider)serviceOneEnumType; + + var serviceTwoEnumType = EnumProvider.Create(serviceTwoEnum, mockDeclaringType.Object); + Assert.IsTrue(serviceTwoEnumType is ApiVersionEnumProvider); + var serviceTwoProvider = (ApiVersionEnumProvider)serviceTwoEnumType; + + // Verify enum names use the full namespace for uniqueness when last segments collide + Assert.AreEqual("AzureServiceOneTestsVersion", serviceOneProvider.Name); + Assert.AreEqual("AzureServiceTwoTestsVersion", serviceTwoProvider.Name); + } } } From 564104b85206c76f7879e99793ff788473f269ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:13:22 +0000 Subject: [PATCH 03/10] refactor: remove null-forgiving operator on collidingSegments Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ClientOptionsProvider.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index a3a74991fa8..9605d3b6327 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -133,7 +133,7 @@ private static bool UseSingletonInstance(InputClient inputClient) var properties = new Dictionary(_serviceVersionsEnums.Count); // Precompute which last namespace segments have collisions - HashSet? collidingSegments = null; + var collidingSegments = new HashSet(StringComparer.OrdinalIgnoreCase); if (_inputClient.IsMultiServiceClient) { var segmentCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -142,9 +142,13 @@ private static bool UseSingletonInstance(InputClient inputClient) var segment = GetLastNamespaceSegment(inputEnum.Namespace); segmentCounts[segment] = segmentCounts.TryGetValue(segment, out var count) ? count + 1 : 1; } - collidingSegments = new HashSet( - segmentCounts.Where(kv => kv.Value > 1).Select(kv => kv.Key), - StringComparer.OrdinalIgnoreCase); + foreach (var kv in segmentCounts) + { + if (kv.Value > 1) + { + collidingSegments.Add(kv.Key); + } + } } foreach (var (inputEnum, enumProvider) in _serviceVersionsEnums) @@ -156,7 +160,7 @@ private static bool UseSingletonInstance(InputClient inputClient) var lastSegment = GetLastNamespaceSegment(ns); // Only use the full namespace when the last segment collides // with another service's last segment. - versionPropertyName = collidingSegments!.Contains(lastSegment) + versionPropertyName = collidingSegments.Contains(lastSegment) ? $"{ns.ToIdentifierName()}{ApiVersionSuffix}" : $"{lastSegment.ToIdentifierName()}{ApiVersionSuffix}"; } From 4672dd527b3aba1321e1e3c6b6b08407d50aeecf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 15:25:46 +0000 Subject: [PATCH 04/10] feat: restore BuildNameForService helper and use it for non-collision naming Re-adds ClientHelper.BuildNameForService and its tests that were removed in PR #10061. Uses BuildNameForService for the non-collision case in both ApiVersionEnumProvider.BuildName() and ClientOptionsProvider.BuildVersionProperties(), falling back to the full namespace only when last segments collide. Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ClientOptionsProvider.cs | 4 +- ...Services_GeneratesExpectedClientOptions.cs | 58 +++--- ...Services_GeneratesExpectedClientOptions.cs | 58 +++--- .../src/Providers/ApiVersionEnumProvider.cs | 20 +- .../src/Shared/ClientHelper.cs | 39 ++++ .../ApiVersionEnumProviderTests.cs | 6 +- .../test/Shared/ClientHelperTests.cs | 171 ++++++++++++++++++ 7 files changed, 287 insertions(+), 69 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Shared/ClientHelperTests.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index 9605d3b6327..c0553333ee7 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -10,6 +10,7 @@ using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; +using Microsoft.TypeSpec.Generator.Shared; using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Statements; using Microsoft.TypeSpec.Generator.Utilities; @@ -162,7 +163,7 @@ private static bool UseSingletonInstance(InputClient inputClient) // with another service's last segment. versionPropertyName = collidingSegments.Contains(lastSegment) ? $"{ns.ToIdentifierName()}{ApiVersionSuffix}" - : $"{lastSegment.ToIdentifierName()}{ApiVersionSuffix}"; + : ClientHelper.BuildNameForService(ns, ServicePrefix, ApiVersionSuffix); } else { @@ -187,6 +188,7 @@ private static string GetLastNamespaceSegment(string ns) int lastDot = ns.LastIndexOf('.'); return lastDot >= 0 ? ns.Substring(lastDot + 1) : ns; } + private IReadOnlyDictionary? LatestVersionsFields => field ??= BuildLatestVersionsFields(); private Dictionary? BuildLatestVersionsFields() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 7dd1ff9bf30..1dffdca1dbb 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,29 +11,29 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.KeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.KeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.StorageVersion LatestStorageVersion = global::Sample.TestClientOptions.StorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ServiceComputeVersion LatestServiceComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; + private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestServiceKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; + private const global::Sample.TestClientOptions.ServiceStorageVersion LatestServiceStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.KeyVaultVersion keyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.StorageVersion storageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ComputeVersion computeVersion = LatestComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestServiceKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestServiceStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestServiceComputeVersion) { - KeyVaultApiVersion = keyVaultVersion switch + ServiceKeyVaultApiVersion = serviceKeyVaultVersion switch { - global::Sample.TestClientOptions.KeyVaultVersion.V7_4 => "7.4", - global::Sample.TestClientOptions.KeyVaultVersion.V7_5 => "7.5", + global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", + global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - StorageApiVersion = storageVersion switch + ServiceStorageApiVersion = serviceStorageVersion switch { - global::Sample.TestClientOptions.StorageVersion.V2023_01_01 => "2023-01-01", - global::Sample.TestClientOptions.StorageVersion.V2024_01_01 => "2024-01-01", + global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", + global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - ComputeApiVersion = computeVersion switch + ServiceComputeApiVersion = serviceComputeVersion switch { - global::Sample.TestClientOptions.ComputeVersion.V2023_07_01 => "2023-07-01", - global::Sample.TestClientOptions.ComputeVersion.V2024_03_01 => "2024-03-01", - global::Sample.TestClientOptions.ComputeVersion.V2024_07_01 => "2024-07-01", + global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", + global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", + global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01 => "2024-07-01", _ => throw new global::System.NotSupportedException() }; } @@ -41,47 +41,47 @@ public TestClientOptions(global::Sample.TestClientOptions.KeyVaultVersion keyVau [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - ComputeApiVersion = "2024-07-01"; - KeyVaultApiVersion = "7.5"; - StorageApiVersion = "2024-01-01"; + ServiceComputeApiVersion = "2024-07-01"; + ServiceKeyVaultApiVersion = "7.5"; + ServiceStorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["ComputeApiVersion"] is string computeApiVersion)) + if ((section["ServiceComputeApiVersion"] is string serviceComputeApiVersion)) { - this.ComputeApiVersion = computeApiVersion; + this.ServiceComputeApiVersion = serviceComputeApiVersion; } - if ((section["KeyVaultApiVersion"] is string keyVaultApiVersion)) + if ((section["ServiceKeyVaultApiVersion"] is string serviceKeyVaultApiVersion)) { - this.KeyVaultApiVersion = keyVaultApiVersion; + this.ServiceKeyVaultApiVersion = serviceKeyVaultApiVersion; } - if ((section["StorageApiVersion"] is string storageApiVersion)) + if ((section["ServiceStorageApiVersion"] is string serviceStorageApiVersion)) { - this.StorageApiVersion = storageApiVersion; + this.ServiceStorageApiVersion = serviceStorageApiVersion; } } - internal string ComputeApiVersion { get; } + internal string ServiceComputeApiVersion { get; } - internal string KeyVaultApiVersion { get; } + internal string ServiceKeyVaultApiVersion { get; } - internal string StorageApiVersion { get; } + internal string ServiceStorageApiVersion { get; } - public enum ComputeVersion + public enum ServiceComputeVersion { V2023_07_01 = 1, V2024_03_01 = 2, V2024_07_01 = 3 } - public enum KeyVaultVersion + public enum ServiceKeyVaultVersion { V7_4 = 1, V7_5 = 2 } - public enum StorageVersion + public enum ServiceStorageVersion { V2023_01_01 = 1, V2024_01_01 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 7dd1ff9bf30..1dffdca1dbb 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,29 +11,29 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.KeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.KeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.StorageVersion LatestStorageVersion = global::Sample.TestClientOptions.StorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ServiceComputeVersion LatestServiceComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; + private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestServiceKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; + private const global::Sample.TestClientOptions.ServiceStorageVersion LatestServiceStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.KeyVaultVersion keyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.StorageVersion storageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ComputeVersion computeVersion = LatestComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestServiceKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestServiceStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestServiceComputeVersion) { - KeyVaultApiVersion = keyVaultVersion switch + ServiceKeyVaultApiVersion = serviceKeyVaultVersion switch { - global::Sample.TestClientOptions.KeyVaultVersion.V7_4 => "7.4", - global::Sample.TestClientOptions.KeyVaultVersion.V7_5 => "7.5", + global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", + global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - StorageApiVersion = storageVersion switch + ServiceStorageApiVersion = serviceStorageVersion switch { - global::Sample.TestClientOptions.StorageVersion.V2023_01_01 => "2023-01-01", - global::Sample.TestClientOptions.StorageVersion.V2024_01_01 => "2024-01-01", + global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", + global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - ComputeApiVersion = computeVersion switch + ServiceComputeApiVersion = serviceComputeVersion switch { - global::Sample.TestClientOptions.ComputeVersion.V2023_07_01 => "2023-07-01", - global::Sample.TestClientOptions.ComputeVersion.V2024_03_01 => "2024-03-01", - global::Sample.TestClientOptions.ComputeVersion.V2024_07_01 => "2024-07-01", + global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", + global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", + global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01 => "2024-07-01", _ => throw new global::System.NotSupportedException() }; } @@ -41,47 +41,47 @@ public TestClientOptions(global::Sample.TestClientOptions.KeyVaultVersion keyVau [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - ComputeApiVersion = "2024-07-01"; - KeyVaultApiVersion = "7.5"; - StorageApiVersion = "2024-01-01"; + ServiceComputeApiVersion = "2024-07-01"; + ServiceKeyVaultApiVersion = "7.5"; + ServiceStorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["ComputeApiVersion"] is string computeApiVersion)) + if ((section["ServiceComputeApiVersion"] is string serviceComputeApiVersion)) { - this.ComputeApiVersion = computeApiVersion; + this.ServiceComputeApiVersion = serviceComputeApiVersion; } - if ((section["KeyVaultApiVersion"] is string keyVaultApiVersion)) + if ((section["ServiceKeyVaultApiVersion"] is string serviceKeyVaultApiVersion)) { - this.KeyVaultApiVersion = keyVaultApiVersion; + this.ServiceKeyVaultApiVersion = serviceKeyVaultApiVersion; } - if ((section["StorageApiVersion"] is string storageApiVersion)) + if ((section["ServiceStorageApiVersion"] is string serviceStorageApiVersion)) { - this.StorageApiVersion = storageApiVersion; + this.ServiceStorageApiVersion = serviceStorageApiVersion; } } - internal string ComputeApiVersion { get; } + internal string ServiceComputeApiVersion { get; } - internal string KeyVaultApiVersion { get; } + internal string ServiceKeyVaultApiVersion { get; } - internal string StorageApiVersion { get; } + internal string ServiceStorageApiVersion { get; } - public enum ComputeVersion + public enum ServiceComputeVersion { V2023_07_01 = 1, V2024_03_01 = 2, V2024_07_01 = 3 } - public enum KeyVaultVersion + public enum ServiceKeyVaultVersion { V7_4 = 1, V7_5 = 2 } - public enum StorageVersion + public enum ServiceStorageVersion { V2023_01_01 = 1, V2024_01_01 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs index 346284286c2..8d2cd415c8a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs @@ -9,6 +9,7 @@ using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Primitives; +using Microsoft.TypeSpec.Generator.Shared; using Microsoft.TypeSpec.Generator.Utilities; using static Microsoft.TypeSpec.Generator.Snippets.Snippet; @@ -38,23 +39,28 @@ protected override string BuildName() var serviceNamespace = _inputEnum.Namespace; if (!string.IsNullOrEmpty(serviceNamespace)) { - var lastSegment = GetLastNamespaceSegment(serviceNamespace); - bool hasCollision = apiVersionEnums.Any(e => - e != _inputEnum && - !string.IsNullOrEmpty(e.Namespace) && - string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); + bool hasCollision = HasLastSegmentCollision(serviceNamespace, apiVersionEnums); // Only use the full namespace when there is a collision in the - // last segment; otherwise, use just the last segment. + // last segment; otherwise, use BuildNameForService with the last segment. return hasCollision ? $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}" - : $"{lastSegment.ToIdentifierName()}{VersionSuffix}"; + : ClientHelper.BuildNameForService(serviceNamespace, ServicePrefix, VersionSuffix); } } return ApiVersionEnumName; } + private bool HasLastSegmentCollision(string serviceNamespace, List apiVersionEnums) + { + var lastSegment = GetLastNamespaceSegment(serviceNamespace); + return apiVersionEnums.Any(e => + e != _inputEnum && + !string.IsNullOrEmpty(e.Namespace) && + string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); + } + private static string GetLastNamespaceSegment(string ns) { int lastDot = ns.LastIndexOf('.'); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs new file mode 100644 index 00000000000..527959086ec --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.TypeSpec.Generator.Shared +{ + internal static class ClientHelper + { + /// + /// Builds a name with the specified prefix and suffix, ensuring no duplicate prefix or suffix + /// if the namespace/service segment already contains them. + /// + /// The full service name. + /// The prefix to ensure (e.g., "Service", "Latest"). + /// The suffix to ensure (e.g., "Version"). + /// A name with the specified prefix and suffix. + public static string BuildNameForService(string serviceName, string prefix, string suffix) + { + var lastNamespaceSegment = serviceName.AsSpan(); + int lastDotIndex = serviceName.LastIndexOf('.'); + if (lastDotIndex >= 0) + { + lastNamespaceSegment = lastNamespaceSegment.Slice(lastDotIndex + 1); + } + + bool hasPrefix = lastNamespaceSegment.StartsWith(prefix.AsSpan(), StringComparison.OrdinalIgnoreCase); + bool hasSuffix = lastNamespaceSegment.EndsWith(suffix.AsSpan(), StringComparison.OrdinalIgnoreCase); + + return (hasPrefix, hasSuffix) switch + { + (true, true) => lastNamespaceSegment.ToString(), + (true, false) => $"{lastNamespaceSegment}{suffix}", + (false, true) => $"{prefix}{lastNamespaceSegment}", + (false, false) => $"{prefix}{lastNamespaceSegment}{suffix}" + }; + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs index 8289cae49e3..afbd8979764 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs @@ -178,9 +178,9 @@ public void MultiServiceClient_WithMultipleApiVersionEnums_GeneratesCorrectEnumN Assert.IsTrue(storageEnumType is ApiVersionEnumProvider); var storageProvider = (ApiVersionEnumProvider)storageEnumType; - // Verify enum names use only the last namespace segment when there are no collisions: {LastSegment}Version - Assert.AreEqual("KeyVaultVersion", keyVaultProvider.Name); - Assert.AreEqual("StorageVersion", storageProvider.Name); + // Verify enum names use BuildNameForService when there are no collisions + Assert.AreEqual("ServiceKeyVaultVersion", keyVaultProvider.Name); + Assert.AreEqual("ServiceStorageVersion", storageProvider.Name); } [Test] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Shared/ClientHelperTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Shared/ClientHelperTests.cs new file mode 100644 index 00000000000..e9ea467e259 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Shared/ClientHelperTests.cs @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.TypeSpec.Generator.Shared; +using NUnit.Framework; + +namespace Microsoft.TypeSpec.Generator.Tests.Shared +{ + public class ClientHelperTests + { + [Test] + public void BuildNameForService_NoPrefixNoSuffix_AddsBoth() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVault", "Service", "Version"); + Assert.AreEqual("ServiceKeyVaultVersion", result); + } + + [Test] + public void BuildNameForService_HasPrefixNoSuffix_AddsSuffix() + { + var result = ClientHelper.BuildNameForService("Sample.ServiceKeyVault", "Service", "Version"); + Assert.AreEqual("ServiceKeyVaultVersion", result); + } + + [Test] + public void BuildNameForService_NoPrefixHasSuffix_AddsPrefix() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVaultVersion", "Service", "Version"); + Assert.AreEqual("ServiceKeyVaultVersion", result); + } + + [Test] + public void BuildNameForService_HasPrefixAndSuffix_ReturnsAsIs() + { + var result = ClientHelper.BuildNameForService("Sample.ServiceKeyVaultVersion", "Service", "Version"); + Assert.AreEqual("ServiceKeyVaultVersion", result); + } + + // Namespace handling tests + + [Test] + public void BuildNameForService_MultipleNamespaceSegments_ExtractsLastSegment() + { + var result = ClientHelper.BuildNameForService("Azure.ResourceManager.Storage", "Service", "Version"); + Assert.AreEqual("ServiceStorageVersion", result); + } + + [Test] + public void BuildNameForService_NoNamespaceSegments_UsesFullName() + { + var result = ClientHelper.BuildNameForService("Storage", "Service", "Version"); + Assert.AreEqual("ServiceStorageVersion", result); + } + + [Test] + public void BuildNameForService_SingleDotNamespace_ExtractsLastSegment() + { + var result = ClientHelper.BuildNameForService("Sample.Compute", "Service", "Version"); + Assert.AreEqual("ServiceComputeVersion", result); + } + + // Case insensitivity tests + + [Test] + public void BuildNameForService_PrefixCaseInsensitive_LowerCase() + { + var result = ClientHelper.BuildNameForService("Sample.serviceKeyVault", "Service", "Version"); + Assert.AreEqual("serviceKeyVaultVersion", result); + } + + [Test] + public void BuildNameForService_SuffixCaseInsensitive_LowerCase() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVaultversion", "Service", "Version"); + Assert.AreEqual("ServiceKeyVaultversion", result); + } + + [Test] + public void BuildNameForService_BothCaseInsensitive_MixedCase() + { + var result = ClientHelper.BuildNameForService("Sample.SERVICEKeyVaultVERSION", "Service", "Version"); + Assert.AreEqual("SERVICEKeyVaultVERSION", result); + } + + // Edge cases + + [Test] + public void BuildNameForService_EmptyServiceName_ReturnsEmptyWithPrefixAndSuffix() + { + var result = ClientHelper.BuildNameForService("", "Service", "Version"); + Assert.AreEqual("ServiceVersion", result); + } + + [Test] + public void BuildNameForService_TrailingDot_ReturnsEmptyWithPrefixAndSuffix() + { + var result = ClientHelper.BuildNameForService("Sample.", "Service", "Version"); + Assert.AreEqual("ServiceVersion", result); + } + + [Test] + public void BuildNameForService_EmptyPrefix_OnlyAddsSuffix() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVault", "", "Version"); + Assert.AreEqual("KeyVaultVersion", result); + } + + [Test] + public void BuildNameForService_EmptySuffix_OnlyAddsPrefix() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVault", "Service", ""); + Assert.AreEqual("ServiceKeyVault", result); + } + + [Test] + public void BuildNameForService_BothPrefixAndSuffixEmpty_ReturnsLastSegment() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVault", "", ""); + Assert.AreEqual("KeyVault", result); + } + + [Test] + public void BuildNameForService_ServiceNameEqualsPrefix_AddsSuffix() + { + var result = ClientHelper.BuildNameForService("Sample.Service", "Service", "Version"); + Assert.AreEqual("ServiceVersion", result); + } + + [Test] + public void BuildNameForService_ServiceNameEqualsSuffix_AddsPrefix() + { + var result = ClientHelper.BuildNameForService("Sample.Version", "Service", "Version"); + Assert.AreEqual("ServiceVersion", result); + } + + [Test] + public void BuildNameForService_ServiceNameEqualsPrefixAndSuffix_ReturnsAsIs() + { + var result = ClientHelper.BuildNameForService("Sample.ServiceVersion", "Service", "Version"); + Assert.AreEqual("ServiceVersion", result); + } + + [Test] + public void BuildNameForService_AzureKeyVault_GeneratesCorrectName() + { + var result = ClientHelper.BuildNameForService("Azure.Security.KeyVault", "Service", "Version"); + Assert.AreEqual("ServiceKeyVaultVersion", result); + } + + [Test] + public void BuildNameForService_AzureStorage_GeneratesCorrectName() + { + var result = ClientHelper.BuildNameForService("Azure.Storage.Blobs", "Service", "Version"); + Assert.AreEqual("ServiceBlobsVersion", result); + } + + [Test] + public void BuildNameForService_ApiVersionSuffix_GeneratesCorrectName() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVault", "Service", "ApiVersion"); + Assert.AreEqual("ServiceKeyVaultApiVersion", result); + } + + [Test] + public void BuildNameForService_LatestPrefix_GeneratesCorrectName() + { + var result = ClientHelper.BuildNameForService("Sample.KeyVault", "Latest", "Version"); + Assert.AreEqual("LatestKeyVaultVersion", result); + } + } +} From 5b29b5809aa685c621ac2f63eec3015ea9adb02d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:28:37 +0000 Subject: [PATCH 05/10] refactor: address review feedback - keep field names unchanged, extract helpers, add TestData validation - Revert ClientOptionsProvider.BuildVersionProperties() to use full namespace (field names unchanged) - Revert ClientProviderTests assertions to original field names - Move GetLastNamespaceSegment and HasLastSegmentCollision to ClientHelper as shared static helpers - Update ApiVersionEnumProvider.BuildName() to use shared ClientHelper methods - Add TestData golden file validation for collision test - Update test data files: only enum type names change, property/field names stay the same Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ClientOptionsProvider.cs | 48 ++----------------- .../ClientProviders/ClientProviderTests.cs | 8 ++-- ...ceClient_GeneratesExpectedClientOptions.cs | 20 ++++---- ...Services_GeneratesExpectedClientOptions.cs | 30 ++++++------ ...edClient_GeneratesExpectedClientOptions.cs | 20 ++++---- ...Services_GeneratesExpectedClientOptions.cs | 30 ++++++------ .../src/Providers/ApiVersionEnumProvider.cs | 19 +------- .../src/Shared/ClientHelper.cs | 31 ++++++++++++ .../ApiVersionEnumProviderTests.cs | 10 ++++ ...tSegments_UsesFullNamespace(ServiceOne).cs | 12 +++++ ...tSegments_UsesFullNamespace(ServiceTwo).cs | 12 +++++ 11 files changed, 125 insertions(+), 115 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index c0553333ee7..975c2492146 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -10,7 +10,6 @@ using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; -using Microsoft.TypeSpec.Generator.Shared; using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Statements; using Microsoft.TypeSpec.Generator.Utilities; @@ -132,43 +131,13 @@ private static bool UseSingletonInstance(InputClient inputClient) } var properties = new Dictionary(_serviceVersionsEnums.Count); - - // Precompute which last namespace segments have collisions - var collidingSegments = new HashSet(StringComparer.OrdinalIgnoreCase); - if (_inputClient.IsMultiServiceClient) - { - var segmentCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var (inputEnum, _) in _serviceVersionsEnums) - { - var segment = GetLastNamespaceSegment(inputEnum.Namespace); - segmentCounts[segment] = segmentCounts.TryGetValue(segment, out var count) ? count + 1 : 1; - } - foreach (var kv in segmentCounts) - { - if (kv.Value > 1) - { - collidingSegments.Add(kv.Key); - } - } - } - foreach (var (inputEnum, enumProvider) in _serviceVersionsEnums) { - string versionPropertyName; - if (_inputClient.IsMultiServiceClient) - { - var ns = inputEnum.Namespace; - var lastSegment = GetLastNamespaceSegment(ns); - // Only use the full namespace when the last segment collides - // with another service's last segment. - versionPropertyName = collidingSegments.Contains(lastSegment) - ? $"{ns.ToIdentifierName()}{ApiVersionSuffix}" - : ClientHelper.BuildNameForService(ns, ServicePrefix, ApiVersionSuffix); - } - else - { - versionPropertyName = VersionSuffix; - } + // For multi-service clients, use the full namespace to guarantee uniqueness + // (the last segment alone can collide when services share a namespace). + var versionPropertyName = _inputClient.IsMultiServiceClient + ? $"{inputEnum.Namespace.ToIdentifierName()}{ApiVersionSuffix}" + : VersionSuffix; var versionProperty = new PropertyProvider( null, @@ -182,13 +151,6 @@ private static bool UseSingletonInstance(InputClient inputClient) return properties; } - - private static string GetLastNamespaceSegment(string ns) - { - int lastDot = ns.LastIndexOf('.'); - return lastDot >= 0 ? ns.Substring(lastDot + 1) : ns; - } - private IReadOnlyDictionary? LatestVersionsFields => field ??= BuildLatestVersionsFields(); private Dictionary? BuildLatestVersionsFields() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs index 5276928178a..127e1420d06 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs @@ -3678,12 +3678,12 @@ public void GetApiVersionFieldForService_MultiService_ReturnsMatchingField() // Should return the matching field for ServiceA var fieldA = clientProvider!.GetApiVersionFieldForService("Sample.ServiceA"); Assert.IsNotNull(fieldA); - Assert.AreEqual("_serviceAApiVersion", fieldA!.Name); + Assert.AreEqual("_sampleServiceAApiVersion", fieldA!.Name); // Should return the matching field for ServiceB var fieldB = clientProvider.GetApiVersionFieldForService("Sample.ServiceB"); Assert.IsNotNull(fieldB); - Assert.AreEqual("_serviceBApiVersion", fieldB!.Name); + Assert.AreEqual("_sampleServiceBApiVersion", fieldB!.Name); } [Test] @@ -3814,11 +3814,11 @@ public void GetApiVersionFieldForService_MultiService_CaseInsensitiveMatch() // Should match case-insensitively var fieldLowerCase = clientProvider!.GetApiVersionFieldForService("sample.serviceA"); Assert.IsNotNull(fieldLowerCase); - Assert.AreEqual("_serviceAApiVersion", fieldLowerCase!.Name); + Assert.AreEqual("_sampleServiceAApiVersion", fieldLowerCase!.Name); var fieldUpperCase = clientProvider.GetApiVersionFieldForService("SAMPLE.SERVICEa"); Assert.IsNotNull(fieldUpperCase); - Assert.AreEqual("_serviceAApiVersion", fieldUpperCase!.Name); + Assert.AreEqual("_sampleServiceAApiVersion", fieldUpperCase!.Name); } [Test] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs index 1eb960fa71f..aea43de4818 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs @@ -16,13 +16,13 @@ public partial class TestClientOptions : global::System.ClientModel.Primitives.C public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) { - ServiceAApiVersion = serviceAVersion switch + SampleServiceAApiVersion = serviceAVersion switch { global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - ServiceBApiVersion = serviceBVersion switch + SampleServiceBApiVersion = serviceBVersion switch { global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", @@ -33,25 +33,25 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion servic [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - ServiceAApiVersion = "2.0"; - ServiceBApiVersion = "4.0"; + SampleServiceAApiVersion = "2.0"; + SampleServiceBApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["ServiceAApiVersion"] is string serviceAApiVersion)) + if ((section["SampleServiceAApiVersion"] is string sampleServiceAApiVersion)) { - this.ServiceAApiVersion = serviceAApiVersion; + this.SampleServiceAApiVersion = sampleServiceAApiVersion; } - if ((section["ServiceBApiVersion"] is string serviceBApiVersion)) + if ((section["SampleServiceBApiVersion"] is string sampleServiceBApiVersion)) { - this.ServiceBApiVersion = serviceBApiVersion; + this.SampleServiceBApiVersion = sampleServiceBApiVersion; } } - internal string ServiceAApiVersion { get; } + internal string SampleServiceAApiVersion { get; } - internal string ServiceBApiVersion { get; } + internal string SampleServiceBApiVersion { get; } public enum ServiceAVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 1dffdca1dbb..ebba2f7aebd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -17,19 +17,19 @@ public partial class TestClientOptions : global::System.ClientModel.Primitives.C public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestServiceKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestServiceStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestServiceComputeVersion) { - ServiceKeyVaultApiVersion = serviceKeyVaultVersion switch + SampleKeyVaultApiVersion = serviceKeyVaultVersion switch { global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - ServiceStorageApiVersion = serviceStorageVersion switch + SampleStorageApiVersion = serviceStorageVersion switch { global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - ServiceComputeApiVersion = serviceComputeVersion switch + SampleComputeApiVersion = serviceComputeVersion switch { global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", @@ -41,32 +41,32 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - ServiceComputeApiVersion = "2024-07-01"; - ServiceKeyVaultApiVersion = "7.5"; - ServiceStorageApiVersion = "2024-01-01"; + SampleComputeApiVersion = "2024-07-01"; + SampleKeyVaultApiVersion = "7.5"; + SampleStorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["ServiceComputeApiVersion"] is string serviceComputeApiVersion)) + if ((section["SampleComputeApiVersion"] is string sampleComputeApiVersion)) { - this.ServiceComputeApiVersion = serviceComputeApiVersion; + this.SampleComputeApiVersion = sampleComputeApiVersion; } - if ((section["ServiceKeyVaultApiVersion"] is string serviceKeyVaultApiVersion)) + if ((section["SampleKeyVaultApiVersion"] is string sampleKeyVaultApiVersion)) { - this.ServiceKeyVaultApiVersion = serviceKeyVaultApiVersion; + this.SampleKeyVaultApiVersion = sampleKeyVaultApiVersion; } - if ((section["ServiceStorageApiVersion"] is string serviceStorageApiVersion)) + if ((section["SampleStorageApiVersion"] is string sampleStorageApiVersion)) { - this.ServiceStorageApiVersion = serviceStorageApiVersion; + this.SampleStorageApiVersion = sampleStorageApiVersion; } } - internal string ServiceComputeApiVersion { get; } + internal string SampleComputeApiVersion { get; } - internal string ServiceKeyVaultApiVersion { get; } + internal string SampleKeyVaultApiVersion { get; } - internal string ServiceStorageApiVersion { get; } + internal string SampleStorageApiVersion { get; } public enum ServiceComputeVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs index 1eb960fa71f..aea43de4818 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs @@ -16,13 +16,13 @@ public partial class TestClientOptions : global::System.ClientModel.Primitives.C public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) { - ServiceAApiVersion = serviceAVersion switch + SampleServiceAApiVersion = serviceAVersion switch { global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - ServiceBApiVersion = serviceBVersion switch + SampleServiceBApiVersion = serviceBVersion switch { global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", @@ -33,25 +33,25 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion servic [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - ServiceAApiVersion = "2.0"; - ServiceBApiVersion = "4.0"; + SampleServiceAApiVersion = "2.0"; + SampleServiceBApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["ServiceAApiVersion"] is string serviceAApiVersion)) + if ((section["SampleServiceAApiVersion"] is string sampleServiceAApiVersion)) { - this.ServiceAApiVersion = serviceAApiVersion; + this.SampleServiceAApiVersion = sampleServiceAApiVersion; } - if ((section["ServiceBApiVersion"] is string serviceBApiVersion)) + if ((section["SampleServiceBApiVersion"] is string sampleServiceBApiVersion)) { - this.ServiceBApiVersion = serviceBApiVersion; + this.SampleServiceBApiVersion = sampleServiceBApiVersion; } } - internal string ServiceAApiVersion { get; } + internal string SampleServiceAApiVersion { get; } - internal string ServiceBApiVersion { get; } + internal string SampleServiceBApiVersion { get; } public enum ServiceAVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 1dffdca1dbb..ebba2f7aebd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -17,19 +17,19 @@ public partial class TestClientOptions : global::System.ClientModel.Primitives.C public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestServiceKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestServiceStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestServiceComputeVersion) { - ServiceKeyVaultApiVersion = serviceKeyVaultVersion switch + SampleKeyVaultApiVersion = serviceKeyVaultVersion switch { global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - ServiceStorageApiVersion = serviceStorageVersion switch + SampleStorageApiVersion = serviceStorageVersion switch { global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - ServiceComputeApiVersion = serviceComputeVersion switch + SampleComputeApiVersion = serviceComputeVersion switch { global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", @@ -41,32 +41,32 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - ServiceComputeApiVersion = "2024-07-01"; - ServiceKeyVaultApiVersion = "7.5"; - ServiceStorageApiVersion = "2024-01-01"; + SampleComputeApiVersion = "2024-07-01"; + SampleKeyVaultApiVersion = "7.5"; + SampleStorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["ServiceComputeApiVersion"] is string serviceComputeApiVersion)) + if ((section["SampleComputeApiVersion"] is string sampleComputeApiVersion)) { - this.ServiceComputeApiVersion = serviceComputeApiVersion; + this.SampleComputeApiVersion = sampleComputeApiVersion; } - if ((section["ServiceKeyVaultApiVersion"] is string serviceKeyVaultApiVersion)) + if ((section["SampleKeyVaultApiVersion"] is string sampleKeyVaultApiVersion)) { - this.ServiceKeyVaultApiVersion = serviceKeyVaultApiVersion; + this.SampleKeyVaultApiVersion = sampleKeyVaultApiVersion; } - if ((section["ServiceStorageApiVersion"] is string serviceStorageApiVersion)) + if ((section["SampleStorageApiVersion"] is string sampleStorageApiVersion)) { - this.ServiceStorageApiVersion = serviceStorageApiVersion; + this.SampleStorageApiVersion = sampleStorageApiVersion; } } - internal string ServiceComputeApiVersion { get; } + internal string SampleComputeApiVersion { get; } - internal string ServiceKeyVaultApiVersion { get; } + internal string SampleKeyVaultApiVersion { get; } - internal string ServiceStorageApiVersion { get; } + internal string SampleStorageApiVersion { get; } public enum ServiceComputeVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs index 8d2cd415c8a..cbcc6cd648e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs @@ -39,11 +39,9 @@ protected override string BuildName() var serviceNamespace = _inputEnum.Namespace; if (!string.IsNullOrEmpty(serviceNamespace)) { - bool hasCollision = HasLastSegmentCollision(serviceNamespace, apiVersionEnums); - // Only use the full namespace when there is a collision in the // last segment; otherwise, use BuildNameForService with the last segment. - return hasCollision + return ClientHelper.HasLastSegmentCollision(serviceNamespace, _inputEnum, apiVersionEnums) ? $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}" : ClientHelper.BuildNameForService(serviceNamespace, ServicePrefix, VersionSuffix); } @@ -52,21 +50,6 @@ protected override string BuildName() return ApiVersionEnumName; } - private bool HasLastSegmentCollision(string serviceNamespace, List apiVersionEnums) - { - var lastSegment = GetLastNamespaceSegment(serviceNamespace); - return apiVersionEnums.Any(e => - e != _inputEnum && - !string.IsNullOrEmpty(e.Namespace) && - string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); - } - - private static string GetLastNamespaceSegment(string ns) - { - int lastDot = ns.LastIndexOf('.'); - return lastDot >= 0 ? ns.Substring(lastDot + 1) : ns; - } - protected override FormattableString BuildDescription() => $"{ApiVersionEnumDescription}"; protected override IReadOnlyList BuildEnumValues() diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs index 527959086ec..62fd5551840 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs @@ -2,6 +2,9 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.TypeSpec.Generator.Input; namespace Microsoft.TypeSpec.Generator.Shared { @@ -35,5 +38,33 @@ public static string BuildNameForService(string serviceName, string prefix, stri (false, false) => $"{prefix}{lastNamespaceSegment}{suffix}" }; } + + /// + /// Extracts the last segment of a dotted namespace. + /// + /// The full namespace (e.g., "Sample.KeyVault"). + /// The last segment (e.g., "KeyVault"), or the input if there is no dot. + public static string GetLastNamespaceSegment(string ns) + { + int lastDot = ns.LastIndexOf('.'); + return lastDot >= 0 ? ns.Substring(lastDot + 1) : ns; + } + + /// + /// Determines whether the last namespace segment of the given service namespace + /// collides with any other enum's last namespace segment in the collection. + /// + /// The namespace to check for collisions. + /// The current enum to exclude from the comparison. + /// All API version enums to compare against. + /// True if another enum has the same last namespace segment. + public static bool HasLastSegmentCollision(string serviceNamespace, InputEnumType currentEnum, IEnumerable apiVersionEnums) + { + var lastSegment = GetLastNamespaceSegment(serviceNamespace); + return apiVersionEnums.Any(e => + e != currentEnum && + !string.IsNullOrEmpty(e.Namespace) && + string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs index afbd8979764..bde61723da4 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.TypeSpec.Generator.Input; +using Microsoft.TypeSpec.Generator.Primitives; using Microsoft.TypeSpec.Generator.Providers; using Microsoft.TypeSpec.Generator.Tests.Common; using NUnit.Framework; @@ -249,6 +250,15 @@ public void MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace() // Verify enum names use the full namespace for uniqueness when last segments collide Assert.AreEqual("AzureServiceOneTestsVersion", serviceOneProvider.Name); Assert.AreEqual("AzureServiceTwoTestsVersion", serviceTwoProvider.Name); + + // Validate generated output + var writerOne = new TypeProviderWriter(serviceOneProvider); + var fileOne = writerOne.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile("ServiceOne"), fileOne.Content); + + var writerTwo = new TypeProviderWriter(serviceTwoProvider); + var fileTwo = writerTwo.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile("ServiceTwo"), fileTwo.Content); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs new file mode 100644 index 00000000000..4f8c659dd91 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Azure.ServiceOne.Tests +{ + public enum AzureServiceOneTestsVersion + { + V1_0 = 1, + V2_0 = 2 + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs new file mode 100644 index 00000000000..eb92b3edda9 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs @@ -0,0 +1,12 @@ +// + +#nullable disable + +namespace Azure.ServiceTwo.Tests +{ + public enum AzureServiceTwoTestsVersion + { + V3_0 = 1, + V4_0 = 2 + } +} From 10024bc904a519d6d629b4cf0f7b0b44e282bc2e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 19:29:00 +0000 Subject: [PATCH 06/10] refactor: apply collision-aware naming to all multi-service members (enum type, version properties, fields) Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/4bb660ba-ec46-42ad-8074-4e13314801b6 --- .../src/Providers/ClientOptionsProvider.cs | 41 +++++++++++++++---- .../ClientProviders/ClientProviderTests.cs | 8 ++-- ...tiServiceClient_GeneratesExpectedClient.cs | 12 +++--- ...thThreeServices_GeneratesExpectedClient.cs | 18 ++++---- ...eCombinedClient_GeneratesExpectedClient.cs | 8 ++-- ...thThreeServices_GeneratesExpectedClient.cs | 12 +++--- ...binedClient_GeneratesExpectedRestClient.cs | 4 +- ...reeServices_GeneratesExpectedRestClient.cs | 6 +-- ...ceClient_GeneratesExpectedClientOptions.cs | 20 ++++----- ...Services_GeneratesExpectedClientOptions.cs | 38 ++++++++--------- ...edClient_GeneratesExpectedClientOptions.cs | 20 ++++----- ...Services_GeneratesExpectedClientOptions.cs | 38 ++++++++--------- 12 files changed, 124 insertions(+), 101 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index 975c2492146..d7af600dd76 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -12,6 +12,7 @@ using Microsoft.TypeSpec.Generator.Providers; using Microsoft.TypeSpec.Generator.Snippets; using Microsoft.TypeSpec.Generator.Statements; +using Microsoft.TypeSpec.Generator.Shared; using Microsoft.TypeSpec.Generator.Utilities; using static Microsoft.TypeSpec.Generator.Snippets.Snippet; @@ -133,11 +134,21 @@ private static bool UseSingletonInstance(InputClient inputClient) var properties = new Dictionary(_serviceVersionsEnums.Count); foreach (var (inputEnum, enumProvider) in _serviceVersionsEnums) { - // For multi-service clients, use the full namespace to guarantee uniqueness - // (the last segment alone can collide when services share a namespace). - var versionPropertyName = _inputClient.IsMultiServiceClient - ? $"{inputEnum.Namespace.ToIdentifierName()}{ApiVersionSuffix}" - : VersionSuffix; + string versionPropertyName; + if (!_inputClient.IsMultiServiceClient) + { + versionPropertyName = VersionSuffix; + } + else + { + var serviceNamespace = inputEnum.Namespace; + // Use the full namespace only when the last segment collides; + // otherwise, use BuildNameForService for shorter names. + versionPropertyName = !string.IsNullOrEmpty(serviceNamespace) && + ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) + ? $"{serviceNamespace.ToIdentifierName()}{ApiVersionSuffix}" + : ClientHelper.BuildNameForService(serviceNamespace ?? string.Empty, string.Empty, ApiVersionSuffix); + } var versionProperty = new PropertyProvider( null, @@ -161,11 +172,23 @@ private static bool UseSingletonInstance(InputClient inputClient) } Dictionary latestVersionFields = new(_serviceVersionsEnums.Count); - foreach (var enumProvider in _serviceVersionsEnums.Values) + foreach (var (inputEnum, enumProvider) in _serviceVersionsEnums) { - var fieldName = _inputClient.IsMultiServiceClient - ? $"{LatestPrefix}{enumProvider.Name.ToIdentifierName()}" - : LatestVersionFieldName; + string fieldName; + if (!_inputClient.IsMultiServiceClient) + { + fieldName = LatestVersionFieldName; + } + else + { + var serviceNamespace = inputEnum.Namespace; + // Use the full namespace only when the last segment collides; + // otherwise, use BuildNameForService for shorter names. + fieldName = !string.IsNullOrEmpty(serviceNamespace) && + ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) + ? $"{LatestPrefix}{serviceNamespace.ToIdentifierName()}{VersionSuffix}" + : ClientHelper.BuildNameForService(serviceNamespace ?? string.Empty, LatestPrefix, VersionSuffix); + } var field = new FieldProvider( modifiers: FieldModifiers.Private | FieldModifiers.Const, type: enumProvider.Type, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs index 127e1420d06..5276928178a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs @@ -3678,12 +3678,12 @@ public void GetApiVersionFieldForService_MultiService_ReturnsMatchingField() // Should return the matching field for ServiceA var fieldA = clientProvider!.GetApiVersionFieldForService("Sample.ServiceA"); Assert.IsNotNull(fieldA); - Assert.AreEqual("_sampleServiceAApiVersion", fieldA!.Name); + Assert.AreEqual("_serviceAApiVersion", fieldA!.Name); // Should return the matching field for ServiceB var fieldB = clientProvider.GetApiVersionFieldForService("Sample.ServiceB"); Assert.IsNotNull(fieldB); - Assert.AreEqual("_sampleServiceBApiVersion", fieldB!.Name); + Assert.AreEqual("_serviceBApiVersion", fieldB!.Name); } [Test] @@ -3814,11 +3814,11 @@ public void GetApiVersionFieldForService_MultiService_CaseInsensitiveMatch() // Should match case-insensitively var fieldLowerCase = clientProvider!.GetApiVersionFieldForService("sample.serviceA"); Assert.IsNotNull(fieldLowerCase); - Assert.AreEqual("_sampleServiceAApiVersion", fieldLowerCase!.Name); + Assert.AreEqual("_serviceAApiVersion", fieldLowerCase!.Name); var fieldUpperCase = clientProvider.GetApiVersionFieldForService("SAMPLE.SERVICEa"); Assert.IsNotNull(fieldUpperCase); - Assert.AreEqual("_sampleServiceAApiVersion", fieldUpperCase!.Name); + Assert.AreEqual("_serviceAApiVersion", fieldUpperCase!.Name); } [Test] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_GeneratesExpectedClient.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_GeneratesExpectedClient.cs index 1c2e1a29911..7aa404c1406 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_GeneratesExpectedClient.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_GeneratesExpectedClient.cs @@ -14,8 +14,8 @@ public partial class TestClient { private readonly global::System.Uri _endpoint; private readonly string _subscriptionId; - private readonly string _sampleServiceAApiVersion; - private readonly string _sampleServiceBApiVersion; + private readonly string _serviceAApiVersion; + private readonly string _serviceBApiVersion; private global::Sample.ServiceA.ServiceA _cachedServiceA; private global::Sample.ServiceB.ServiceB _cachedServiceB; @@ -44,8 +44,8 @@ internal TestClient(global::System.ClientModel.Primitives.AuthenticationPolicy a { Pipeline = global::System.ClientModel.Primitives.ClientPipeline.Create(options, Array.Empty(), new global::System.ClientModel.Primitives.PipelinePolicy[] { new global::System.ClientModel.Primitives.UserAgentPolicy(typeof(global::Sample.TestClient).Assembly) }, Array.Empty()); } - _sampleServiceAApiVersion = options.SampleServiceAApiVersion; - _sampleServiceBApiVersion = options.SampleServiceBApiVersion; + _serviceAApiVersion = options.ServiceAApiVersion; + _serviceBApiVersion = options.ServiceBApiVersion; } public TestClient(global::System.Uri endpoint, string subscriptionId, global::Sample.TestClientOptions options) : this(null, endpoint, subscriptionId, options) @@ -56,12 +56,12 @@ public TestClient(global::System.Uri endpoint, string subscriptionId, global::Sa public virtual global::Sample.ServiceA.ServiceA GetServiceAClient() { - return (global::System.Threading.Volatile.Read(ref _cachedServiceA) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedServiceA, new global::Sample.ServiceA.ServiceA(Pipeline, _endpoint, _sampleServiceAApiVersion, _subscriptionId), null) ?? _cachedServiceA)); + return (global::System.Threading.Volatile.Read(ref _cachedServiceA) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedServiceA, new global::Sample.ServiceA.ServiceA(Pipeline, _endpoint, _serviceAApiVersion, _subscriptionId), null) ?? _cachedServiceA)); } public virtual global::Sample.ServiceB.ServiceB GetServiceBClient() { - return (global::System.Threading.Volatile.Read(ref _cachedServiceB) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedServiceB, new global::Sample.ServiceB.ServiceB(Pipeline, _endpoint, _sampleServiceBApiVersion, _subscriptionId), null) ?? _cachedServiceB)); + return (global::System.Threading.Volatile.Read(ref _cachedServiceB) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedServiceB, new global::Sample.ServiceB.ServiceB(Pipeline, _endpoint, _serviceBApiVersion, _subscriptionId), null) ?? _cachedServiceB)); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClient.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClient.cs index bc0774b1cce..f0dc227b3eb 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClient.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClient.cs @@ -15,9 +15,9 @@ public partial class TestClient { private readonly global::System.Uri _endpoint; private readonly string _subscriptionId; - private readonly string _sampleComputeApiVersion; - private readonly string _sampleKeyVaultApiVersion; - private readonly string _sampleStorageApiVersion; + private readonly string _computeApiVersion; + private readonly string _keyVaultApiVersion; + private readonly string _storageApiVersion; private global::Sample.KeyVault.KeyVault _cachedKeyVault; private global::Sample.Storage.Storage _cachedStorage; private global::Sample.Compute.Compute _cachedCompute; @@ -47,9 +47,9 @@ internal TestClient(global::System.ClientModel.Primitives.AuthenticationPolicy a { Pipeline = global::System.ClientModel.Primitives.ClientPipeline.Create(options, Array.Empty(), new global::System.ClientModel.Primitives.PipelinePolicy[] { new global::System.ClientModel.Primitives.UserAgentPolicy(typeof(global::Sample.TestClient).Assembly) }, Array.Empty()); } - _sampleComputeApiVersion = options.SampleComputeApiVersion; - _sampleKeyVaultApiVersion = options.SampleKeyVaultApiVersion; - _sampleStorageApiVersion = options.SampleStorageApiVersion; + _computeApiVersion = options.ComputeApiVersion; + _keyVaultApiVersion = options.KeyVaultApiVersion; + _storageApiVersion = options.StorageApiVersion; } public TestClient(global::System.Uri endpoint, string subscriptionId, global::Sample.TestClientOptions options) : this(null, endpoint, subscriptionId, options) @@ -60,17 +60,17 @@ public TestClient(global::System.Uri endpoint, string subscriptionId, global::Sa public virtual global::Sample.KeyVault.KeyVault GetKeyVaultClient() { - return (global::System.Threading.Volatile.Read(ref _cachedKeyVault) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedKeyVault, new global::Sample.KeyVault.KeyVault(Pipeline, _endpoint, _sampleKeyVaultApiVersion, _subscriptionId), null) ?? _cachedKeyVault)); + return (global::System.Threading.Volatile.Read(ref _cachedKeyVault) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedKeyVault, new global::Sample.KeyVault.KeyVault(Pipeline, _endpoint, _keyVaultApiVersion, _subscriptionId), null) ?? _cachedKeyVault)); } public virtual global::Sample.Storage.Storage GetStorageClient() { - return (global::System.Threading.Volatile.Read(ref _cachedStorage) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedStorage, new global::Sample.Storage.Storage(Pipeline, _endpoint, _sampleStorageApiVersion, _subscriptionId), null) ?? _cachedStorage)); + return (global::System.Threading.Volatile.Read(ref _cachedStorage) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedStorage, new global::Sample.Storage.Storage(Pipeline, _endpoint, _storageApiVersion, _subscriptionId), null) ?? _cachedStorage)); } public virtual global::Sample.Compute.Compute GetComputeClient() { - return (global::System.Threading.Volatile.Read(ref _cachedCompute) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedCompute, new global::Sample.Compute.Compute(Pipeline, _endpoint, _sampleComputeApiVersion, _subscriptionId), null) ?? _cachedCompute)); + return (global::System.Threading.Volatile.Read(ref _cachedCompute) ?? (global::System.Threading.Interlocked.CompareExchange(ref _cachedCompute, new global::Sample.Compute.Compute(Pipeline, _endpoint, _computeApiVersion, _subscriptionId), null) ?? _cachedCompute)); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedClient.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedClient.cs index cd29a07eb89..56c1c6cf460 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedClient.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedClient.cs @@ -13,8 +13,8 @@ namespace Sample public partial class TestClient { private readonly global::System.Uri _endpoint; - private readonly string _sampleServiceAApiVersion; - private readonly string _sampleServiceBApiVersion; + private readonly string _serviceAApiVersion; + private readonly string _serviceBApiVersion; protected TestClient() { @@ -39,8 +39,8 @@ internal TestClient(global::System.ClientModel.Primitives.AuthenticationPolicy a { Pipeline = global::System.ClientModel.Primitives.ClientPipeline.Create(options, Array.Empty(), new global::System.ClientModel.Primitives.PipelinePolicy[] { new global::System.ClientModel.Primitives.UserAgentPolicy(typeof(global::Sample.TestClient).Assembly) }, Array.Empty()); } - _sampleServiceAApiVersion = options.SampleServiceAApiVersion; - _sampleServiceBApiVersion = options.SampleServiceBApiVersion; + _serviceAApiVersion = options.ServiceAApiVersion; + _serviceBApiVersion = options.ServiceBApiVersion; } public TestClient(global::System.Uri endpoint, global::Sample.TestClientOptions options) : this(null, endpoint, options) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClient.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClient.cs index f9684cf2336..7a3f23c4d82 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClient.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/TestData/ClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClient.cs @@ -13,9 +13,9 @@ namespace Sample public partial class TestClient { private readonly global::System.Uri _endpoint; - private readonly string _sampleComputeApiVersion; - private readonly string _sampleKeyVaultApiVersion; - private readonly string _sampleStorageApiVersion; + private readonly string _computeApiVersion; + private readonly string _keyVaultApiVersion; + private readonly string _storageApiVersion; protected TestClient() { @@ -40,9 +40,9 @@ internal TestClient(global::System.ClientModel.Primitives.AuthenticationPolicy a { Pipeline = global::System.ClientModel.Primitives.ClientPipeline.Create(options, Array.Empty(), new global::System.ClientModel.Primitives.PipelinePolicy[] { new global::System.ClientModel.Primitives.UserAgentPolicy(typeof(global::Sample.TestClient).Assembly) }, Array.Empty()); } - _sampleComputeApiVersion = options.SampleComputeApiVersion; - _sampleKeyVaultApiVersion = options.SampleKeyVaultApiVersion; - _sampleStorageApiVersion = options.SampleStorageApiVersion; + _computeApiVersion = options.ComputeApiVersion; + _keyVaultApiVersion = options.KeyVaultApiVersion; + _storageApiVersion = options.StorageApiVersion; } public TestClient(global::System.Uri endpoint, global::Sample.TestClientOptions options) : this(null, endpoint, options) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedRestClient.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedRestClient.cs index a0a9bd218e1..c083bf8f50e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedRestClient.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_GeneratesExpectedRestClient.cs @@ -16,7 +16,7 @@ public partial class TestClient { global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder(); uri.Reset(_endpoint); - uri.AppendQuery("apiVersion", _sampleServiceAApiVersion, true); + uri.AppendQuery("apiVersion", _serviceAApiVersion, true); global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage(uri.ToUri(), "GET", PipelineMessageClassifier200); global::System.ClientModel.Primitives.PipelineRequest request = message.Request; message.Apply(options); @@ -27,7 +27,7 @@ public partial class TestClient { global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder(); uri.Reset(_endpoint); - uri.AppendQuery("apiVersion", _sampleServiceBApiVersion, true); + uri.AppendQuery("apiVersion", _serviceBApiVersion, true); global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage(uri.ToUri(), "GET", PipelineMessageClassifier200); global::System.ClientModel.Primitives.PipelineRequest request = message.Request; message.Apply(options); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedRestClient.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedRestClient.cs index b11b9e12462..15cc22e0e14 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedRestClient.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/TestData/RestClientProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedRestClient.cs @@ -16,7 +16,7 @@ public partial class TestClient { global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder(); uri.Reset(_endpoint); - uri.AppendQuery("apiVersion", _sampleKeyVaultApiVersion, true); + uri.AppendQuery("apiVersion", _keyVaultApiVersion, true); global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage(uri.ToUri(), "GET", PipelineMessageClassifier200); global::System.ClientModel.Primitives.PipelineRequest request = message.Request; message.Apply(options); @@ -27,7 +27,7 @@ public partial class TestClient { global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder(); uri.Reset(_endpoint); - uri.AppendQuery("apiVersion", _sampleStorageApiVersion, true); + uri.AppendQuery("apiVersion", _storageApiVersion, true); global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage(uri.ToUri(), "GET", PipelineMessageClassifier200); global::System.ClientModel.Primitives.PipelineRequest request = message.Request; message.Apply(options); @@ -38,7 +38,7 @@ public partial class TestClient { global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder(); uri.Reset(_endpoint); - uri.AppendQuery("apiVersion", _sampleComputeApiVersion, true); + uri.AppendQuery("apiVersion", _computeApiVersion, true); global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage(uri.ToUri(), "GET", PipelineMessageClassifier200); global::System.ClientModel.Primitives.PipelineRequest request = message.Request; message.Apply(options); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs index aea43de4818..1eb960fa71f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs @@ -16,13 +16,13 @@ public partial class TestClientOptions : global::System.ClientModel.Primitives.C public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) { - SampleServiceAApiVersion = serviceAVersion switch + ServiceAApiVersion = serviceAVersion switch { global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - SampleServiceBApiVersion = serviceBVersion switch + ServiceBApiVersion = serviceBVersion switch { global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", @@ -33,25 +33,25 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion servic [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleServiceAApiVersion = "2.0"; - SampleServiceBApiVersion = "4.0"; + ServiceAApiVersion = "2.0"; + ServiceBApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleServiceAApiVersion"] is string sampleServiceAApiVersion)) + if ((section["ServiceAApiVersion"] is string serviceAApiVersion)) { - this.SampleServiceAApiVersion = sampleServiceAApiVersion; + this.ServiceAApiVersion = serviceAApiVersion; } - if ((section["SampleServiceBApiVersion"] is string sampleServiceBApiVersion)) + if ((section["ServiceBApiVersion"] is string serviceBApiVersion)) { - this.SampleServiceBApiVersion = sampleServiceBApiVersion; + this.ServiceBApiVersion = serviceBApiVersion; } } - internal string SampleServiceAApiVersion { get; } + internal string ServiceAApiVersion { get; } - internal string SampleServiceBApiVersion { get; } + internal string ServiceBApiVersion { get; } public enum ServiceAVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs index ebba2f7aebd..448385c36db 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,25 +11,25 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ServiceComputeVersion LatestServiceComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestServiceKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.ServiceStorageVersion LatestServiceStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ServiceComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; + private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; + private const global::Sample.TestClientOptions.ServiceStorageVersion LatestStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestServiceKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestServiceStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestServiceComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestComputeVersion) { - SampleKeyVaultApiVersion = serviceKeyVaultVersion switch + KeyVaultApiVersion = serviceKeyVaultVersion switch { global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - SampleStorageApiVersion = serviceStorageVersion switch + StorageApiVersion = serviceStorageVersion switch { global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - SampleComputeApiVersion = serviceComputeVersion switch + ComputeApiVersion = serviceComputeVersion switch { global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", @@ -41,32 +41,32 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleComputeApiVersion = "2024-07-01"; - SampleKeyVaultApiVersion = "7.5"; - SampleStorageApiVersion = "2024-01-01"; + ComputeApiVersion = "2024-07-01"; + KeyVaultApiVersion = "7.5"; + StorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleComputeApiVersion"] is string sampleComputeApiVersion)) + if ((section["ComputeApiVersion"] is string computeApiVersion)) { - this.SampleComputeApiVersion = sampleComputeApiVersion; + this.ComputeApiVersion = computeApiVersion; } - if ((section["SampleKeyVaultApiVersion"] is string sampleKeyVaultApiVersion)) + if ((section["KeyVaultApiVersion"] is string keyVaultApiVersion)) { - this.SampleKeyVaultApiVersion = sampleKeyVaultApiVersion; + this.KeyVaultApiVersion = keyVaultApiVersion; } - if ((section["SampleStorageApiVersion"] is string sampleStorageApiVersion)) + if ((section["StorageApiVersion"] is string storageApiVersion)) { - this.SampleStorageApiVersion = sampleStorageApiVersion; + this.StorageApiVersion = storageApiVersion; } } - internal string SampleComputeApiVersion { get; } + internal string ComputeApiVersion { get; } - internal string SampleKeyVaultApiVersion { get; } + internal string KeyVaultApiVersion { get; } - internal string SampleStorageApiVersion { get; } + internal string StorageApiVersion { get; } public enum ServiceComputeVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs index aea43de4818..1eb960fa71f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs @@ -16,13 +16,13 @@ public partial class TestClientOptions : global::System.ClientModel.Primitives.C public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) { - SampleServiceAApiVersion = serviceAVersion switch + ServiceAApiVersion = serviceAVersion switch { global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - SampleServiceBApiVersion = serviceBVersion switch + ServiceBApiVersion = serviceBVersion switch { global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", @@ -33,25 +33,25 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion servic [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleServiceAApiVersion = "2.0"; - SampleServiceBApiVersion = "4.0"; + ServiceAApiVersion = "2.0"; + ServiceBApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleServiceAApiVersion"] is string sampleServiceAApiVersion)) + if ((section["ServiceAApiVersion"] is string serviceAApiVersion)) { - this.SampleServiceAApiVersion = sampleServiceAApiVersion; + this.ServiceAApiVersion = serviceAApiVersion; } - if ((section["SampleServiceBApiVersion"] is string sampleServiceBApiVersion)) + if ((section["ServiceBApiVersion"] is string serviceBApiVersion)) { - this.SampleServiceBApiVersion = sampleServiceBApiVersion; + this.ServiceBApiVersion = serviceBApiVersion; } } - internal string SampleServiceAApiVersion { get; } + internal string ServiceAApiVersion { get; } - internal string SampleServiceBApiVersion { get; } + internal string ServiceBApiVersion { get; } public enum ServiceAVersion { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs index ebba2f7aebd..448385c36db 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,25 +11,25 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ServiceComputeVersion LatestServiceComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestServiceKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.ServiceStorageVersion LatestServiceStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ServiceComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; + private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; + private const global::Sample.TestClientOptions.ServiceStorageVersion LatestStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestServiceKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestServiceStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestServiceComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestComputeVersion) { - SampleKeyVaultApiVersion = serviceKeyVaultVersion switch + KeyVaultApiVersion = serviceKeyVaultVersion switch { global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - SampleStorageApiVersion = serviceStorageVersion switch + StorageApiVersion = serviceStorageVersion switch { global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - SampleComputeApiVersion = serviceComputeVersion switch + ComputeApiVersion = serviceComputeVersion switch { global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", @@ -41,32 +41,32 @@ public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - SampleComputeApiVersion = "2024-07-01"; - SampleKeyVaultApiVersion = "7.5"; - SampleStorageApiVersion = "2024-01-01"; + ComputeApiVersion = "2024-07-01"; + KeyVaultApiVersion = "7.5"; + StorageApiVersion = "2024-01-01"; if (((section is null) || !section.Exists())) { return; } - if ((section["SampleComputeApiVersion"] is string sampleComputeApiVersion)) + if ((section["ComputeApiVersion"] is string computeApiVersion)) { - this.SampleComputeApiVersion = sampleComputeApiVersion; + this.ComputeApiVersion = computeApiVersion; } - if ((section["SampleKeyVaultApiVersion"] is string sampleKeyVaultApiVersion)) + if ((section["KeyVaultApiVersion"] is string keyVaultApiVersion)) { - this.SampleKeyVaultApiVersion = sampleKeyVaultApiVersion; + this.KeyVaultApiVersion = keyVaultApiVersion; } - if ((section["SampleStorageApiVersion"] is string sampleStorageApiVersion)) + if ((section["StorageApiVersion"] is string storageApiVersion)) { - this.SampleStorageApiVersion = sampleStorageApiVersion; + this.StorageApiVersion = storageApiVersion; } } - internal string SampleComputeApiVersion { get; } + internal string ComputeApiVersion { get; } - internal string SampleKeyVaultApiVersion { get; } + internal string KeyVaultApiVersion { get; } - internal string SampleStorageApiVersion { get; } + internal string StorageApiVersion { get; } public enum ServiceComputeVersion { From e81dc128504714e0b76d591a40ec0f33d84517e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 20:04:48 +0000 Subject: [PATCH 07/10] test: add TestData validation to MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/d68247ba-74e0-495e-9cd6-961dcb2cd21b --- .../Providers/ClientOptionsProviderTests.cs | 5 ++ ...eLastSegment_ProducesUniqueVersionEnums.cs | 68 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs index f65378c270f..ec41b410935 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs @@ -752,6 +752,11 @@ public void MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums() var nestedTypes = clientOptionsProvider!.NestedTypes; Assert.AreEqual(2, nestedTypes.Count); CollectionAssert.AllItemsAreUnique(nestedTypes.Select(t => t.Name).ToList()); + + var writer = new TypeProviderWriter(clientOptionsProvider!); + var file = writer.Write(); + + Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); } [Test] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs new file mode 100644 index 00000000000..5e24fd80aa3 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs @@ -0,0 +1,68 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Configuration; + +namespace Sample +{ + public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions + { + private const global::Sample.TestClientOptions.AzureServiceOneTestsVersion LatestAzureServiceOneTestsVersion = global::Sample.TestClientOptions.AzureServiceOneTestsVersion.V2_0; + private const global::Sample.TestClientOptions.AzureServiceTwoTestsVersion LatestAzureServiceTwoTestsVersion = global::Sample.TestClientOptions.AzureServiceTwoTestsVersion.V4_0; + + public TestClientOptions(global::Sample.TestClientOptions.AzureServiceOneTestsVersion azureServiceOneTestsVersion = LatestAzureServiceOneTestsVersion, global::Sample.TestClientOptions.AzureServiceTwoTestsVersion azureServiceTwoTestsVersion = LatestAzureServiceTwoTestsVersion) + { + AzureServiceOneTestsApiVersion = azureServiceOneTestsVersion switch + { + global::Sample.TestClientOptions.AzureServiceOneTestsVersion.V1_0 => "1.0", + global::Sample.TestClientOptions.AzureServiceOneTestsVersion.V2_0 => "2.0", + _ => throw new global::System.NotSupportedException() + }; + AzureServiceTwoTestsApiVersion = azureServiceTwoTestsVersion switch + { + global::Sample.TestClientOptions.AzureServiceTwoTestsVersion.V3_0 => "3.0", + global::Sample.TestClientOptions.AzureServiceTwoTestsVersion.V4_0 => "4.0", + _ => throw new global::System.NotSupportedException() + }; + } + + [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] + internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) + { + AzureServiceOneTestsApiVersion = "2.0"; + AzureServiceTwoTestsApiVersion = "4.0"; + if (((section is null) || !section.Exists())) + { + return; + } + if ((section["AzureServiceOneTestsApiVersion"] is string azureServiceOneTestsApiVersion)) + { + this.AzureServiceOneTestsApiVersion = azureServiceOneTestsApiVersion; + } + if ((section["AzureServiceTwoTestsApiVersion"] is string azureServiceTwoTestsApiVersion)) + { + this.AzureServiceTwoTestsApiVersion = azureServiceTwoTestsApiVersion; + } + } + + internal string AzureServiceOneTestsApiVersion { get; } + + internal string AzureServiceTwoTestsApiVersion { get; } + + public enum AzureServiceOneTestsVersion + { + V1_0 = 1, + V2_0 = 2 + } + + public enum AzureServiceTwoTestsVersion + { + V3_0 = 1, + V4_0 = 2 + } + } +} From 352b1dad9cd6512b130bfbe4d174b1fa92104366 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 20:32:28 +0000 Subject: [PATCH 08/10] refactor: change multi-service enum naming from ServiceXVersion to XServiceVersion Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/1abc462d-6e01-4045-857d-007426a4d797 --- ...ceClient_GeneratesExpectedClientOptions.cs | 22 ++++++------ ...Services_GeneratesExpectedClientOptions.cs | 34 +++++++++---------- ...edClient_GeneratesExpectedClientOptions.cs | 22 ++++++------ ...Services_GeneratesExpectedClientOptions.cs | 34 +++++++++---------- .../src/Providers/ApiVersionEnumProvider.cs | 2 +- .../ApiVersionEnumProviderTests.cs | 4 +-- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs index 1eb960fa71f..cbb435387e8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_GeneratesExpectedClientOptions.cs @@ -11,21 +11,21 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ServiceAVersion LatestServiceAVersion = global::Sample.TestClientOptions.ServiceAVersion.V2_0; - private const global::Sample.TestClientOptions.ServiceBVersion LatestServiceBVersion = global::Sample.TestClientOptions.ServiceBVersion.V4_0; + private const global::Sample.TestClientOptions.ServiceAServiceVersion LatestServiceAVersion = global::Sample.TestClientOptions.ServiceAServiceVersion.V2_0; + private const global::Sample.TestClientOptions.ServiceBServiceVersion LatestServiceBVersion = global::Sample.TestClientOptions.ServiceBServiceVersion.V4_0; - public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceAServiceVersion serviceAServiceVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBServiceVersion serviceBServiceVersion = LatestServiceBVersion) { - ServiceAApiVersion = serviceAVersion switch + ServiceAApiVersion = serviceAServiceVersion switch { - global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", - global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", + global::Sample.TestClientOptions.ServiceAServiceVersion.V1_0 => "1.0", + global::Sample.TestClientOptions.ServiceAServiceVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - ServiceBApiVersion = serviceBVersion switch + ServiceBApiVersion = serviceBServiceVersion switch { - global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", - global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", + global::Sample.TestClientOptions.ServiceBServiceVersion.V3_0 => "3.0", + global::Sample.TestClientOptions.ServiceBServiceVersion.V4_0 => "4.0", _ => throw new global::System.NotSupportedException() }; } @@ -53,13 +53,13 @@ internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigura internal string ServiceBApiVersion { get; } - public enum ServiceAVersion + public enum ServiceAServiceVersion { V1_0 = 1, V2_0 = 2 } - public enum ServiceBVersion + public enum ServiceBServiceVersion { V3_0 = 1, V4_0 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 448385c36db..37011ea3530 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,29 +11,29 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ServiceComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.ServiceStorageVersion LatestStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ComputeServiceVersion LatestComputeVersion = global::Sample.TestClientOptions.ComputeServiceVersion.V2024_07_01; + private const global::Sample.TestClientOptions.KeyVaultServiceVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.KeyVaultServiceVersion.V7_5; + private const global::Sample.TestClientOptions.StorageServiceVersion LatestStorageVersion = global::Sample.TestClientOptions.StorageServiceVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.KeyVaultServiceVersion keyVaultServiceVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.StorageServiceVersion storageServiceVersion = LatestStorageVersion, global::Sample.TestClientOptions.ComputeServiceVersion computeServiceVersion = LatestComputeVersion) { - KeyVaultApiVersion = serviceKeyVaultVersion switch + KeyVaultApiVersion = keyVaultServiceVersion switch { - global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", - global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", + global::Sample.TestClientOptions.KeyVaultServiceVersion.V7_4 => "7.4", + global::Sample.TestClientOptions.KeyVaultServiceVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - StorageApiVersion = serviceStorageVersion switch + StorageApiVersion = storageServiceVersion switch { - global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", - global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", + global::Sample.TestClientOptions.StorageServiceVersion.V2023_01_01 => "2023-01-01", + global::Sample.TestClientOptions.StorageServiceVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - ComputeApiVersion = serviceComputeVersion switch + ComputeApiVersion = computeServiceVersion switch { - global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", - global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", - global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01 => "2024-07-01", + global::Sample.TestClientOptions.ComputeServiceVersion.V2023_07_01 => "2023-07-01", + global::Sample.TestClientOptions.ComputeServiceVersion.V2024_03_01 => "2024-03-01", + global::Sample.TestClientOptions.ComputeServiceVersion.V2024_07_01 => "2024-07-01", _ => throw new global::System.NotSupportedException() }; } @@ -68,20 +68,20 @@ internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigura internal string StorageApiVersion { get; } - public enum ServiceComputeVersion + public enum ComputeServiceVersion { V2023_07_01 = 1, V2024_03_01 = 2, V2024_07_01 = 3 } - public enum ServiceKeyVaultVersion + public enum KeyVaultServiceVersion { V7_4 = 1, V7_5 = 2 } - public enum ServiceStorageVersion + public enum StorageServiceVersion { V2023_01_01 = 1, V2024_01_01 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs index 1eb960fa71f..cbb435387e8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_GeneratesExpectedClientOptions.cs @@ -11,21 +11,21 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ServiceAVersion LatestServiceAVersion = global::Sample.TestClientOptions.ServiceAVersion.V2_0; - private const global::Sample.TestClientOptions.ServiceBVersion LatestServiceBVersion = global::Sample.TestClientOptions.ServiceBVersion.V4_0; + private const global::Sample.TestClientOptions.ServiceAServiceVersion LatestServiceAVersion = global::Sample.TestClientOptions.ServiceAServiceVersion.V2_0; + private const global::Sample.TestClientOptions.ServiceBServiceVersion LatestServiceBVersion = global::Sample.TestClientOptions.ServiceBServiceVersion.V4_0; - public TestClientOptions(global::Sample.TestClientOptions.ServiceAVersion serviceAVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBVersion serviceBVersion = LatestServiceBVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceAServiceVersion serviceAServiceVersion = LatestServiceAVersion, global::Sample.TestClientOptions.ServiceBServiceVersion serviceBServiceVersion = LatestServiceBVersion) { - ServiceAApiVersion = serviceAVersion switch + ServiceAApiVersion = serviceAServiceVersion switch { - global::Sample.TestClientOptions.ServiceAVersion.V1_0 => "1.0", - global::Sample.TestClientOptions.ServiceAVersion.V2_0 => "2.0", + global::Sample.TestClientOptions.ServiceAServiceVersion.V1_0 => "1.0", + global::Sample.TestClientOptions.ServiceAServiceVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - ServiceBApiVersion = serviceBVersion switch + ServiceBApiVersion = serviceBServiceVersion switch { - global::Sample.TestClientOptions.ServiceBVersion.V3_0 => "3.0", - global::Sample.TestClientOptions.ServiceBVersion.V4_0 => "4.0", + global::Sample.TestClientOptions.ServiceBServiceVersion.V3_0 => "3.0", + global::Sample.TestClientOptions.ServiceBServiceVersion.V4_0 => "4.0", _ => throw new global::System.NotSupportedException() }; } @@ -53,13 +53,13 @@ internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigura internal string ServiceBApiVersion { get; } - public enum ServiceAVersion + public enum ServiceAServiceVersion { V1_0 = 1, V2_0 = 2 } - public enum ServiceBVersion + public enum ServiceBServiceVersion { V3_0 = 1, V4_0 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs index 448385c36db..37011ea3530 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceCombinedClient_WithThreeServices_GeneratesExpectedClientOptions.cs @@ -11,29 +11,29 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.ServiceComputeVersion LatestComputeVersion = global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01; - private const global::Sample.TestClientOptions.ServiceKeyVaultVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5; - private const global::Sample.TestClientOptions.ServiceStorageVersion LatestStorageVersion = global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01; + private const global::Sample.TestClientOptions.ComputeServiceVersion LatestComputeVersion = global::Sample.TestClientOptions.ComputeServiceVersion.V2024_07_01; + private const global::Sample.TestClientOptions.KeyVaultServiceVersion LatestKeyVaultVersion = global::Sample.TestClientOptions.KeyVaultServiceVersion.V7_5; + private const global::Sample.TestClientOptions.StorageServiceVersion LatestStorageVersion = global::Sample.TestClientOptions.StorageServiceVersion.V2024_01_01; - public TestClientOptions(global::Sample.TestClientOptions.ServiceKeyVaultVersion serviceKeyVaultVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.ServiceStorageVersion serviceStorageVersion = LatestStorageVersion, global::Sample.TestClientOptions.ServiceComputeVersion serviceComputeVersion = LatestComputeVersion) + public TestClientOptions(global::Sample.TestClientOptions.KeyVaultServiceVersion keyVaultServiceVersion = LatestKeyVaultVersion, global::Sample.TestClientOptions.StorageServiceVersion storageServiceVersion = LatestStorageVersion, global::Sample.TestClientOptions.ComputeServiceVersion computeServiceVersion = LatestComputeVersion) { - KeyVaultApiVersion = serviceKeyVaultVersion switch + KeyVaultApiVersion = keyVaultServiceVersion switch { - global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_4 => "7.4", - global::Sample.TestClientOptions.ServiceKeyVaultVersion.V7_5 => "7.5", + global::Sample.TestClientOptions.KeyVaultServiceVersion.V7_4 => "7.4", + global::Sample.TestClientOptions.KeyVaultServiceVersion.V7_5 => "7.5", _ => throw new global::System.NotSupportedException() }; - StorageApiVersion = serviceStorageVersion switch + StorageApiVersion = storageServiceVersion switch { - global::Sample.TestClientOptions.ServiceStorageVersion.V2023_01_01 => "2023-01-01", - global::Sample.TestClientOptions.ServiceStorageVersion.V2024_01_01 => "2024-01-01", + global::Sample.TestClientOptions.StorageServiceVersion.V2023_01_01 => "2023-01-01", + global::Sample.TestClientOptions.StorageServiceVersion.V2024_01_01 => "2024-01-01", _ => throw new global::System.NotSupportedException() }; - ComputeApiVersion = serviceComputeVersion switch + ComputeApiVersion = computeServiceVersion switch { - global::Sample.TestClientOptions.ServiceComputeVersion.V2023_07_01 => "2023-07-01", - global::Sample.TestClientOptions.ServiceComputeVersion.V2024_03_01 => "2024-03-01", - global::Sample.TestClientOptions.ServiceComputeVersion.V2024_07_01 => "2024-07-01", + global::Sample.TestClientOptions.ComputeServiceVersion.V2023_07_01 => "2023-07-01", + global::Sample.TestClientOptions.ComputeServiceVersion.V2024_03_01 => "2024-03-01", + global::Sample.TestClientOptions.ComputeServiceVersion.V2024_07_01 => "2024-07-01", _ => throw new global::System.NotSupportedException() }; } @@ -68,20 +68,20 @@ internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigura internal string StorageApiVersion { get; } - public enum ServiceComputeVersion + public enum ComputeServiceVersion { V2023_07_01 = 1, V2024_03_01 = 2, V2024_07_01 = 3 } - public enum ServiceKeyVaultVersion + public enum KeyVaultServiceVersion { V7_4 = 1, V7_5 = 2 } - public enum ServiceStorageVersion + public enum StorageServiceVersion { V2023_01_01 = 1, V2024_01_01 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs index cbcc6cd648e..47ef2e2b96a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs @@ -43,7 +43,7 @@ protected override string BuildName() // last segment; otherwise, use BuildNameForService with the last segment. return ClientHelper.HasLastSegmentCollision(serviceNamespace, _inputEnum, apiVersionEnums) ? $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}" - : ClientHelper.BuildNameForService(serviceNamespace, ServicePrefix, VersionSuffix); + : ClientHelper.BuildNameForService(serviceNamespace, string.Empty, ApiVersionEnumName); } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs index bde61723da4..5688137d9fd 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs @@ -180,8 +180,8 @@ public void MultiServiceClient_WithMultipleApiVersionEnums_GeneratesCorrectEnumN var storageProvider = (ApiVersionEnumProvider)storageEnumType; // Verify enum names use BuildNameForService when there are no collisions - Assert.AreEqual("ServiceKeyVaultVersion", keyVaultProvider.Name); - Assert.AreEqual("ServiceStorageVersion", storageProvider.Name); + Assert.AreEqual("KeyVaultServiceVersion", keyVaultProvider.Name); + Assert.AreEqual("StorageServiceVersion", storageProvider.Name); } [Test] From 1d1db57359892c6a5a27403288b822b2a3a782f1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:12:56 +0000 Subject: [PATCH 09/10] fix: handle same-namespace collision in multi-service API version naming; add original issue #10055 tests Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/af6998c3-f2e0-408c-a60e-efe47635b696 --- .../src/Providers/ClientOptionsProvider.cs | 38 +++-- .../Providers/ClientOptionsProviderTests.cs | 160 ++++++++++++++++++ ...nalIssueSpec_ProducesUniqueVersionEnums.cs | 64 +++++++ ...ameNamespace_ProducesUniqueVersionEnums.cs | 64 +++++++ .../src/Providers/ApiVersionEnumProvider.cs | 22 ++- .../src/Shared/ClientHelper.cs | 15 ++ 6 files changed, 345 insertions(+), 18 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index d7af600dd76..c42e6cd64df 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -124,7 +124,7 @@ private static bool UseSingletonInstance(InputClient inputClient) internal IReadOnlyDictionary? VersionProperties => field ??= BuildVersionProperties(); - private Dictionary? BuildVersionProperties() + private Dictionary? BuildVersionProperties() { if (_serviceVersionsEnums is null) { @@ -142,12 +142,18 @@ private static bool UseSingletonInstance(InputClient inputClient) else { var serviceNamespace = inputEnum.Namespace; - // Use the full namespace only when the last segment collides; - // otherwise, use BuildNameForService for shorter names. - versionPropertyName = !string.IsNullOrEmpty(serviceNamespace) && - ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) - ? $"{serviceNamespace.ToIdentifierName()}{ApiVersionSuffix}" - : ClientHelper.BuildNameForService(serviceNamespace ?? string.Empty, string.Empty, ApiVersionSuffix); + if (!string.IsNullOrEmpty(serviceNamespace) && + ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys)) + { + // Last segment collides — use the full namespace. + versionPropertyName = ClientHelper.HasFullNamespaceCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) + ? $"{serviceNamespace.ToIdentifierName()}{inputEnum.Name.ToIdentifierName()}{ApiVersionSuffix}" + : $"{serviceNamespace.ToIdentifierName()}{ApiVersionSuffix}"; + } + else + { + versionPropertyName = ClientHelper.BuildNameForService(serviceNamespace ?? string.Empty, string.Empty, ApiVersionSuffix); + } } var versionProperty = new PropertyProvider( @@ -182,12 +188,18 @@ private static bool UseSingletonInstance(InputClient inputClient) else { var serviceNamespace = inputEnum.Namespace; - // Use the full namespace only when the last segment collides; - // otherwise, use BuildNameForService for shorter names. - fieldName = !string.IsNullOrEmpty(serviceNamespace) && - ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) - ? $"{LatestPrefix}{serviceNamespace.ToIdentifierName()}{VersionSuffix}" - : ClientHelper.BuildNameForService(serviceNamespace ?? string.Empty, LatestPrefix, VersionSuffix); + if (!string.IsNullOrEmpty(serviceNamespace) && + ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys)) + { + // Last segment collides — use the full namespace. + fieldName = ClientHelper.HasFullNamespaceCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) + ? $"{LatestPrefix}{serviceNamespace.ToIdentifierName()}{inputEnum.Name.ToIdentifierName()}{VersionSuffix}" + : $"{LatestPrefix}{serviceNamespace.ToIdentifierName()}{VersionSuffix}"; + } + else + { + fieldName = ClientHelper.BuildNameForService(serviceNamespace ?? string.Empty, LatestPrefix, VersionSuffix); + } } var field = new FieldProvider( modifiers: FieldModifiers.Private | FieldModifiers.Const, diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs index ec41b410935..4ddee3462bc 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs @@ -759,6 +759,166 @@ public void MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums() Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); } + [Test] + public void MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums() + { + // Simulates the spec from the original issue #10055: + // ServiceOne (namespace ServiceOne) with Versions enum (2024-01-01) + // ServiceTwo (namespace ServiceTwo) with Versions enum (2024-06-01) + // Combined in a multi-service client + List serviceOneVersions = ["2024-01-01"]; + List serviceTwoVersions = ["2024-06-01"]; + + var serviceOneEnumValues = serviceOneVersions.Select(a => (a, a)); + var serviceTwoEnumValues = serviceTwoVersions.Select(a => (a, a)); + + var serviceOneEnum = InputFactory.StringEnum( + "Versions", + serviceOneEnumValues, + usage: InputModelTypeUsage.ApiVersionEnum, + clientNamespace: "ServiceOne"); + var serviceTwoEnum = InputFactory.StringEnum( + "Versions", + serviceTwoEnumValues, + usage: InputModelTypeUsage.ApiVersionEnum, + clientNamespace: "ServiceTwo"); + + InputParameter apiVersionParameter = InputFactory.QueryParameter( + "apiVersion", + InputPrimitiveType.String, + isRequired: true, + scope: InputParameterScope.Client, + isApiVersion: true); + + var serviceOneOperation = InputFactory.Operation( + "ServiceOneOperation", + parameters: [apiVersionParameter], + ns: "ServiceOne"); + + var serviceTwoOperation = InputFactory.Operation( + "ServiceTwoOperation", + parameters: [apiVersionParameter], + ns: "ServiceTwo"); + + var client = InputFactory.Client( + "MultiServiceClient", + methods: + [ + InputFactory.BasicServiceMethod("ServiceOneMethod", serviceOneOperation), + InputFactory.BasicServiceMethod("ServiceTwoMethod", serviceTwoOperation) + ], + parameters: [apiVersionParameter], + isMultiServiceClient: true); + + MockHelpers.LoadMockGenerator( + apiVersions: () => [.. serviceOneVersions, .. serviceTwoVersions], + clients: () => [client], + inputEnums: () => [serviceOneEnum, serviceTwoEnum]); + + var clientProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(client); + Assert.IsNotNull(clientProvider); + + // Validate that Fields access does not crash (the original issue crashed here) + Assert.DoesNotThrow(() => _ = clientProvider!.Fields); + + // Validate that Methods access does not crash (original crash site: Fields.ToDictionary in BuildMethods) + Assert.DoesNotThrow(() => _ = clientProvider!.Methods); + + var clientOptionsProvider = clientProvider?.ClientOptions; + Assert.IsNotNull(clientOptionsProvider); + + // Validate nested service version enums have unique names + var nestedTypes = clientOptionsProvider!.NestedTypes; + Assert.AreEqual(2, nestedTypes.Count); + CollectionAssert.AllItemsAreUnique(nestedTypes.Select(t => t.Name).ToList()); + + // Verify enum names follow the XServiceVersion pattern + Assert.AreEqual("ServiceOneServiceVersion", nestedTypes[0].Name); + Assert.AreEqual("ServiceTwoServiceVersion", nestedTypes[1].Name); + + var writer = new TypeProviderWriter(clientOptionsProvider!); + var file = writer.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); + } + + [Test] + public void MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums() + { + // Regression test for the scenario where both enums share the exact same namespace + // (e.g., when tspconfig remaps both services to the same C# output namespace). + // This is the actual crash scenario from issue #10055 where the mgmt emitter + // remaps both ServiceOne.Versions and ServiceTwo.Versions to the same namespace. + List serviceOneVersions = ["2024-01-01"]; + List serviceTwoVersions = ["2024-06-01"]; + + var serviceOneEnumValues = serviceOneVersions.Select(a => (a, a)); + var serviceTwoEnumValues = serviceTwoVersions.Select(a => (a, a)); + + // Both enums have the EXACT SAME namespace (simulates tspconfig namespace override) + var serviceOneEnum = InputFactory.StringEnum( + "ServiceOneVersions", + serviceOneEnumValues, + usage: InputModelTypeUsage.ApiVersionEnum, + clientNamespace: "Azure.Generator.MgmtTypeSpec.MultiService.Tests"); + var serviceTwoEnum = InputFactory.StringEnum( + "ServiceTwoVersions", + serviceTwoEnumValues, + usage: InputModelTypeUsage.ApiVersionEnum, + clientNamespace: "Azure.Generator.MgmtTypeSpec.MultiService.Tests"); + + InputParameter apiVersionParameter = InputFactory.QueryParameter( + "apiVersion", + InputPrimitiveType.String, + isRequired: true, + scope: InputParameterScope.Client, + isApiVersion: true); + + var serviceOneOperation = InputFactory.Operation( + "ServiceOneOperation", + parameters: [apiVersionParameter], + ns: "Azure.Generator.MgmtTypeSpec.MultiService.Tests"); + + var serviceTwoOperation = InputFactory.Operation( + "ServiceTwoOperation", + parameters: [apiVersionParameter], + ns: "Azure.Generator.MgmtTypeSpec.MultiService.Tests"); + + var client = InputFactory.Client( + "MultiServiceClient", + methods: + [ + InputFactory.BasicServiceMethod("ServiceOneMethod", serviceOneOperation), + InputFactory.BasicServiceMethod("ServiceTwoMethod", serviceTwoOperation) + ], + parameters: [apiVersionParameter], + isMultiServiceClient: true); + + MockHelpers.LoadMockGenerator( + apiVersions: () => [.. serviceOneVersions, .. serviceTwoVersions], + clients: () => [client], + inputEnums: () => [serviceOneEnum, serviceTwoEnum]); + + var clientProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(client); + Assert.IsNotNull(clientProvider); + + // The key validation: accessing Fields and Methods must not crash + // (this was the original crash site from issue #10055) + Assert.DoesNotThrow(() => _ = clientProvider!.Fields); + Assert.DoesNotThrow(() => _ = clientProvider!.Methods); + + var clientOptionsProvider = clientProvider?.ClientOptions; + Assert.IsNotNull(clientOptionsProvider); + + // Validate nested service version enums have unique names + var nestedTypes = clientOptionsProvider!.NestedTypes; + Assert.AreEqual(2, nestedTypes.Count); + CollectionAssert.AllItemsAreUnique(nestedTypes.Select(t => t.Name).ToList()); + + var writer = new TypeProviderWriter(clientOptionsProvider!); + var file = writer.Write(); + Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); + } + [Test] public void TestConfigurationSectionConstructorBody_WithBoolProperty() { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs new file mode 100644 index 00000000000..ae41ff46731 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs @@ -0,0 +1,64 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Configuration; + +namespace Sample +{ + public partial class MultiServiceClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions + { + private const global::Sample.MultiServiceClientOptions.ServiceOneServiceVersion LatestServiceOneVersion = global::Sample.MultiServiceClientOptions.ServiceOneServiceVersion.V2024_01_01; + private const global::Sample.MultiServiceClientOptions.ServiceTwoServiceVersion LatestServiceTwoVersion = global::Sample.MultiServiceClientOptions.ServiceTwoServiceVersion.V2024_06_01; + + public MultiServiceClientOptions(global::Sample.MultiServiceClientOptions.ServiceOneServiceVersion serviceOneServiceVersion = LatestServiceOneVersion, global::Sample.MultiServiceClientOptions.ServiceTwoServiceVersion serviceTwoServiceVersion = LatestServiceTwoVersion) + { + ServiceOneApiVersion = serviceOneServiceVersion switch + { + global::Sample.MultiServiceClientOptions.ServiceOneServiceVersion.V2024_01_01 => "2024-01-01", + _ => throw new global::System.NotSupportedException() + }; + ServiceTwoApiVersion = serviceTwoServiceVersion switch + { + global::Sample.MultiServiceClientOptions.ServiceTwoServiceVersion.V2024_06_01 => "2024-06-01", + _ => throw new global::System.NotSupportedException() + }; + } + + [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] + internal MultiServiceClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) + { + ServiceOneApiVersion = "2024-01-01"; + ServiceTwoApiVersion = "2024-06-01"; + if (((section is null) || !section.Exists())) + { + return; + } + if ((section["ServiceOneApiVersion"] is string serviceOneApiVersion)) + { + this.ServiceOneApiVersion = serviceOneApiVersion; + } + if ((section["ServiceTwoApiVersion"] is string serviceTwoApiVersion)) + { + this.ServiceTwoApiVersion = serviceTwoApiVersion; + } + } + + internal string ServiceOneApiVersion { get; } + + internal string ServiceTwoApiVersion { get; } + + public enum ServiceOneServiceVersion + { + V2024_01_01 = 1 + } + + public enum ServiceTwoServiceVersion + { + V2024_06_01 = 1 + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums.cs new file mode 100644 index 00000000000..8381cce6351 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums.cs @@ -0,0 +1,64 @@ +// + +#nullable disable + +using System; +using System.ClientModel.Primitives; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Configuration; + +namespace Sample +{ + public partial class MultiServiceClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions + { + private const global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion LatestAzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion = global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion.V2024_01_01; + private const global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion LatestAzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion = global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion.V2024_06_01; + + public MultiServiceClientOptions(global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion azureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion = LatestAzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion, global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion azureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion = LatestAzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion) + { + AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion = azureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion switch + { + global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion.V2024_01_01 => "2024-01-01", + _ => throw new global::System.NotSupportedException() + }; + AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion = azureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion switch + { + global::Sample.MultiServiceClientOptions.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion.V2024_06_01 => "2024-06-01", + _ => throw new global::System.NotSupportedException() + }; + } + + [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] + internal MultiServiceClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) + { + AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion = "2024-01-01"; + AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion = "2024-06-01"; + if (((section is null) || !section.Exists())) + { + return; + } + if ((section["AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion"] is string azureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion)) + { + this.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion = azureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion; + } + if ((section["AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion"] is string azureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion)) + { + this.AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion = azureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion; + } + } + + internal string AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsApiVersion { get; } + + internal string AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsApiVersion { get; } + + public enum AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceOneVersionsVersion + { + V2024_01_01 = 1 + } + + public enum AzureGeneratorMgmtTypeSpecMultiServiceTestsServiceTwoVersionsVersion + { + V2024_06_01 = 1 + } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs index 47ef2e2b96a..0028546803d 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs @@ -39,11 +39,23 @@ protected override string BuildName() var serviceNamespace = _inputEnum.Namespace; if (!string.IsNullOrEmpty(serviceNamespace)) { - // Only use the full namespace when there is a collision in the - // last segment; otherwise, use BuildNameForService with the last segment. - return ClientHelper.HasLastSegmentCollision(serviceNamespace, _inputEnum, apiVersionEnums) - ? $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}" - : ClientHelper.BuildNameForService(serviceNamespace, string.Empty, ApiVersionEnumName); + if (!ClientHelper.HasLastSegmentCollision(serviceNamespace, _inputEnum, apiVersionEnums)) + { + // No collision in the last segment — use BuildNameForService with the last segment. + return ClientHelper.BuildNameForService(serviceNamespace, string.Empty, ApiVersionEnumName); + } + + // Last segment collides — use the full namespace. + string fullNamespaceName = $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}"; + + // If the full namespace also collides (multiple enums share the exact same namespace), + // append the enum's input name for disambiguation. + if (ClientHelper.HasFullNamespaceCollision(serviceNamespace, _inputEnum, apiVersionEnums)) + { + return $"{serviceNamespace.ToIdentifierName()}{_inputEnum.Name.ToIdentifierName()}{VersionSuffix}"; + } + + return fullNamespaceName; } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs index 62fd5551840..abd8861c201 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs @@ -66,5 +66,20 @@ public static bool HasLastSegmentCollision(string serviceNamespace, InputEnumTyp !string.IsNullOrEmpty(e.Namespace) && string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); } + /// + /// Determines whether the full namespace of the given service namespace + /// collides with any other enum's full namespace in the collection. + /// + /// The namespace to check for collisions. + /// The current enum to exclude from the comparison. + /// All API version enums to compare against. + /// True if another enum has the same full namespace. + public static bool HasFullNamespaceCollision(string serviceNamespace, InputEnumType currentEnum, IEnumerable apiVersionEnums) + { + return apiVersionEnums.Any(e => + e != currentEnum && + !string.IsNullOrEmpty(e.Namespace) && + string.Equals(e.Namespace, serviceNamespace, StringComparison.OrdinalIgnoreCase)); + } } } From 18a34a1d8aa97bbe637575d0a4705fd6dabbd471 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:29:18 +0000 Subject: [PATCH 10/10] refactor: use segment-by-segment name resolution for shorter unique names on collision Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/9150f63a-0dcd-4821-a91c-67215de3eeba --- .../src/Providers/ClientOptionsProvider.cs | 14 +++-- .../Providers/ClientOptionsProviderTests.cs | 10 +--- .../ClientProviders/ClientProviderTests.cs | 8 +-- ...eLastSegment_ProducesUniqueVersionEnums.cs | 38 +++++++------- ...eNamespaces_ProducesUniqueVersionEnums.cs} | 0 .../src/Providers/ApiVersionEnumProvider.cs | 14 ++--- .../src/Shared/ClientHelper.cs | 51 +++++++++++++++---- .../ApiVersionEnumProviderTests.cs | 10 ++-- ...s_UsesShortestUniquePrefix(ServiceOne).cs} | 2 +- ...s_UsesShortestUniquePrefix(ServiceTwo).cs} | 2 +- 10 files changed, 82 insertions(+), 67 deletions(-) rename packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/{MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs => MultiServiceClient_UniqueNamespaces_ProducesUniqueVersionEnums.cs} (100%) rename packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/{MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs => MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceOne).cs} (73%) rename packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/{MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs => MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceTwo).cs} (73%) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs index c42e6cd64df..2751a397773 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -145,10 +145,9 @@ private static bool UseSingletonInstance(InputClient inputClient) if (!string.IsNullOrEmpty(serviceNamespace) && ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys)) { - // Last segment collides — use the full namespace. - versionPropertyName = ClientHelper.HasFullNamespaceCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) - ? $"{serviceNamespace.ToIdentifierName()}{inputEnum.Name.ToIdentifierName()}{ApiVersionSuffix}" - : $"{serviceNamespace.ToIdentifierName()}{ApiVersionSuffix}"; + // Last segment collides — find the shortest unique namespace suffix. + string uniquePrefix = ClientHelper.GetShortestUniqueNamespacePrefix(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys); + versionPropertyName = $"{uniquePrefix.ToIdentifierName()}{ApiVersionSuffix}"; } else { @@ -191,10 +190,9 @@ private static bool UseSingletonInstance(InputClient inputClient) if (!string.IsNullOrEmpty(serviceNamespace) && ClientHelper.HasLastSegmentCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys)) { - // Last segment collides — use the full namespace. - fieldName = ClientHelper.HasFullNamespaceCollision(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys) - ? $"{LatestPrefix}{serviceNamespace.ToIdentifierName()}{inputEnum.Name.ToIdentifierName()}{VersionSuffix}" - : $"{LatestPrefix}{serviceNamespace.ToIdentifierName()}{VersionSuffix}"; + // Last segment collides — find the shortest unique namespace suffix. + string uniquePrefix = ClientHelper.GetShortestUniqueNamespacePrefix(serviceNamespace, inputEnum, _serviceVersionsEnums.Keys); + fieldName = $"{LatestPrefix}{uniquePrefix.ToIdentifierName()}{VersionSuffix}"; } else { diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs index 4ddee3462bc..63d5bb33f97 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientOptionsProviderTests.cs @@ -760,12 +760,8 @@ public void MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums() } [Test] - public void MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums() + public void MultiServiceClient_UniqueNamespaces_ProducesUniqueVersionEnums() { - // Simulates the spec from the original issue #10055: - // ServiceOne (namespace ServiceOne) with Versions enum (2024-01-01) - // ServiceTwo (namespace ServiceTwo) with Versions enum (2024-06-01) - // Combined in a multi-service client List serviceOneVersions = ["2024-01-01"]; List serviceTwoVersions = ["2024-06-01"]; @@ -846,8 +842,6 @@ public void MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums() { // Regression test for the scenario where both enums share the exact same namespace // (e.g., when tspconfig remaps both services to the same C# output namespace). - // This is the actual crash scenario from issue #10055 where the mgmt emitter - // remaps both ServiceOne.Versions and ServiceTwo.Versions to the same namespace. List serviceOneVersions = ["2024-01-01"]; List serviceTwoVersions = ["2024-06-01"]; @@ -901,8 +895,6 @@ public void MultiServiceClient_SameNamespace_ProducesUniqueVersionEnums() var clientProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(client); Assert.IsNotNull(clientProvider); - // The key validation: accessing Fields and Methods must not crash - // (this was the original crash site from issue #10055) Assert.DoesNotThrow(() => _ = clientProvider!.Fields); Assert.DoesNotThrow(() => _ = clientProvider!.Methods); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs index 5276928178a..c18cf79e9ff 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs @@ -3883,7 +3883,7 @@ public void GetApiVersionFieldForService_MultiService_SameLastSegment_ProducesUn // This should not crash — previously it threw due to duplicate field names Assert.DoesNotThrow(() => _ = clientProvider!.Fields); - // Verify we have two distinct api version fields using the full namespace + // Verify we have two distinct api version fields using the shortest unique namespace suffix var apiVersionFields = clientProvider!.Fields .Where(f => f.Name.Contains("ApiVersion", StringComparison.OrdinalIgnoreCase)) .OrderBy(f => f.Name) @@ -3891,9 +3891,9 @@ public void GetApiVersionFieldForService_MultiService_SameLastSegment_ProducesUn Assert.AreEqual(2, apiVersionFields.Count); Assert.AreNotEqual(apiVersionFields[0].Name, apiVersionFields[1].Name); - // Full namespace produces unique names: "Azure.ServiceOne.Tests" → "AzureServiceOneTests" - Assert.AreEqual("_azureServiceOneTestsApiVersion", apiVersionFields[0].Name); - Assert.AreEqual("_azureServiceTwoTestsApiVersion", apiVersionFields[1].Name); + // Shortest unique suffix: "ServiceOne.Tests" → "ServiceOneTests" + Assert.AreEqual("_serviceOneTestsApiVersion", apiVersionFields[0].Name); + Assert.AreEqual("_serviceTwoTestsApiVersion", apiVersionFields[1].Name); } [TestCase("{endpoint}")] diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs index 5e24fd80aa3..9308d24e008 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_SameLastSegment_ProducesUniqueVersionEnums.cs @@ -11,21 +11,21 @@ namespace Sample { public partial class TestClientOptions : global::System.ClientModel.Primitives.ClientPipelineOptions { - private const global::Sample.TestClientOptions.AzureServiceOneTestsVersion LatestAzureServiceOneTestsVersion = global::Sample.TestClientOptions.AzureServiceOneTestsVersion.V2_0; - private const global::Sample.TestClientOptions.AzureServiceTwoTestsVersion LatestAzureServiceTwoTestsVersion = global::Sample.TestClientOptions.AzureServiceTwoTestsVersion.V4_0; + private const global::Sample.TestClientOptions.ServiceOneTestsVersion LatestServiceOneTestsVersion = global::Sample.TestClientOptions.ServiceOneTestsVersion.V2_0; + private const global::Sample.TestClientOptions.ServiceTwoTestsVersion LatestServiceTwoTestsVersion = global::Sample.TestClientOptions.ServiceTwoTestsVersion.V4_0; - public TestClientOptions(global::Sample.TestClientOptions.AzureServiceOneTestsVersion azureServiceOneTestsVersion = LatestAzureServiceOneTestsVersion, global::Sample.TestClientOptions.AzureServiceTwoTestsVersion azureServiceTwoTestsVersion = LatestAzureServiceTwoTestsVersion) + public TestClientOptions(global::Sample.TestClientOptions.ServiceOneTestsVersion serviceOneTestsVersion = LatestServiceOneTestsVersion, global::Sample.TestClientOptions.ServiceTwoTestsVersion serviceTwoTestsVersion = LatestServiceTwoTestsVersion) { - AzureServiceOneTestsApiVersion = azureServiceOneTestsVersion switch + ServiceOneTestsApiVersion = serviceOneTestsVersion switch { - global::Sample.TestClientOptions.AzureServiceOneTestsVersion.V1_0 => "1.0", - global::Sample.TestClientOptions.AzureServiceOneTestsVersion.V2_0 => "2.0", + global::Sample.TestClientOptions.ServiceOneTestsVersion.V1_0 => "1.0", + global::Sample.TestClientOptions.ServiceOneTestsVersion.V2_0 => "2.0", _ => throw new global::System.NotSupportedException() }; - AzureServiceTwoTestsApiVersion = azureServiceTwoTestsVersion switch + ServiceTwoTestsApiVersion = serviceTwoTestsVersion switch { - global::Sample.TestClientOptions.AzureServiceTwoTestsVersion.V3_0 => "3.0", - global::Sample.TestClientOptions.AzureServiceTwoTestsVersion.V4_0 => "4.0", + global::Sample.TestClientOptions.ServiceTwoTestsVersion.V3_0 => "3.0", + global::Sample.TestClientOptions.ServiceTwoTestsVersion.V4_0 => "4.0", _ => throw new global::System.NotSupportedException() }; } @@ -33,33 +33,33 @@ public TestClientOptions(global::Sample.TestClientOptions.AzureServiceOneTestsVe [global::System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SCME0002")] internal TestClientOptions(global::Microsoft.Extensions.Configuration.IConfigurationSection section) : base(section) { - AzureServiceOneTestsApiVersion = "2.0"; - AzureServiceTwoTestsApiVersion = "4.0"; + ServiceOneTestsApiVersion = "2.0"; + ServiceTwoTestsApiVersion = "4.0"; if (((section is null) || !section.Exists())) { return; } - if ((section["AzureServiceOneTestsApiVersion"] is string azureServiceOneTestsApiVersion)) + if ((section["ServiceOneTestsApiVersion"] is string serviceOneTestsApiVersion)) { - this.AzureServiceOneTestsApiVersion = azureServiceOneTestsApiVersion; + this.ServiceOneTestsApiVersion = serviceOneTestsApiVersion; } - if ((section["AzureServiceTwoTestsApiVersion"] is string azureServiceTwoTestsApiVersion)) + if ((section["ServiceTwoTestsApiVersion"] is string serviceTwoTestsApiVersion)) { - this.AzureServiceTwoTestsApiVersion = azureServiceTwoTestsApiVersion; + this.ServiceTwoTestsApiVersion = serviceTwoTestsApiVersion; } } - internal string AzureServiceOneTestsApiVersion { get; } + internal string ServiceOneTestsApiVersion { get; } - internal string AzureServiceTwoTestsApiVersion { get; } + internal string ServiceTwoTestsApiVersion { get; } - public enum AzureServiceOneTestsVersion + public enum ServiceOneTestsVersion { V1_0 = 1, V2_0 = 2 } - public enum AzureServiceTwoTestsVersion + public enum ServiceTwoTestsVersion { V3_0 = 1, V4_0 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_UniqueNamespaces_ProducesUniqueVersionEnums.cs similarity index 100% rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_OriginalIssueSpec_ProducesUniqueVersionEnums.cs rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/TestData/ClientOptionsProviderTests/MultiServiceClient_UniqueNamespaces_ProducesUniqueVersionEnums.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs index 0028546803d..8bb38365343 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ApiVersionEnumProvider.cs @@ -45,17 +45,9 @@ protected override string BuildName() return ClientHelper.BuildNameForService(serviceNamespace, string.Empty, ApiVersionEnumName); } - // Last segment collides — use the full namespace. - string fullNamespaceName = $"{serviceNamespace.ToIdentifierName()}{VersionSuffix}"; - - // If the full namespace also collides (multiple enums share the exact same namespace), - // append the enum's input name for disambiguation. - if (ClientHelper.HasFullNamespaceCollision(serviceNamespace, _inputEnum, apiVersionEnums)) - { - return $"{serviceNamespace.ToIdentifierName()}{_inputEnum.Name.ToIdentifierName()}{VersionSuffix}"; - } - - return fullNamespaceName; + // Last segment collides — find the shortest unique namespace suffix. + string uniquePrefix = ClientHelper.GetShortestUniqueNamespacePrefix(serviceNamespace, _inputEnum, apiVersionEnums); + return $"{uniquePrefix.ToIdentifierName()}{VersionSuffix}"; } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs index abd8861c201..7430554896a 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Shared/ClientHelper.cs @@ -66,20 +66,53 @@ public static bool HasLastSegmentCollision(string serviceNamespace, InputEnumTyp !string.IsNullOrEmpty(e.Namespace) && string.Equals(GetLastNamespaceSegment(e.Namespace), lastSegment, StringComparison.OrdinalIgnoreCase)); } + /// - /// Determines whether the full namespace of the given service namespace - /// collides with any other enum's full namespace in the collection. + /// Finds the shortest unique namespace suffix for the given enum among all API version enums, + /// by progressively adding segments from right to left until the suffix is unique. + /// If all segments are exhausted and the suffix is still not unique (same namespace), + /// the enum's input name is appended for disambiguation. /// - /// The namespace to check for collisions. + /// The full namespace of the current enum. /// The current enum to exclude from the comparison. /// All API version enums to compare against. - /// True if another enum has the same full namespace. - public static bool HasFullNamespaceCollision(string serviceNamespace, InputEnumType currentEnum, IEnumerable apiVersionEnums) + /// + /// The shortest unique namespace suffix string (e.g., "ServiceOne.Tests" from "Azure.ServiceOne.Tests"), + /// or the full namespace plus the enum's input name if the namespace itself is not unique. + /// + public static string GetShortestUniqueNamespacePrefix(string serviceNamespace, InputEnumType currentEnum, IEnumerable apiVersionEnums) { - return apiVersionEnums.Any(e => - e != currentEnum && - !string.IsNullOrEmpty(e.Namespace) && - string.Equals(e.Namespace, serviceNamespace, StringComparison.OrdinalIgnoreCase)); + var otherNamespaces = apiVersionEnums + .Where(e => e != currentEnum && !string.IsNullOrEmpty(e.Namespace)) + .Select(e => e.Namespace!) + .ToList(); + + string[] segments = serviceNamespace.Split('.'); + + // Start from the last segment and progressively prepend segments + for (int count = 1; count <= segments.Length; count++) + { + string candidate = string.Join(".", segments, segments.Length - count, count); + bool isUnique = true; + foreach (var otherNs in otherNamespaces) + { + string[] otherSegments = otherNs.Split('.'); + int otherCount = Math.Min(count, otherSegments.Length); + string otherCandidate = string.Join(".", otherSegments, otherSegments.Length - otherCount, otherCount); + if (string.Equals(candidate, otherCandidate, StringComparison.OrdinalIgnoreCase)) + { + isUnique = false; + break; + } + } + if (isUnique) + { + return candidate; + } + } + + // Full namespace still collides (identical namespaces) — append enum input name + return $"{serviceNamespace}.{currentEnum.Name}"; } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs index 5688137d9fd..a26619bceb3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/ApiVersionEnumProviderTests.cs @@ -214,10 +214,10 @@ public void MultiServiceClient_WithOneApiVersionEnums_GeneratesCorrectEnumNames( } [Test] - public void MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace() + public void MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix() { // When two services have different full namespaces but the same last segment, - // the full namespace should be used to avoid collisions. + // the shortest unique namespace suffix should be used to avoid collisions. var serviceOneEnum = InputFactory.StringEnum( "ServiceOneVersion", [("1.0", "1.0"), ("2.0", "2.0")], @@ -247,9 +247,9 @@ public void MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace() Assert.IsTrue(serviceTwoEnumType is ApiVersionEnumProvider); var serviceTwoProvider = (ApiVersionEnumProvider)serviceTwoEnumType; - // Verify enum names use the full namespace for uniqueness when last segments collide - Assert.AreEqual("AzureServiceOneTestsVersion", serviceOneProvider.Name); - Assert.AreEqual("AzureServiceTwoTestsVersion", serviceTwoProvider.Name); + // Verify enum names use the shortest unique namespace suffix (2 segments: ServiceOne.Tests) + Assert.AreEqual("ServiceOneTestsVersion", serviceOneProvider.Name); + Assert.AreEqual("ServiceTwoTestsVersion", serviceTwoProvider.Name); // Validate generated output var writerOne = new TypeProviderWriter(serviceOneProvider); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceOne).cs similarity index 73% rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceOne).cs index 4f8c659dd91..574d744733f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceOne).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceOne).cs @@ -4,7 +4,7 @@ namespace Azure.ServiceOne.Tests { - public enum AzureServiceOneTestsVersion + public enum ServiceOneTestsVersion { V1_0 = 1, V2_0 = 2 diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceTwo).cs similarity index 73% rename from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs rename to packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceTwo).cs index eb92b3edda9..7c2c401c0ce 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesFullNamespace(ServiceTwo).cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/ApiVersionEnumProviderTests/MultiServiceClient_WithCollidingLastSegments_UsesShortestUniquePrefix(ServiceTwo).cs @@ -4,7 +4,7 @@ namespace Azure.ServiceTwo.Tests { - public enum AzureServiceTwoTestsVersion + public enum ServiceTwoTestsVersion { V3_0 = 1, V4_0 = 2