Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,13 @@ public static ClientOptionsProvider CreateClientOptionsProvider(InputClient inpu
}

/// <summary>
/// Determines if a client has only standard parameters (ApiVersion and Endpoint).
/// Determines if a client can share the singleton options instance.
/// Multi-service clients always need their own options type for service-specific API version properties.
/// Only optional parameters with default values that become properties on the options class
/// should trigger a separate client-specific options type.
/// </summary>
/// <param name="inputClient">The input client to check.</param>
/// <returns>True if the client has only standard parameters, false otherwise.</returns>
/// <returns>True if the client can share the singleton options instance, false otherwise.</returns>
private static bool UseSingletonInstance(InputClient inputClient)
{
var rootClients = ScmCodeModelGenerator.Instance.InputLibrary.InputNamespace.RootClients;
Expand All @@ -98,6 +101,12 @@ private static bool UseSingletonInstance(InputClient inputClient)
return false;
}

// Multi-service clients need their own options type for service-specific API version properties
if (inputClient.IsMultiServiceClient)
{
return false;
}

foreach (var parameter in inputClient.Parameters)
{
// Check if parameter is NOT an ApiVersion or Endpoint parameter
Expand All @@ -111,11 +120,15 @@ private static bool UseSingletonInstance(InputClient inputClient)
return false; // Found a non-standard endpoint parameter
}
}
else
else if (parameter.DefaultValue != null)
{
// Found a non-ApiVersion, non-Endpoint parameter
// Found a non-ApiVersion, non-Endpoint optional parameter that will become
// a property on the options class — requires a separate client-specific options type.
return false;
}
// Required parameters (DefaultValue == null) are inlined as constructor parameters
// on the client and do not become properties on the options class,
// so they should not trigger a separate client-specific options type.
}
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,42 @@ public void MultipleClientsWithCustomParametersCreateSeparateOptions()
Assert.AreEqual("SampleClientOptions", options2!.Name);
}

[Test]
public void MultipleClientsWithRequiredCustomParametersShareSingletonOptions()
{
// Required parameters (no DefaultValue) should NOT trigger a separate client-specific options type.
// They are inlined as constructor parameters on the client, not as properties on ClientOptions.
var requiredParam = InputFactory.MethodParameter(
"knowledgeBaseName",
InputPrimitiveType.String,
isRequired: true,
scope: InputParameterScope.Client);

var client1 = InputFactory.Client("KnowledgeBaseRetrievalClient", clientNamespace: "TestNamespace", parameters: [requiredParam]);
var client2 = InputFactory.Client("SearchClient", clientNamespace: "TestNamespace");

MockHelpers.LoadMockGenerator(clients: () => [client1, client2]);

var clientProvider1 = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(client1);
var clientProvider2 = ScmCodeModelGenerator.Instance.TypeFactory.CreateClient(client2);

Assert.IsNotNull(clientProvider1);
Assert.IsNotNull(clientProvider2);

var options1 = clientProvider1!.ClientOptions;
var options2 = clientProvider2!.ClientOptions;

Assert.IsNotNull(options1);
Assert.IsNotNull(options2);

// Both clients should share the same singleton ClientOptions instance
// because the required parameter does not become a property on the options class
Assert.AreSame(options1, options2);

// The name should be based on the InputNamespace (singleton naming)
Assert.AreEqual("SampleClientOptions", options1!.Name);
}

[Test]
public void NamespaceLastSegmentIsUsedForSingletonName()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
Expand Down Expand Up @@ -69,6 +70,10 @@ public bool ValidateIsLastNamespaceSegmentTheSame(string left, string right)
[SetUp]
public void SetUp()
{
// Reset the singleton instance before each test to prevent state leaking
var singletonField = typeof(ClientOptionsProvider).GetField("_singletonInstance", BindingFlags.Static | BindingFlags.NonPublic);
singletonField?.SetValue(null, null);

var categories = TestContext.CurrentContext.Test?.Properties["Category"];
_containsSubClients = categories?.Contains(SubClientsCategory) ?? false;
_hasKeyAuth = categories?.Contains(KeyAuthCategory) ?? false;
Expand Down
Loading