Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip empty clients #5652

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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 @@ -47,6 +47,7 @@ private record OAuth2Fields(FieldProvider AuthField, FieldProvider Authorization
private Lazy<IReadOnlyList<FieldProvider>> _additionalClientFields;

private Lazy<ParameterProvider?> ClientOptionsParameter { get; }
internal IReadOnlyList<ClientProvider> SubClients => _subClients.Value;

// for mocking
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,29 @@ private ExtensibleEnumSerializationProvider[] CreateExtensibleEnumSerializations
}
}

ClientCache[inputClient] = client;
return client;
var result = client is not null && IsValidClient(client) ? client : null;
ClientCache[inputClient] = result;
return result;
}

private bool IsValidClient(ClientProvider client)
{
// client is valid if it has methods or custom code has methods
if (client.Methods.Count > 0 || client.CustomCodeView?.Methods.Count > 0)
{
return true;
}

// client is valid if any of its subclients have methods or custom code has methods
foreach (var subclient in client.SubClients)
{
if (subclient.Methods.Count > 0 || subclient.CustomCodeView?.Methods.Count > 0)
{
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to also check to see if the subclients have custom code defined.

Copy link
Contributor Author

@live1206 live1206 Jan 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the check of custom code but only check methods of custom code for now.

}
}

return false;
}

protected virtual ClientProvider? CreateClientCore(InputClient inputClient) => new ClientProvider(inputClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers.ClientProviders
{
public class ClientProviderCustomizationTests
{
private const string TestClientName = "TestClient";

[Test]
public async Task CanAddMethod()
{
Expand Down Expand Up @@ -45,6 +47,33 @@ public async Task CanAddMethod()
Assert.AreEqual(string.Empty, customMethods[0].BodyStatements!.ToDisplayString());
}

[Test]
public async Task ShouldGenerateEmptyClientWithCustomMethods()
{
var client = InputFactory.Client(TestClientName);
var plugin = await MockHelpers.LoadMockPluginAsync(
clients: () => [client],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
var clientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == TestClientName);
Assert.IsNotNull(clientProvider);
}

[Test]
public async Task GenerateSubClientWithCustomMethods()
{
var inputOperation = InputFactory.Operation("HelloAgain", parameters:
[
InputFactory.Parameter("p1", InputFactory.Array(InputPrimitiveType.String))
]);
var client = InputFactory.Client(TestClientName);
var subClient = InputFactory.Client($"Sub{TestClientName}", [inputOperation], [], client.Name);
var plugin = await MockHelpers.LoadMockPluginAsync(
clients: () => [client, subClient],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
var clientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == TestClientName);
Assert.IsNotNull(clientProvider);
}

[Test]
public async Task CanAddMultipleMethods()
{
Expand Down Expand Up @@ -275,7 +304,7 @@ public async Task CanRenameSubClient()
InputFactory.Parameter("p1", InputFactory.Array(InputPrimitiveType.String))
]);
var inputClient = InputFactory.Client("TestClient", operations: [inputOperation]);
InputClient subClient = InputFactory.Client("custom", [], [], inputClient.Name);
InputClient subClient = InputFactory.Client("custom", [inputOperation], [], inputClient.Name);
var plugin = await MockHelpers.LoadMockPluginAsync(
clients: () => [inputClient, subClient],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers.ClientProviders
public class ClientProviderSubClientTests
{
private const string TestClientName = "TestClient";
private static readonly InputClient _animalClient = new("animal", string.Empty, "AnimalClient description", [], [], TestClientName);
private static readonly InputClient _dogClient = new("dog", string.Empty, "DogClient description", [], [], _animalClient.Name);
private static readonly InputClient _catClient = new("cat", string.Empty, "CatClient description", [], [], _animalClient.Name);
private static readonly InputClient _hawkClient = new("hawkClient", string.Empty, "HawkClient description", [], [], _animalClient.Name);
private static readonly InputClient _huskyClient = new("husky", string.Empty, "HuskyClient description", [], [], _dogClient.Name);
private static readonly InputOperation _inputOperation = InputFactory.Operation("HelloAgain", parameters:
[
InputFactory.Parameter("p1", InputFactory.Array(InputPrimitiveType.String))
]);
private static readonly InputClient _animalClient = new("animal", string.Empty, "AnimalClient description", [_inputOperation], [], TestClientName);
private static readonly InputClient _dogClient = new("dog", string.Empty, "DogClient description", [_inputOperation], [], _animalClient.Name);
private static readonly InputClient _catClient = new("cat", string.Empty, "CatClient description", [_inputOperation], [], _animalClient.Name);
private static readonly InputClient _hawkClient = new("hawkClient", string.Empty, "HawkClient description", [_inputOperation], [], _animalClient.Name);
private static readonly InputClient _huskyClient = new("husky", string.Empty, "HuskyClient description", [_inputOperation], [], _dogClient.Name);

[SetUp]
public void SetUp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ public class ClientProviderTests
private const string KeyAuthCategory = "WithKeyAuth";
private const string OAuth2Category = "WithOAuth2";
private const string TestClientName = "TestClient";
private static readonly InputClient _animalClient = new("animal", "", "AnimalClient description", [], [], TestClientName);
private static readonly InputClient _dogClient = new("dog", "", "DogClient description", [], [], _animalClient.Name);
private static readonly InputClient _huskyClient = new("husky", "", "HuskyClient description", [], [], _dogClient.Name);
private static readonly InputOperation _inputOperation = InputFactory.Operation("HelloAgain", parameters:
[
InputFactory.Parameter("p1", InputFactory.Array(InputPrimitiveType.String))
]);
private static readonly InputClient _animalClient = new("animal", "", "AnimalClient description", [_inputOperation], [], TestClientName);
private static readonly InputClient _dogClient = new("dog", "", "DogClient description", [_inputOperation], [], _animalClient.Name);
private static readonly InputClient _huskyClient = new("husky", "", "HuskyClient description", [_inputOperation], [], _dogClient.Name);
private static readonly InputModelType _spreadModel = InputFactory.Model(
"spreadModel",
usage: InputModelTypeUsage.Spread,
Expand Down Expand Up @@ -63,6 +67,54 @@ public void SetUp()
clientPipelineApi: TestClientPipelineApi.Instance);
}

[Test]
public void ShouldSkipEmptyClient()
{
var client = InputFactory.Client(TestClientName);
var plugin = MockHelpers.LoadMockPlugin(clients: () => [client]);

var clientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == TestClientName);
var restClientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is RestClientProvider && t.Name == TestClientName);
Assert.IsNull(clientProvider);
Assert.IsNull(restClientProvider);
}

[Test]
public void ShouldGenerateClientWithNonEmptySubClient()
{
var inputOperation = InputFactory.Operation("HelloAgain", parameters:
[
InputFactory.Parameter("p1", InputFactory.Array(InputPrimitiveType.String))
]);
var client = InputFactory.Client(TestClientName);
var subClient = InputFactory.Client($"Sub{TestClientName}", [inputOperation], [], client.Name);
var plugin = MockHelpers.LoadMockPlugin(clients: () => [client, subClient]);

var subClientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == subClient.Name);
Assert.IsNotNull(subClientProvider);

var clientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == TestClientName);
Assert.IsNotNull(clientProvider);
}

[Test]
public void ShouldSkipClientWithEmptySubClient()
{
var client = InputFactory.Client(TestClientName);
var subClient = InputFactory.Client($"Sub{TestClientName}", [], [], client.Name);
var plugin = MockHelpers.LoadMockPlugin(clients: () => [client, subClient]);

var subClientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == subClient.Name);
var subRestClientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is RestClientProvider && t.Name == subClient.Name);
Assert.IsNull(subClientProvider);
Assert.IsNull(subRestClientProvider);

var clientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is ClientProvider && t.Name == TestClientName);
var restClientProvider = plugin.Object.OutputLibrary.TypeProviders.SingleOrDefault(t => t is RestClientProvider && t.Name == TestClientName);
Assert.IsNull(clientProvider);
Assert.IsNull(restClientProvider);
}

[Test]
public void TestBuildProperties()
{
Expand Down Expand Up @@ -736,7 +788,7 @@ public void TestApiVersionPathParameterOfClient(InputClient inputClient)
public void ClientProviderIsAddedToLibrary()
{
var plugin = MockHelpers.LoadMockPlugin(
clients: () => [new InputClient("test", "test", "test", [], [], null)]);
clients: () => [new InputClient("test", "test", "test", [_inputOperation], [], null)]);

Assert.AreEqual(1, plugin.Object.OutputLibrary.TypeProviders.OfType<ClientProvider>().Count());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sample
{
/// <summary></summary>
public partial class SubTestClient
{
public virtual ClientResult NewMethod(BinaryContent content, RequestOptions options)
{
Argument.AssertNotNull(content, nameof(content));

using PipelineMessage message = CreateRequest(content, options);
return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sample
{
/// <summary></summary>
public partial class TestClient
{
public virtual ClientResult NewMethod(BinaryContent content, RequestOptions options)
{
Argument.AssertNotNull(content, nameof(content));

using PipelineMessage message = CreateRequest(content, options);
return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Generator.CSharp.ClientModel.Providers;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Tests.Common;
using NUnit.Framework;
Expand All @@ -16,7 +17,11 @@ public class RestClientProviderCustomizationTests
[Test]
public async Task CanChangeClientNamespace()
{
var inputClient = InputFactory.Client("TestClient");
var inputOperation = InputFactory.Operation("HelloAgain", parameters:
[
InputFactory.Parameter("p1", InputFactory.Array(InputPrimitiveType.String))
]);
var inputClient = InputFactory.Client("TestClient", [inputOperation]);
var plugin = await MockHelpers.LoadMockPluginAsync(
clients: () => [inputClient],
compilation: async () => await Helpers.GetCompilationFromDirectoryAsync());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,31 @@

#nullable disable

using System.ClientModel;
using System.ClientModel.Primitives;
using Sample;

namespace Sample.Custom
{
/// <summary></summary>
public partial class TestClient
{
private static global::System.ClientModel.Primitives.PipelineMessageClassifier _pipelineMessageClassifier200;

private static global::System.ClientModel.Primitives.PipelineMessageClassifier PipelineMessageClassifier200 => _pipelineMessageClassifier200 = global::System.ClientModel.Primitives.PipelineMessageClassifier.Create(stackalloc ushort[] { 200 });

internal global::System.ClientModel.Primitives.PipelineMessage CreateHelloAgainRequest(global::System.ClientModel.BinaryContent content, global::System.ClientModel.Primitives.RequestOptions options)
{
global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage();
message.ResponseClassifier = PipelineMessageClassifier200;
global::System.ClientModel.Primitives.PipelineRequest request = message.Request;
request.Method = "GET";
global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder();
uri.Reset(_endpoint);
request.Uri = uri.ToUri();
request.Content = content;
message.Apply(options);
return message;
}
}
}
Loading