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

wip to refactor methodprovidercollection #3686

Merged
merged 4 commits into from
Jun 27, 2024
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 @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using Microsoft.Generator.CSharp.ClientModel.Providers;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Providers;

Expand All @@ -18,36 +19,7 @@ public class ScmTypeFactory : TypeFactory
/// </summary>
/// <param name="operation">The input operation to create methods for.</param>
/// <param name="enclosingType">The enclosing type of the operation.</param>
public override MethodProviderCollection? CreateMethodProviders(InputOperation operation, TypeProvider enclosingType)
{
if (_operations.TryGetValue(operation, out var methods))
{
return methods;
}

methods = GetOperationKind(operation).ToString() switch
{
"Default" => MethodProviderCollection.DefaultCSharpMethodCollection(operation, enclosingType),
_ => null,
};

_operations.Add(operation, methods);
return methods;
}

/// <summary>
/// Returns the <see cref="InputOperationKinds"/> of the given operation.
/// By default, the operation kind is <see cref="InputOperationKinds.Default"/>.
/// </summary>
private static InputOperationKinds GetOperationKind(InputOperation operation)
{
return operation switch
{
{ LongRunning: { } } => InputOperationKinds.LongRunning,
{ Paging: { } } => InputOperationKinds.Paging,
_ => InputOperationKinds.Default,
};
}
public override MethodProviderCollection CreateMethodProviders(InputOperation operation, TypeProvider enclosingType) => new ScmMethodProviderCollection(operation, enclosingType);

public virtual CSharpType MatchConditionsType()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using Microsoft.Generator.CSharp.Expressions;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Providers;
using Microsoft.Generator.CSharp.Statements;
using static Microsoft.Generator.CSharp.Snippets.Snippet;
using Microsoft.Generator.CSharp.Snippets;

namespace Microsoft.Generator.CSharp.ClientModel.Providers
{
internal class ScmMethodProviderCollection : MethodProviderCollection
{
public ScmMethodProviderCollection(InputOperation operation, TypeProvider enclosingType)
: base(operation, enclosingType)
{
}

protected override IReadOnlyList<MethodProvider> BuildMethods()
{
return
[
// TO-DO: Add Protocol and Convenience methods https://github.com/Azure/autorest.csharp/issues/4585, https://github.com/Azure/autorest.csharp/issues/4586
BuildCreateMessageMethod(_operation, _enclosingType),
BuildProtocolMethod(_operation, _enclosingType, false),
BuildProtocolMethod(_operation, _enclosingType, true)
];
}
private static MethodProvider BuildProtocolMethod(InputOperation operation, TypeProvider enclosingType, bool isAsync)
{
List<ParameterProvider> methodParameters = new();
foreach (InputParameter inputParam in operation.Parameters)
{
if (inputParam.Kind != InputOperationParameterKind.Method)
continue;
methodParameters.Add(ClientModelPlugin.Instance.TypeFactory.CreateCSharpParam(inputParam));
}

var methodModifier = MethodSignatureModifiers.Public | MethodSignatureModifiers.Virtual;
if (isAsync)
{
methodModifier |= MethodSignatureModifiers.Async;
}
var opName = operation.Name.ToCleanName();
var methodSignature = new MethodSignature(
isAsync ? opName + "Async" : opName,
FormattableStringHelpers.FromString(operation.Description),
methodModifier,
GetResponseType(operation.Responses, isAsync),
null,
Parameters: [.. methodParameters, KnownParameters.CancellationTokenParameter]);
m-nash marked this conversation as resolved.
Show resolved Hide resolved
MethodBodyStatement[] methodBody =
[
//UsingDeclare("message", typeof(RequestMess)
//using PipelineMessage message = CreateSayHiRequest(headParameter, queryParameter, optionalQuery, options);
isAsync ? new InvokeStaticPropertyExpression(typeof(Task), nameof(Task.CompletedTask), true).Terminate() : EmptyStatement
];

return new MethodProvider(methodSignature, methodBody, enclosingType);
}

private static CSharpType? GetResponseType(IReadOnlyList<OperationResponse> responses, bool isAsync)
{
var response = responses.FirstOrDefault(r => !r.IsErrorResponse);
if (response is null || response.BodyType is null)
return null;
var returnType = ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(response.BodyType);
if (isAsync)
{
returnType = returnType.WrapInTask();
}
return returnType;
}

private static MethodProvider BuildCreateMessageMethod(InputOperation operation, TypeProvider enclosingType)
{
// TO-DO: properly build method https://github.com/Azure/autorest.csharp/issues/4583
List<ParameterProvider> methodParameters = new();
foreach (var inputParam in operation.Parameters)
{
methodParameters.Add(ClientModelPlugin.Instance.TypeFactory.CreateCSharpParam(inputParam));
}

var methodModifier = MethodSignatureModifiers.Internal;
var methodSignatureName = $"Create{operation.Name.ToCleanName()}Request";
var methodSignature = new MethodSignature(methodSignatureName, FormattableStringHelpers.FromString(operation.Description), methodModifier, null, null, Parameters: methodParameters);
var methodBody = Throw(New.NotImplementedException(Literal("Method not implemented.")));

return new MethodProvider(methodSignature, methodBody, enclosingType);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Generator.CSharp.ClientModel.Tests
{
internal class MockTypeFactory : ScmTypeFactory
{
public override MethodProviderCollection? CreateMethodProviders(InputOperation operation, TypeProvider enclosingType)
public override MethodProviderCollection CreateMethodProviders(InputOperation operation, TypeProvider enclosingType)
{
throw new NotImplementedException();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Microsoft.Generator.CSharp.Providers;

namespace Microsoft.Generator.CSharp.ClientModel.Tests
{
internal class MockTypeProvider : TypeProvider
{
public override string RelativeFilePath => throw new NotImplementedException();
public override string Name => throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Microsoft.Generator.CSharp.ClientModel.Providers;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Providers;
using Moq;
using Moq.Protected;
using NUnit.Framework;

namespace Microsoft.Generator.CSharp.Tests.Providers
namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers
{
internal class MethodProviderCollectionTests
internal class ScmMethodProviderCollectionTests
{
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private TypeFactory _typeFactory;
private ScmTypeFactory _typeFactory;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
private readonly string _mocksFolder = "Mocks";
private FieldInfo? _mockPlugin;
Expand All @@ -25,16 +26,17 @@ internal class MethodProviderCollectionTests
public void Setup()
{
var mockParameter = new ParameterProvider("mockParam", $"mock description", typeof(bool), null);
var mockTypeFactory = new Mock<TypeFactory>() { };
var mockTypeFactory = new Mock<ScmTypeFactory>() { };
mockTypeFactory.Protected().Setup<CSharpType>("CreateCSharpTypeCore", ItExpr.IsAny<InputType>()).Returns(new CSharpType(typeof(bool)));
mockTypeFactory.Setup(t => t.CreateCSharpParam(It.IsAny<InputParameter>())).Returns(mockParameter);
_typeFactory = mockTypeFactory.Object;

var configFilePath = Path.Combine(AppContext.BaseDirectory, _mocksFolder);
// initialize the mock singleton instance of the plugin
_mockPlugin = typeof(CodeModelPlugin).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
var mockGeneratorContext = new Mock<GeneratorContext>(Configuration.Load(configFilePath));
var mockPluginInstance = new Mock<CodeModelPlugin>(mockGeneratorContext.Object) { };
_mockPlugin = typeof(ClientModelPlugin).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
var mockConfiguration = new Mock<Configuration>() { };
var mockGeneratorContext = new Mock<GeneratorContext>(mockConfiguration.Object);
var mockPluginInstance = new Mock<ClientModelPlugin>(mockGeneratorContext.Object) { };
mockPluginInstance.SetupGet(p => p.TypeFactory).Returns(_typeFactory);

_mockPlugin?.SetValue(null, mockPluginInstance.Object);
Expand All @@ -50,9 +52,9 @@ public void Teardown()
[TestCaseSource(nameof(DefaultCSharpMethodCollectionTestCases))]
public void TestDefaultCSharpMethodCollection(InputOperation inputOperation)
{
var methodCollection = MethodProviderCollection.DefaultCSharpMethodCollection(inputOperation, new MockTypeProvider());
var methodCollection = new ScmMethodProviderCollection(inputOperation, new MockTypeProvider());
Assert.IsNotNull(methodCollection);
Assert.AreEqual(1, methodCollection?.Count);
Assert.AreEqual(3, methodCollection.Count);

var method = methodCollection![0];
var signature = method.Signature;
Expand Down Expand Up @@ -93,5 +95,4 @@ public static IEnumerable<TestCaseData> DefaultCSharpMethodCollectionTestCases
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ public class Configuration
{
private const string ConfigurationFileName = "Configuration.json";

// for mocking
protected Configuration()
{
OutputDirectory = null!;
AdditionalConfigOptions = null!;
LibraryName = null!;
Namespace = null!;
}

private Configuration(
string outputPath,
Dictionary<string, BinaryData> additionalConfigOptions,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Generator.CSharp.Statements;

namespace Microsoft.Generator.CSharp.Expressions
{
public sealed record InvokeStaticPropertyExpression(CSharpType? ContainingType, string PropertyName, bool CallAsAsync = false) : ValueExpression
{
internal override void Write(CodeWriter writer)
{
writer.AppendRawIf("await ", CallAsAsync);
if (ContainingType != null)
{
writer.Append($"{ContainingType}.");
}

writer.AppendRaw(PropertyName);
writer.AppendRawIf(".ConfigureAwait(false)", CallAsAsync);
}

private MethodBodyStatement? _terminated;
public MethodBodyStatement Terminate() => _terminated ??= new ExpressionStatement(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Generator.CSharp.Providers;

namespace Microsoft.Generator.CSharp
Expand Down Expand Up @@ -608,5 +609,15 @@ public CSharpType MakeGenericType(IReadOnlyList<CSharpType> arguments)
return new CSharpType(Implementation, arguments, IsNullable);
}
}

public CSharpType WrapInTask()
{
if (IsFrameworkType && FrameworkType == typeof(Task<>))
{
return this;
}

return new CSharpType(typeof(Task<>), this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public CSharpType CreateCSharpType(InputType inputType)
/// <returns>An instance of <see cref="MethodProviderCollection"/> containing the chain of methods
/// associated with the input operation, or <c>null</c> if no methods are constructed.
/// </returns>
public abstract MethodProviderCollection? CreateMethodProviders(InputOperation operation, TypeProvider enclosingType);
public virtual MethodProviderCollection CreateMethodProviders(InputOperation operation, TypeProvider enclosingType) => new(operation, enclosingType);

/// <summary>
/// Factory method for retrieving the serialization format for a given input type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,41 @@

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Generator.CSharp.Input;
using Microsoft.Generator.CSharp.Snippets;

namespace Microsoft.Generator.CSharp.Providers
{
/// <summary>
/// Represents an immutable collection of methods that are associated with an operation <see cref="InputOperation"/>.
/// </summary>
public sealed class MethodProviderCollection : IReadOnlyList<MethodProvider>
public class MethodProviderCollection : IReadOnlyList<MethodProvider>
{
private readonly IReadOnlyList<MethodProvider> _cSharpMethods;
private IReadOnlyList<MethodProvider>? _cSharpMethods;
protected readonly InputOperation _operation;
protected readonly TypeProvider _enclosingType;

private MethodProviderCollection(IEnumerable<MethodProvider>? methods)
public MethodProviderCollection(InputOperation operation, TypeProvider enclosingType)
{
_cSharpMethods = methods?.ToList() ?? [];
_operation = operation;
_enclosingType = enclosingType;
}

/// <summary>
/// Builds a default <see cref="MethodProviderCollection"/> for the given <see cref="InputOperation"/>
/// with a single method that creates a message.
/// </summary>
/// <param name="operation">The <see cref="InputOperation"/> to convert.</param>
/// <param name="enclosingType">The <see cref="TypeProvider"/> that will contain the methods.</param>
public static MethodProviderCollection DefaultCSharpMethodCollection(InputOperation operation, TypeProvider enclosingType)
{
var createMessageMethod = BuildCreateMessageMethod(operation, enclosingType);
var cSharpMethods = new List<MethodProvider>() { createMessageMethod };
// TO-DO: Add Protocol and Convenience methods https://github.com/Azure/autorest.csharp/issues/4585, https://github.com/Azure/autorest.csharp/issues/4586
return new MethodProviderCollection(cSharpMethods);
}
protected virtual IReadOnlyList<MethodProvider> BuildMethods() => [];
public IReadOnlyList<MethodProvider> MethodProviders => _cSharpMethods ??= BuildMethods();

public MethodProvider this[int index]
{
get { return _cSharpMethods[index]; }
get { return MethodProviders[index]; }
}

public int Count
{
get { return _cSharpMethods.Count; }
}

private static MethodProvider BuildCreateMessageMethod(InputOperation operation, TypeProvider enclosingType)
{
// TO-DO: properly build method https://github.com/Azure/autorest.csharp/issues/4583
List<ParameterProvider> methodParameters = new();
foreach (var inputParam in operation.Parameters)
{
methodParameters.Add(CodeModelPlugin.Instance.TypeFactory.CreateCSharpParam(inputParam));
}

var methodModifier = MethodSignatureModifiers.Internal;
var methodSignatureName = $"Create{operation.Name.ToCleanName()}Request";
var methodSignature = new MethodSignature(methodSignatureName, FormattableStringHelpers.FromString(operation.Description), methodModifier, null, null, Parameters: methodParameters);
var methodBody = Snippet.Throw(Snippet.New.NotImplementedException(Snippet.Literal("Method not implemented.")));

return new MethodProvider(methodSignature, methodBody, enclosingType);
get { return MethodProviders.Count; }
}

public IEnumerator<MethodProvider> GetEnumerator()
{
return _cSharpMethods.GetEnumerator();
return MethodProviders.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Generator.CSharp.Tests
{
internal class MockTypeFactory : TypeFactory
{
public override MethodProviderCollection? CreateMethodProviders(InputOperation operation, TypeProvider enclosingType)
public override MethodProviderCollection CreateMethodProviders(InputOperation operation, TypeProvider enclosingType)
{
throw new NotImplementedException();
}
Expand Down
Loading
Loading