Construct request options from cancellation token (#5152)

Fix to https://github.com/microsoft/typespec/pull/5150
This commit is contained in:
JoshLove-msft 2024-11-19 16:42:52 -08:00 коммит произвёл GitHub
Родитель 9baee337e7
Коммит 53a475d99d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 73 добавлений и 30 удалений

Просмотреть файл

@ -87,7 +87,7 @@ namespace Microsoft.Generator.CSharp.ClientModel.Providers
methodBody =
[
.. GetStackVariablesForProtocolParamConversion(ConvenienceMethodParameters, out var declarations),
Return(This.Invoke(protocolMethod.Signature, [.. GetProtocolMethodArguments(ConvenienceMethodParameters, declarations)], isAsync))
Return(This.Invoke(protocolMethod.Signature, [.. GetProtocolMethodArguments(ConvenienceMethodParameters, declarations, isAsync)], isAsync))
];
}
else
@ -95,7 +95,7 @@ namespace Microsoft.Generator.CSharp.ClientModel.Providers
methodBody =
[
.. GetStackVariablesForProtocolParamConversion(ConvenienceMethodParameters, out var paramDeclarations),
Declare("result", This.Invoke(protocolMethod.Signature, [.. GetProtocolMethodArguments(ConvenienceMethodParameters, paramDeclarations)], isAsync).ToApi<ClientResponseApi>(), out ClientResponseApi result),
Declare("result", This.Invoke(protocolMethod.Signature, [.. GetProtocolMethodArguments(ConvenienceMethodParameters, paramDeclarations, isAsync)], isAsync).ToApi<ClientResponseApi>(), out ClientResponseApi result),
.. GetStackVariablesForReturnValueConversion(result, responseBodyType, isAsync, out var resultDeclarations),
Return(result.FromValue(GetResultConversion(result, result.GetRawResponse(), responseBodyType, resultDeclarations), result.GetRawResponse())),
];
@ -315,7 +315,10 @@ namespace Microsoft.Generator.CSharp.ClientModel.Providers
return result.CastTo(responseBodyType);
}
private IReadOnlyList<ValueExpression> GetProtocolMethodArguments(IReadOnlyList<ParameterProvider> convenienceMethodParameters, Dictionary<string, ValueExpression> declarations)
private IReadOnlyList<ValueExpression> GetProtocolMethodArguments(
IReadOnlyList<ParameterProvider> convenienceMethodParameters,
Dictionary<string, ValueExpression> declarations,
bool isAsync)
{
List<ValueExpression> conversions = new List<ValueExpression>();
bool addedSpreadSource = false;
@ -363,7 +366,11 @@ namespace Microsoft.Generator.CSharp.ClientModel.Providers
}
}
// RequestOptions argument
conversions.Add(ScmKnownParameters.RequestOptions.PositionalReference(Null));
conversions.Add(
isAsync
? RequestOptionsSnippets.FromCancellationToken(ScmKnownParameters.CancellationToken)
: ScmKnownParameters.RequestOptions.PositionalReference(Null));
return conversions;
}

Просмотреть файл

@ -2,6 +2,8 @@
// Licensed under the MIT License.
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Generator.CSharp.Expressions;
using Microsoft.Generator.CSharp.Primitives;
using Microsoft.Generator.CSharp.Snippets;
@ -11,8 +13,15 @@ namespace Microsoft.Generator.CSharp.ClientModel.Snippets
{
internal static class RequestOptionsSnippets
{
public static ScopedApi<RequestOptions> FromCancellationToken()
=> Static<RequestOptions>().Invoke("FromCancellationToken", [KnownParameters.CancellationTokenParameter]).As<RequestOptions>();
public static ScopedApi<RequestOptions> FromCancellationToken(ValueExpression cancellationToken)
=> new TernaryConditionalExpression(
cancellationToken.Property(nameof(CancellationToken.CanBeCanceled)),
New.Instance<RequestOptions>(
arguments: [],
properties: new Dictionary<ValueExpression, ValueExpression>
{ { new MemberExpression(null, nameof(RequestOptions.CancellationToken)), cancellationToken } },
useSingleLineForPropertyInitialization: true),
Null).As<RequestOptions>();
public static ValueExpression ErrorOptions(this ScopedApi<RequestOptions> requestOptions) => requestOptions.Property(nameof(RequestOptions.ErrorOptions));
}

Просмотреть файл

@ -361,17 +361,18 @@ namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers.ClientProviders
Assert.AreEqual(2, methods.Where(m => m.Signature.Parameters.Any(p => p.Name == "queryParam" && p.Type.IsFrameworkType && p.Type.FrameworkType == typeof(string))).Count());
}
[Test]
public void ValidateQueryParamWriterDiff()
[TestCase(true)]
[TestCase(false)]
public void ValidateQueryParamWriterDiff(bool isAsync)
{
MockHelpers.LoadMockPlugin(
createClientCore: (client) => new ValidateQueryParamDiffClientProvider(client));
createClientCore: (client) => new ValidateQueryParamDiffClientProvider(client, isAsync));
var clientProvider = ClientModelPlugin.Instance.TypeFactory.CreateClient(GetEnumQueryParamClient());
TypeProviderWriter writer = new(clientProvider);
var codeFile = writer.Write();
Assert.AreEqual(Helpers.GetExpectedFromFile(), codeFile.Content);
Assert.AreEqual(Helpers.GetExpectedFromFile(isAsync.ToString()), codeFile.Content);
}
[Test]
@ -574,14 +575,19 @@ namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers.ClientProviders
private class ValidateQueryParamDiffClientProvider : ClientProvider
{
public ValidateQueryParamDiffClientProvider(InputClient client)
private readonly bool _isAsync;
public ValidateQueryParamDiffClientProvider(InputClient client, bool isAsync = false)
: base(client)
{
_isAsync = isAsync;
}
protected override MethodProvider[] BuildMethods()
{
var method = base.BuildMethods().Where(m => m.Signature.Parameters.Any(p => p.Name == "queryParam" && p.Type.Name == "InputEnum" && !m.Signature.Name.EndsWith("Async"))).First();
var method = base.BuildMethods().First(m => m.Signature.Parameters.Any(p =>
p is { Name: "queryParam", Type.Name: "InputEnum" } &&
((_isAsync && m.Signature.Name.EndsWith("Async")) || (!_isAsync && !m.Signature.Name.EndsWith("Async")))));
method.Update(xmlDocProvider: new XmlDocProvider()); // null out the docs
return [method];
}

Просмотреть файл

@ -0,0 +1,21 @@
// <auto-generated/>
#nullable disable
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Threading;
using System.Threading.Tasks;
using Sample.Models;
namespace Sample
{
/// <summary></summary>
public partial class TestClient
{
public virtual async global::System.Threading.Tasks.Task<global::System.ClientModel.ClientResult> OperationAsync(global::Sample.Models.InputEnum queryParam, global::System.Threading.CancellationToken cancellationToken = default)
{
return await this.OperationAsync(queryParam.ToString(), cancellationToken.CanBeCanceled ? new global::System.ClientModel.Primitives.RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
}
}
}

Просмотреть файл

@ -75,8 +75,8 @@ namespace Microsoft.Generator.CSharp.Snippets
public static ValueExpression Instance(CSharpType type, IReadOnlyDictionary<ValueExpression, ValueExpression> properties) => new NewInstanceExpression(type, [], new ObjectInitializerExpression(properties));
public static ScopedApi Instance(Type type, params ValueExpression[] arguments) => new NewInstanceExpression(type, arguments).As(type);
public static ScopedApi Instance(Type type, IReadOnlyDictionary<ValueExpression, ValueExpression> properties) => new NewInstanceExpression(type, [], new ObjectInitializerExpression(properties)).As(type);
public static ScopedApi<T> Instance<T>(IEnumerable<ValueExpression> arguments, IReadOnlyDictionary<ValueExpression, ValueExpression> properties)
=> new NewInstanceExpression(TypeReferenceExpression.GetTypeFromDefinition(typeof(T)), [.. arguments], new ObjectInitializerExpression(properties)).As<T>();
public static ScopedApi<T> Instance<T>(IEnumerable<ValueExpression> arguments, IReadOnlyDictionary<ValueExpression, ValueExpression> properties, bool useSingleLineForPropertyInitialization = false)
=> new NewInstanceExpression(TypeReferenceExpression.GetTypeFromDefinition(typeof(T)), [.. arguments], new ObjectInitializerExpression(properties, useSingleLineForPropertyInitialization)).As<T>();
public static ScopedApi<T> Instance<T>(params ValueExpression[] arguments)
=> new NewInstanceExpression(TypeReferenceExpression.GetTypeFromDefinition(typeof(T)), arguments).As<T>();
}

Просмотреть файл

@ -131,7 +131,7 @@ namespace UnbrandedTypeSpec
Argument.AssertNotNull(headParameter, nameof(headParameter));
Argument.AssertNotNull(queryParameter, nameof(queryParameter));
ClientResult result = await SayHiAsync(headParameter, queryParameter, optionalQuery, options: null).ConfigureAwait(false);
ClientResult result = await SayHiAsync(headParameter, queryParameter, optionalQuery, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -214,7 +214,7 @@ namespace UnbrandedTypeSpec
Argument.AssertNotNull(p1, nameof(p1));
Argument.AssertNotNull(action, nameof(action));
ClientResult result = await HelloAgainAsync(p2, p1, action, options: null).ConfigureAwait(false);
ClientResult result = await HelloAgainAsync(p2, p1, action, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((RoundTripModel)result, result.GetRawResponse());
}
@ -315,7 +315,7 @@ namespace UnbrandedTypeSpec
/// <exception cref="ClientResultException"> Service returned a non-success status code. </exception>
public virtual async Task<ClientResult<Thing>> HelloDemo2Async(CancellationToken cancellationToken = default)
{
ClientResult result = await HelloDemo2Async(options: null).ConfigureAwait(false);
ClientResult result = await HelloDemo2Async(cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -382,7 +382,7 @@ namespace UnbrandedTypeSpec
{
Argument.AssertNotNull(body, nameof(body));
ClientResult result = await CreateLiteralAsync(body, options: null).ConfigureAwait(false);
ClientResult result = await CreateLiteralAsync(body, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -433,7 +433,7 @@ namespace UnbrandedTypeSpec
/// <exception cref="ClientResultException"> Service returned a non-success status code. </exception>
public virtual async Task<ClientResult<Thing>> HelloLiteralAsync(CancellationToken cancellationToken = default)
{
ClientResult result = await HelloLiteralAsync(options: null).ConfigureAwait(false);
ClientResult result = await HelloLiteralAsync(cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -488,7 +488,7 @@ namespace UnbrandedTypeSpec
/// <exception cref="ClientResultException"> Service returned a non-success status code. </exception>
public virtual async Task<ClientResult<Thing>> TopActionAsync(DateTimeOffset action, CancellationToken cancellationToken = default)
{
ClientResult result = await TopActionAsync(action, options: null).ConfigureAwait(false);
ClientResult result = await TopActionAsync(action, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -689,7 +689,7 @@ namespace UnbrandedTypeSpec
optionalNullableList?.ToList() as IList<int> ?? new ChangeTrackingList<int>(),
requiredNullableList?.ToList() as IList<int> ?? new ChangeTrackingList<int>(),
null);
ClientResult result = await AnonymousBodyAsync(spreadModel, options: null).ConfigureAwait(false);
ClientResult result = await AnonymousBodyAsync(spreadModel, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -758,7 +758,7 @@ namespace UnbrandedTypeSpec
Argument.AssertNotNull(name, nameof(name));
Friend spreadModel = new Friend(name, null);
ClientResult result = await FriendlyModelAsync(spreadModel, options: null).ConfigureAwait(false);
ClientResult result = await FriendlyModelAsync(spreadModel, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Friend)result, result.GetRawResponse());
}
@ -808,7 +808,7 @@ namespace UnbrandedTypeSpec
/// <exception cref="ClientResultException"> Service returned a non-success status code. </exception>
public virtual async Task<ClientResult> AddTimeHeaderAsync(CancellationToken cancellationToken = default)
{
return await AddTimeHeaderAsync(options: null).ConfigureAwait(false);
return await AddTimeHeaderAsync(cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
}
/// <summary>
@ -876,7 +876,7 @@ namespace UnbrandedTypeSpec
Argument.AssertNotNull(name, nameof(name));
ProjectedModel spreadModel = new ProjectedModel(name, null);
ClientResult result = await ProjectedNameModelAsync(spreadModel, options: null).ConfigureAwait(false);
ClientResult result = await ProjectedNameModelAsync(spreadModel, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((ProjectedModel)result, result.GetRawResponse());
}
@ -927,7 +927,7 @@ namespace UnbrandedTypeSpec
/// <exception cref="ClientResultException"> Service returned a non-success status code. </exception>
public virtual async Task<ClientResult<ReturnsAnonymousModelResponse>> ReturnsAnonymousModelAsync(CancellationToken cancellationToken = default)
{
ClientResult result = await ReturnsAnonymousModelAsync(options: null).ConfigureAwait(false);
ClientResult result = await ReturnsAnonymousModelAsync(cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((ReturnsAnonymousModelResponse)result, result.GetRawResponse());
}
@ -994,7 +994,7 @@ namespace UnbrandedTypeSpec
{
Argument.AssertNotNull(accept, nameof(accept));
ClientResult result = await GetUnknownValueAsync(accept, options: null).ConfigureAwait(false);
ClientResult result = await GetUnknownValueAsync(accept, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue(result.GetRawResponse().Content.ToObjectFromJson<string>(), result.GetRawResponse());
}
@ -1061,7 +1061,7 @@ namespace UnbrandedTypeSpec
{
Argument.AssertNotNull(body, nameof(body));
ClientResult result = await InternalProtocolAsync(body, options: null).ConfigureAwait(false);
ClientResult result = await InternalProtocolAsync(body, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
return ClientResult.FromValue((Thing)result, result.GetRawResponse());
}
@ -1111,7 +1111,7 @@ namespace UnbrandedTypeSpec
/// <exception cref="ClientResultException"> Service returned a non-success status code. </exception>
public virtual async Task<ClientResult> StillConvenientAsync(CancellationToken cancellationToken = default)
{
return await StillConvenientAsync(options: null).ConfigureAwait(false);
return await StillConvenientAsync(cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
}
/// <summary>
@ -1176,7 +1176,7 @@ namespace UnbrandedTypeSpec
{
Argument.AssertNotNull(id, nameof(id));
return await HeadAsBooleanAsync(id, options: null).ConfigureAwait(false);
return await HeadAsBooleanAsync(id, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
}
/// <summary>
@ -1241,7 +1241,7 @@ namespace UnbrandedTypeSpec
{
Argument.AssertNotNull(p1, nameof(p1));
return await WithApiVersionAsync(p1, options: null).ConfigureAwait(false);
return await WithApiVersionAsync(p1, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
}
}
}