From 371d5c1adbfa4ad60480cdaa80356304c6841d1a Mon Sep 17 00:00:00 2001 From: Jorge Rangel <102122018+jorgerangel-msft@users.noreply.github.com> Date: Wed, 21 Aug 2024 18:27:56 -0500 Subject: [PATCH] Add support for spread of aliases & models (#4199) This PR adds support for spread of aliases & models within a client operation's parameters. fixes: https://github.com/microsoft/typespec/issues/3831 --------- Co-authored-by: m-nash <64171366+m-nash@users.noreply.github.com> --- .../src/Primitives/ScmKnownParameters.cs | 2 +- .../src/Providers/RestClientProvider.cs | 112 +++++++- .../Providers/ScmMethodProviderCollection.cs | 68 ++++- .../ClientProviders/ClientProviderTests.cs | 59 +++++ .../RestClientProviderTests.cs | 173 +++++++++++- .../ScmMethodProviderCollectionTests.cs | 49 +++- .../src/Primitives/ParameterLocation.cs | 3 + .../src/Providers/ParameterProvider.cs | 19 +- .../src/Shared/Conversions.cs | 3 + .../test/common/InputFactory.cs | 5 +- .../UnbrandedTypeSpecClient.RestClient.cs | 13 +- .../src/Generated/UnbrandedTypeSpecClient.cs | 248 +++++++++++++----- 12 files changed, 660 insertions(+), 94 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Primitives/ScmKnownParameters.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Primitives/ScmKnownParameters.cs index 142a73acf6..be4111cb05 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Primitives/ScmKnownParameters.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Primitives/ScmKnownParameters.cs @@ -33,7 +33,7 @@ public static ParameterProvider ClientOptions(CSharpType clientOptionsType) public static readonly ParameterProvider TokenAuth = new("tokenCredential", $"The token credential to copy", ClientModelPlugin.Instance.TypeFactory.TokenCredentialType()); public static readonly ParameterProvider MatchConditionsParameter = new("matchConditions", $"The content to send as the request conditions of the request.", ClientModelPlugin.Instance.TypeFactory.MatchConditionsType(), DefaultOf(ClientModelPlugin.Instance.TypeFactory.MatchConditionsType())); public static readonly ParameterProvider RequestOptions = new("options", $"The request options, which can override default behaviors of the client pipeline on a per-call basis.", typeof(RequestOptions)); - public static readonly ParameterProvider BinaryContent = new("content", $"The content to send as the body of the request.", typeof(BinaryContent)) { Validation = ParameterValidationType.AssertNotNull }; + public static readonly ParameterProvider BinaryContent = new("content", $"The content to send as the body of the request.", typeof(BinaryContent), location: ParameterLocation.Body) { Validation = ParameterValidationType.AssertNotNull }; // Known header parameters public static readonly ParameterProvider RepeatabilityRequestId = new("repeatabilityRequestId", FormattableStringHelpers.Empty, typeof(Guid)) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/RestClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/RestClientProvider.cs index 2a99952319..ecba0da7b9 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/RestClientProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/RestClientProvider.cs @@ -356,6 +356,43 @@ private static void GetParamInfo(Dictionary paramMap, } } + private static IReadOnlyList BuildSpreadParametersForModel(InputModelType inputModel) + { + var builtParameters = new ParameterProvider[inputModel.Properties.Count]; + + int index = 0; + foreach (var property in inputModel.Properties) + { + // convert the property to a parameter + var inputParameter = new InputParameter( + property.Name, + property.SerializedName, + property.Description, + property.Type, + RequestLocation.Body, + null, + InputOperationParameterKind.Method, + property.IsRequired, + false, + false, + false, + false, + false, + false, + null, + null); + + var paramProvider = ClientModelPlugin.Instance.TypeFactory.CreateParameter(inputParameter); + paramProvider.DefaultValue = !inputParameter.IsRequired ? Default : null; + paramProvider.SpreadSource = ClientModelPlugin.Instance.TypeFactory.CreateModel(inputModel); + paramProvider.Type = paramProvider.Type.InputType; + + builtParameters[index++] = paramProvider; + } + + return builtParameters; + } + private static bool TryGetSpecialHeaderParam(InputParameter inputParameter, [NotNullWhen(true)] out ParameterProvider? parameterProvider) { if (inputParameter.Location == RequestLocation.Header) @@ -375,12 +412,22 @@ internal MethodProvider GetCreateRequestMethod(InputOperation operation) internal static List GetMethodParameters(InputOperation operation, bool isProtocol = false) { - List methodParameters = new(); + SortedList sortedParams = []; + int path = 0; + int required = 100; + int bodyRequired = 200; + int bodyOptional = 300; + int contentType = 400; + int optional = 500; + foreach (InputParameter inputParam in operation.Parameters) { - if (inputParam.Kind != InputOperationParameterKind.Method || TryGetSpecialHeaderParam(inputParam, out var _)) + if ((inputParam.Kind != InputOperationParameterKind.Method && inputParam.Kind != InputOperationParameterKind.Spread) + || TryGetSpecialHeaderParam(inputParam, out var _)) continue; + var spreadInputModel = inputParam.Kind == InputOperationParameterKind.Spread ? GetSpreadParameterModel(inputParam) : null; + ParameterProvider? parameter = ClientModelPlugin.Instance.TypeFactory.CreateParameter(inputParam); if (isProtocol) @@ -394,11 +441,66 @@ internal static List GetMethodParameters(InputOperation opera parameter.Type = parameter.Type.IsEnum ? parameter.Type.UnderlyingEnumType : parameter.Type; } } + else if (spreadInputModel != null) + { + foreach (var bodyParam in BuildSpreadParametersForModel(spreadInputModel)) + { + if (bodyParam.DefaultValue is null) + { + sortedParams.Add(bodyRequired++, bodyParam); + } + else + { + sortedParams.Add(bodyOptional++, bodyParam); + } + } + continue; + } + + if (parameter is null) + continue; - if (parameter is not null) - methodParameters.Add(parameter); + switch (parameter.Location) + { + case ParameterLocation.Path: + case ParameterLocation.Uri: + sortedParams.Add(path++, parameter); + break; + case ParameterLocation.Query: + case ParameterLocation.Header: + if (inputParam.IsContentType) + { + sortedParams.Add(contentType++, parameter); + } + else if (parameter.Validation != ParameterValidationType.None) + { + sortedParams.Add(required++, parameter); + } + else + { + sortedParams.Add(optional++, parameter); + } + break; + case ParameterLocation.Body: + sortedParams.Add(bodyRequired++, parameter); + break; + default: + sortedParams.Add(optional++, parameter); + break; + } + } + + return [.. sortedParams.Values]; + } + + internal static InputModelType GetSpreadParameterModel(InputParameter inputParam) + { + if (inputParam.Kind.HasFlag(InputOperationParameterKind.Spread) && inputParam.Type is InputModelType model) + { + return model; } - return methodParameters; + + throw new InvalidOperationException($"inputParam `{inputParam.Name}` is `Spread` but not a model type"); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ScmMethodProviderCollection.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ScmMethodProviderCollection.cs index 88aec9321f..187e043432 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ScmMethodProviderCollection.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ScmMethodProviderCollection.cs @@ -64,6 +64,7 @@ private MethodProvider BuildConvenienceMethod(MethodProvider protocolMethod, boo { methodModifier |= MethodSignatureModifiers.Async; } + var methodSignature = new MethodSignature( isAsync ? _cleanOperationName + "Async" : _cleanOperationName, FormattableStringHelpers.FromString(Operation.Description), @@ -74,12 +75,13 @@ private MethodProvider BuildConvenienceMethod(MethodProvider protocolMethod, boo var processMessageName = isAsync ? "ProcessMessageAsync" : "ProcessMessage"; MethodBodyStatement[] methodBody; + if (responseBodyType is null) { methodBody = [ - .. GetStackVariablesForProtocolParamConversion(ConvenienceMethodParameters, out var paramDeclarations), - Return(This.Invoke(protocolMethod.Signature, [.. GetParamConversions(ConvenienceMethodParameters, paramDeclarations), Null], isAsync)) + .. GetStackVariablesForProtocolParamConversion(ConvenienceMethodParameters, out var declarations), + Return(This.Invoke(protocolMethod.Signature, [.. GetParamConversions(ConvenienceMethodParameters, declarations), Null], isAsync)) ]; } else @@ -88,11 +90,11 @@ .. GetStackVariablesForProtocolParamConversion(ConvenienceMethodParameters, out [ .. GetStackVariablesForProtocolParamConversion(ConvenienceMethodParameters, out var paramDeclarations), Declare("result", This.Invoke(protocolMethod.Signature, [.. GetParamConversions(ConvenienceMethodParameters, paramDeclarations), Null], isAsync).As(), out ScopedApi result), - .. GetStackVariablesForReturnValueConversion(result, responseBodyType, isAsync, out var declarations), + .. GetStackVariablesForReturnValueConversion(result, responseBodyType, isAsync, out var resultDeclarations), Return(Static().Invoke( nameof(ClientResult.FromValue), [ - GetResultConversion(result, responseBodyType, declarations), + GetResultConversion(result, responseBodyType, resultDeclarations), result.Invoke("GetRawResponse") ])), ]; @@ -109,6 +111,9 @@ private IEnumerable GetStackVariablesForProtocolParamConver declarations = new Dictionary(); foreach (var parameter in convenienceMethodParameters) { + if (parameter.SpreadSource is not null) + continue; + if (parameter.Location == ParameterLocation.Body) { if (parameter.Type.IsReadOnlyMemory) @@ -136,9 +141,53 @@ private IEnumerable GetStackVariablesForProtocolParamConver } } } + + // add spread parameter model variable declaration + var spreadSource = convenienceMethodParameters.FirstOrDefault(p => p.SpreadSource is not null)?.SpreadSource; + if (spreadSource is not null) + { + statements.Add(Declare("spreadModel", New.Instance(spreadSource.Type, [.. GetSpreadConversion(spreadSource)]).As(spreadSource.Type), out var spread)); + declarations["spread"] = spread; + } + return statements; } + private List GetSpreadConversion(TypeProvider spreadSource) + { + var convenienceMethodParams = ConvenienceMethodParameters.ToDictionary(p => p.Name); + List expressions = new(spreadSource.Properties.Count); + // we should make this find more deterministic + var ctor = spreadSource.Constructors.First(c => c.Signature.Parameters.Count == spreadSource.Properties.Count + 1 && + c.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Internal)); + + foreach (var param in ctor.Signature.Parameters) + { + if (convenienceMethodParams.TryGetValue(param.Name, out var convenienceParam)) + { + if (convenienceParam.Type.IsList) + { + var interfaceType = param.Property!.WireInfo?.IsReadOnly == true + ? new CSharpType(typeof(IReadOnlyList<>), convenienceParam.Type.Arguments) + : new CSharpType(typeof(IList<>), convenienceParam.Type.Arguments); + expressions.Add(NullCoalescing( + new AsExpression(convenienceParam.NullConditional().ToList(), interfaceType), + New.Instance(convenienceParam.Type.PropertyInitializationType, []))); + } + else + { + expressions.Add(convenienceParam); + } + } + else + { + expressions.Add(Null); + } + } + + return expressions; + } + private IEnumerable GetStackVariablesForReturnValueConversion(ScopedApi result, CSharpType responseBodyType, bool isAsync, out Dictionary declarations) { if (responseBodyType.IsList) @@ -216,9 +265,18 @@ private ValueExpression GetResultConversion(ScopedApi result, CSha private IReadOnlyList GetParamConversions(IReadOnlyList convenienceMethodParameters, Dictionary declarations) { List conversions = new List(); + bool addedSpreadSource = false; foreach (var param in convenienceMethodParameters) { - if (param.Location == ParameterLocation.Body) + if (param.SpreadSource is not null) + { + if (!addedSpreadSource) + { + conversions.Add(declarations["spread"]); + addedSpreadSource = true; + } + } + else if (param.Location == ParameterLocation.Body) { if (param.Type.IsReadOnlyMemory || param.Type.IsList) { diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs index c86f817d43..4df37f6099 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; using System.Linq; @@ -25,6 +26,13 @@ public class ClientProviderTests 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 InputModelType _spreadModel = InputFactory.Model( + "spreadModel", + usage: InputModelTypeUsage.Spread, + properties: + [ + InputFactory.Property("p1", InputPrimitiveType.String, isRequired: true), + ]); [SetUp] public void SetUp() @@ -365,6 +373,33 @@ public void ValidateQueryParamWriterDiff() Assert.AreEqual(Helpers.GetExpectedFromFile(), codeFile.Content); } + [TestCaseSource(nameof(ValidateClientWithSpreadTestCases))] + public void ValidateClientWithSpread(InputClient inputClient) + { + var clientProvider = new ClientProvider(inputClient); + var methods = clientProvider.Methods; + + Assert.AreEqual(4, methods.Count); + + var protocolMethods = methods.Where(m => m.Signature.Parameters.Any(p => p.Type.Equals(typeof(BinaryContent)))).ToList(); + Assert.AreEqual(2, protocolMethods.Count); + Assert.AreEqual(2, protocolMethods[0].Signature.Parameters.Count); + Assert.AreEqual(2, protocolMethods[1].Signature.Parameters.Count); + + Assert.AreEqual(new CSharpType(typeof(BinaryContent)), protocolMethods[0].Signature.Parameters[0].Type); + Assert.AreEqual(new CSharpType(typeof(RequestOptions)), protocolMethods[0].Signature.Parameters[1].Type); + Assert.AreEqual(new CSharpType(typeof(BinaryContent)), protocolMethods[1].Signature.Parameters[0].Type); + Assert.AreEqual(new CSharpType(typeof(RequestOptions)), protocolMethods[1].Signature.Parameters[1].Type); + + var convenienceMethods = methods.Where(m => m.Signature.Parameters.Any(p => p.Type.Equals(typeof(string)))).ToList(); + Assert.AreEqual(2, convenienceMethods.Count); + Assert.AreEqual(1, convenienceMethods[0].Signature.Parameters.Count); + + Assert.AreEqual(new CSharpType(typeof(string)), convenienceMethods[0].Signature.Parameters[0].Type); + Assert.AreEqual("p1", convenienceMethods[0].Signature.Parameters[0].Name); + + } + private static InputClient GetEnumQueryParamClient() => InputFactory.Client( TestClientName, @@ -386,6 +421,7 @@ private static InputClient GetEnumQueryParamClient() InputFactory.EnumMember.String("value1", "value1"), InputFactory.EnumMember.String("value2", "value2") ]), + isRequired: true, location: RequestLocation.Query) ]) ]); @@ -469,6 +505,29 @@ public static IEnumerable SubClientTestCases } } + public static IEnumerable ValidateClientWithSpreadTestCases + { + get + { + yield return new TestCaseData(InputFactory.Client( + TestClientName, + operations: + [ + InputFactory.Operation( + "CreateMessage", + parameters: + [ + InputFactory.Parameter( + "spread", + _spreadModel, + location: RequestLocation.Body, + isRequired: true, + kind: InputOperationParameterKind.Spread), + ]) + ])); + } + } + public static IEnumerable BuildConstructorsTestCases { get diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/RestClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/RestClientProviderTests.cs index c182da5c4c..39ac3d5c2b 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/RestClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ClientProviders/RestClientProviderTests.cs @@ -4,17 +4,28 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.Generator.CSharp.ClientModel.Providers; using Microsoft.Generator.CSharp.Input; using Microsoft.Generator.CSharp.Primitives; using Microsoft.Generator.CSharp.Providers; using Microsoft.Generator.CSharp.Tests.Common; using NUnit.Framework; +using Microsoft.Generator.CSharp.Snippets; namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers.ClientProviders { public class RestClientProviderTests { + private static readonly InputModelType _spreadModel = InputFactory.Model( + "spreadModel", + usage: InputModelTypeUsage.Spread, + properties: + [ + InputFactory.Property("p1", InputPrimitiveType.String, isRequired: true), + InputFactory.Property("optionalProp", InputPrimitiveType.String, isRequired: false) + ]); + public RestClientProviderTests() { MockHelpers.LoadMockPlugin(); @@ -120,6 +131,58 @@ public void TestGetMethodParameters(InputOperation inputOperation) p.Name.Equals("repeatabilityFirstSent", StringComparison.OrdinalIgnoreCase) && p.Name.Equals("repeatabilityRequestId", StringComparison.OrdinalIgnoreCase))); } + + var spreadInputParameter = inputOperation.Parameters.FirstOrDefault(p => p.Kind == InputOperationParameterKind.Spread); + if (spreadInputParameter != null) + { + Assert.AreEqual(_spreadModel.Properties.Count + 1, methodParameters.Count); + // validate path parameter + Assert.AreEqual(inputOperation.Parameters[1].Name, methodParameters[0].Name); + // validate spread parameters + Assert.AreEqual(_spreadModel.Properties[0].Name, methodParameters[1].Name); + Assert.IsNull(methodParameters[1].DefaultValue); + // validate optional parameter + Assert.AreEqual(_spreadModel.Properties[1].Name, methodParameters[2].Name); + Assert.AreEqual(Snippet.Default, methodParameters[2].DefaultValue); + } + } + + [TestCase] + public void TestGetMethodParameters_ProperOrdering() + { + var methodParameters = RestClientProvider.GetMethodParameters(OperationWithMixedParamOrdering); + + Assert.AreEqual(OperationWithMixedParamOrdering.Parameters.Count, methodParameters.Count); + + // validate ordering + Assert.AreEqual("requiredPath", methodParameters[0].Name); + Assert.AreEqual("requiredQuery", methodParameters[1].Name); + Assert.AreEqual("requiredHeader", methodParameters[2].Name); + Assert.AreEqual("body", methodParameters[3].Name); + Assert.AreEqual("contentType", methodParameters[4].Name); + Assert.AreEqual("optionalQuery", methodParameters[5].Name); + Assert.AreEqual("optionalHeader", methodParameters[6].Name); + + var orderedPathParams = RestClientProvider.GetMethodParameters(OperationWithOnlyPathParams); + Assert.AreEqual(OperationWithOnlyPathParams.Parameters.Count, orderedPathParams.Count); + Assert.AreEqual("c", orderedPathParams[0].Name); + Assert.AreEqual("a", orderedPathParams[1].Name); + Assert.AreEqual("b", orderedPathParams[2].Name); + } + + [TestCaseSource(nameof(GetSpreadParameterModelTestCases))] + public void TestGetSpreadParameterModel(InputParameter inputParameter) + { + if (inputParameter.Kind == InputOperationParameterKind.Spread) + { + var model = RestClientProvider.GetSpreadParameterModel(inputParameter); + Assert.AreEqual(_spreadModel, model); + } + else + { + // assert throws + Assert.Throws(() => RestClientProvider.GetSpreadParameterModel(inputParameter)); + } } [Test] @@ -151,6 +214,103 @@ public void ValidateClientWithSpecialHeaders() InputFactory.Parameter("message", InputPrimitiveType.Boolean, isRequired: true) ]); + private readonly static InputOperation OperationWithSpreadParam = InputFactory.Operation( + "CreateMessageWithSpread", + parameters: + [ + InputFactory.Parameter( + "spread", + _spreadModel, + location: RequestLocation.Body, + isRequired: true, + kind: InputOperationParameterKind.Spread), + InputFactory.Parameter( + "p2", + InputPrimitiveType.Boolean, + location: RequestLocation.Path, + isRequired: true, + kind: InputOperationParameterKind.Method) + ]); + + private static readonly InputOperation OperationWithMixedParamOrdering = InputFactory.Operation( + "CreateMessage", + parameters: + [ + // require query param + InputFactory.Parameter( + "requiredQuery", + InputPrimitiveType.String, + location: RequestLocation.Query, + isRequired: true, + kind: InputOperationParameterKind.Method), + // optional query param + InputFactory.Parameter( + "optionalQuery", + InputPrimitiveType.String, + location: RequestLocation.Query, + isRequired: false, + kind: InputOperationParameterKind.Method), + // required path param + InputFactory.Parameter( + "requiredPath", + InputPrimitiveType.String, + location: RequestLocation.Path, + isRequired: true, + kind: InputOperationParameterKind.Method), + // required header param + InputFactory.Parameter( + "requiredHeader", + InputPrimitiveType.String, + location: RequestLocation.Header, + isRequired: true, + kind: InputOperationParameterKind.Method), + // optional header param + InputFactory.Parameter( + "optionalHeader", + InputPrimitiveType.String, + location: RequestLocation.Header, + isRequired: false, + kind: InputOperationParameterKind.Method), + // content type param + InputFactory.Parameter( + "contentType", + InputPrimitiveType.String, + location: RequestLocation.Header, + isContentType: true, + kind: InputOperationParameterKind.Method), + // body param + InputFactory.Parameter( + "body", + InputPrimitiveType.String, + location: RequestLocation.Body, + isRequired: true, + kind: InputOperationParameterKind.Method) + ]); + + private static readonly InputOperation OperationWithOnlyPathParams = InputFactory.Operation( + "CreateMessage", + parameters: + [ + InputFactory.Parameter( + "c", + InputPrimitiveType.String, + location: RequestLocation.Path, + isRequired: true, + kind: InputOperationParameterKind.Method), + InputFactory.Parameter( + "a", + InputPrimitiveType.String, + location: RequestLocation.Path, + isRequired: true, + kind: InputOperationParameterKind.Method), + InputFactory.Parameter( + "b", + InputPrimitiveType.String, + location: RequestLocation.Path, + isRequired: true, + kind: InputOperationParameterKind.Method) + ]); + private readonly static InputClient SingleOpInputClient = InputFactory.Client("TestClient", operations: [BasicOperation]); private static IEnumerable DefaultCSharpMethodCollectionTestCases => @@ -160,7 +320,18 @@ public void ValidateClientWithSpecialHeaders() private static IEnumerable GetMethodParametersTestCases => [ - new TestCaseData(BasicOperation) + new TestCaseData(OperationWithSpreadParam), + new TestCaseData(BasicOperation), + new TestCaseData(OperationWithMixedParamOrdering) + ]; + + private static IEnumerable GetSpreadParameterModelTestCases => + [ + // spread param + new TestCaseData(InputFactory.Parameter("spread", _spreadModel, location: RequestLocation.Body, kind: InputOperationParameterKind.Spread, isRequired: true)), + // non spread param + new TestCaseData(InputFactory.Parameter("p1", InputPrimitiveType.Boolean, location: RequestLocation.Path, isRequired: true, kind: InputOperationParameterKind.Method)) + ]; private class MockClientProvider : RestClientProvider diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs index ee8777147e..c5bed2c234 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/test/Providers/ScmMethodProviderCollectionTests.cs @@ -2,10 +2,10 @@ // Licensed under the MIT License. using System.Collections.Generic; +using System.Linq; using Microsoft.Generator.CSharp.ClientModel.Providers; using Microsoft.Generator.CSharp.Input; using Microsoft.Generator.CSharp.Primitives; -using Microsoft.Generator.CSharp.Providers; using Microsoft.Generator.CSharp.Tests.Common; using NUnit.Framework; @@ -13,6 +13,14 @@ namespace Microsoft.Generator.CSharp.ClientModel.Tests.Providers { internal class ScmMethodProviderCollectionTests { + private static readonly InputModelType _spreadModel = InputFactory.Model( + "spreadModel", + usage: InputModelTypeUsage.Spread, + properties: + [ + InputFactory.Property("p2", InputPrimitiveType.String, isRequired: true), + ]); + // Validate that the default method collection consists of the expected method kind(s) [TestCaseSource(nameof(DefaultCSharpMethodCollectionTestCases))] public void TestDefaultCSharpMethodCollection(InputOperation inputOperation) @@ -20,8 +28,7 @@ public void TestDefaultCSharpMethodCollection(InputOperation inputOperation) var inputClient = InputFactory.Client("TestClient", operations: [inputOperation]); MockHelpers.LoadMockPlugin( - createCSharpTypeCore: (inputType) => new CSharpType(typeof(bool)), - createParameterCore: (inputParameter) => new ParameterProvider("mockParam", $"mock description", typeof(bool), null)); + createCSharpTypeCore: (inputType) => new CSharpType(typeof(bool))); var methodCollection = new ScmMethodProviderCollection(inputOperation, ClientModelPlugin.Instance.TypeFactory.CreateClient(inputClient)); Assert.IsNotNull(methodCollection); @@ -35,6 +42,23 @@ public void TestDefaultCSharpMethodCollection(InputOperation inputOperation) var parameters = signature.Parameters; Assert.IsNotNull(parameters); Assert.AreEqual(inputOperation.Parameters.Count + 1, parameters.Count); + + var convenienceMethod = methodCollection.FirstOrDefault(m + => !m.Signature.Parameters.Any(p => p.Name == "content") + && m.Signature.Name == $"{inputOperation.Name.ToCleanName()}"); + Assert.IsNotNull(convenienceMethod); + + var convenienceMethodParams = convenienceMethod!.Signature.Parameters; + Assert.IsNotNull(convenienceMethodParams); + + var spreadInputParameter = inputOperation.Parameters.FirstOrDefault(p => p.Kind == InputOperationParameterKind.Spread); + if (spreadInputParameter != null) + { + var spreadModelProperties = _spreadModel.Properties; + Assert.AreEqual(spreadModelProperties.Count + 1, convenienceMethodParams.Count); + Assert.AreEqual("p1", convenienceMethodParams[0].Name); + Assert.AreEqual(spreadModelProperties[0].Name, convenienceMethodParams[1].Name); + } } public static IEnumerable DefaultCSharpMethodCollectionTestCases @@ -50,6 +74,25 @@ public static IEnumerable DefaultCSharpMethodCollectionTestCases InputPrimitiveType.Boolean, isRequired: true) ])); + + // Operation with spread parameter + yield return new TestCaseData(InputFactory.Operation( + "CreateMessage", + parameters: + [ + InputFactory.Parameter( + "spread", + _spreadModel, + location: RequestLocation.Body, + isRequired: true, + kind: InputOperationParameterKind.Spread), + InputFactory.Parameter( + "p1", + InputPrimitiveType.Boolean, + location: RequestLocation.Path, + isRequired: true, + kind: InputOperationParameterKind.Method) + ])); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Primitives/ParameterLocation.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Primitives/ParameterLocation.cs index fa8cb5c4b4..374a91ceb0 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Primitives/ParameterLocation.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Primitives/ParameterLocation.cs @@ -8,5 +8,8 @@ public enum ParameterLocation Unknown, Body, Uri, + Path, + Query, + Header, } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ParameterProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ParameterProvider.cs index db34edd9d6..4627d044ea 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ParameterProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ParameterProvider.cs @@ -23,7 +23,7 @@ public sealed class ParameterProvider : IEquatable /// /// The default value of the parameter. /// - public ValueExpression? DefaultValue { get; init; } + public ValueExpression? DefaultValue { get; set; } public ValueExpression? InitializationValue { get; init; } public ParameterValidationType Validation { get; init; } = ParameterValidationType.None; public bool IsRef { get; } @@ -50,8 +50,15 @@ public ParameterProvider(InputParameter inputParameter) { Name = inputParameter.Name; Description = FormattableStringHelpers.FromString(inputParameter.Description) ?? FormattableStringHelpers.Empty; - Type = CodeModelPlugin.Instance.TypeFactory.CreateCSharpType(inputParameter.Type) ?? throw new InvalidOperationException($"Failed to create CSharpType for {inputParameter.Type}"); - Validation = inputParameter.IsRequired && !Type.IsValueType ? ParameterValidationType.AssertNotNull : ParameterValidationType.None; + var type = CodeModelPlugin.Instance.TypeFactory.CreateCSharpType(inputParameter.Type) ?? throw new InvalidOperationException($"Failed to create CSharpType for {inputParameter.Type}"); + if (!inputParameter.IsRequired && !type.IsCollection) + { + type = type.WithNullable(true); + } + Type = type; + Validation = inputParameter.IsRequired && !Type.IsValueType && !Type.IsNullable + ? ParameterValidationType.AssertNotNull + : ParameterValidationType.None; WireInfo = new WireInformation(CodeModelPlugin.Instance.TypeFactory.GetSerializationFormat(inputParameter.Type), inputParameter.NameInRequest); Location = inputParameter.Location.ToParameterLocation(); } @@ -66,7 +73,8 @@ public ParameterProvider( IReadOnlyList? attributes = null, PropertyProvider? property = null, FieldProvider? field = null, - ValueExpression? initializationValue = null) + ValueExpression? initializationValue = null, + ParameterLocation? location = null) { Debug.Assert(!(property is not null && field is not null), "A parameter cannot be both a property and a field"); @@ -82,6 +90,7 @@ public ParameterProvider( Validation = GetParameterValidation(); InitializationValue = initializationValue; WireInfo = new WireInformation(SerializationFormat.Default, name); + Location = location ?? ParameterLocation.Unknown; } private ParameterProvider? _inputParameter; @@ -162,6 +171,8 @@ private static VariableExpression GetVariableExpression(ParameterProvider parame private VariableExpression? _asVariable; public VariableExpression AsExpression => _asVariable ??= this; + public TypeProvider? SpreadSource { get; set; } + private ParameterValidationType GetParameterValidation() { if (Field is not null && !Field.Type.IsNullable) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Shared/Conversions.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Shared/Conversions.cs index 77ae0fe944..97d35fa3d6 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Shared/Conversions.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Shared/Conversions.cs @@ -13,6 +13,9 @@ public static ParameterLocation ToParameterLocation(this RequestLocation locatio { RequestLocation.Body => ParameterLocation.Body, RequestLocation.Uri => ParameterLocation.Uri, + RequestLocation.Path => ParameterLocation.Path, + RequestLocation.Query => ParameterLocation.Query, + RequestLocation.Header => ParameterLocation.Header, _ => ParameterLocation.Unknown, }; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/common/InputFactory.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/common/InputFactory.cs index a332bb0944..7421cd8280 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/common/InputFactory.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/common/InputFactory.cs @@ -61,7 +61,8 @@ public static InputParameter Parameter( bool isRequired = false, InputOperationParameterKind kind = InputOperationParameterKind.Method, bool isEndpoint = false, - bool isResourceParameter = false) + bool isResourceParameter = false, + bool isContentType = false) { return new InputParameter( name, @@ -74,7 +75,7 @@ public static InputParameter Parameter( isRequired, false, isResourceParameter, - false, + isContentType, isEndpoint, false, false, diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs index ffdb1447d0..85b55db090 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.RestClient.cs @@ -42,7 +42,7 @@ internal PipelineMessage CreateSayHiRequest(string headParameter, string queryPa return message; } - internal PipelineMessage CreateHelloAgainRequest(string p1, string p2, BinaryContent content, RequestOptions options) + internal PipelineMessage CreateHelloAgainRequest(string p2, string p1, BinaryContent content, RequestOptions options) { PipelineMessage message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -61,7 +61,7 @@ internal PipelineMessage CreateHelloAgainRequest(string p1, string p2, BinaryCon return message; } - internal PipelineMessage CreateNoContentTypeRequest(string p1, string p2, BinaryContent content, RequestOptions options) + internal PipelineMessage CreateNoContentTypeRequest(string p2, string p1, BinaryContent content, RequestOptions options) { PipelineMessage message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -178,7 +178,7 @@ internal PipelineMessage CreatePatchActionRequest(BinaryContent content, Request return message; } - internal PipelineMessage CreateAnonymousBodyRequest(RequestOptions options) + internal PipelineMessage CreateAnonymousBodyRequest(BinaryContent content, RequestOptions options) { PipelineMessage message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -190,11 +190,12 @@ internal PipelineMessage CreateAnonymousBodyRequest(RequestOptions options) request.Uri = uri.ToUri(); request.Headers.Set("Content-Type", "application/json"); request.Headers.Set("Accept", "application/json"); + request.Content = content; message.Apply(options); return message; } - internal PipelineMessage CreateFriendlyModelRequest(RequestOptions options) + internal PipelineMessage CreateFriendlyModelRequest(BinaryContent content, RequestOptions options) { PipelineMessage message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -206,6 +207,7 @@ internal PipelineMessage CreateFriendlyModelRequest(RequestOptions options) request.Uri = uri.ToUri(); request.Headers.Set("Content-Type", "application/json"); request.Headers.Set("Accept", "application/json"); + request.Content = content; message.Apply(options); return message; } @@ -225,7 +227,7 @@ internal PipelineMessage CreateAddTimeHeaderRequest(RequestOptions options) return message; } - internal PipelineMessage CreateProjectedNameModelRequest(RequestOptions options) + internal PipelineMessage CreateProjectedNameModelRequest(BinaryContent content, RequestOptions options) { PipelineMessage message = Pipeline.CreateMessage(); message.ResponseClassifier = PipelineMessageClassifier200; @@ -237,6 +239,7 @@ internal PipelineMessage CreateProjectedNameModelRequest(RequestOptions options) request.Uri = uri.ToUri(); request.Headers.Set("Content-Type", "application/json"); request.Headers.Set("Accept", "application/json"); + request.Content = content; message.Apply(options); return message; } diff --git a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs index b2bb41bf27..21d3f1aa5f 100644 --- a/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs +++ b/packages/http-client-csharp/generator/TestProjects/Local/Unbranded-TypeSpec/src/Generated/UnbrandedTypeSpecClient.cs @@ -5,6 +5,8 @@ using System; using System.ClientModel; using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using UnbrandedTypeSpec.Models; @@ -137,20 +139,20 @@ public virtual async Task> SayHiAsync(string headParameter, /// /// /// - /// /// + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// , or is null. + /// , or is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult HelloAgain(string p1, string p2, BinaryContent content, RequestOptions options) + public virtual ClientResult HelloAgain(string p2, string p1, BinaryContent content, RequestOptions options) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(content, nameof(content)); - using PipelineMessage message = CreateHelloAgainRequest(p1, p2, content, options); + using PipelineMessage message = CreateHelloAgainRequest(p2, p1, content, options); return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options)); } @@ -162,52 +164,52 @@ public virtual ClientResult HelloAgain(string p1, string p2, BinaryContent conte /// /// /// - /// /// + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// , or is null. + /// , or is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task HelloAgainAsync(string p1, string p2, BinaryContent content, RequestOptions options) + public virtual async Task HelloAgainAsync(string p2, string p1, BinaryContent content, RequestOptions options) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(content, nameof(content)); - using PipelineMessage message = CreateHelloAgainRequest(p1, p2, content, options); + using PipelineMessage message = CreateHelloAgainRequest(p2, p1, content, options); return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } /// Return hi again. - /// /// + /// /// - /// , or is null. + /// , or is null. /// Service returned a non-success status code. - public virtual ClientResult HelloAgain(string p1, string p2, RoundTripModel action) + public virtual ClientResult HelloAgain(string p2, string p1, RoundTripModel action) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(action, nameof(action)); - ClientResult result = HelloAgain(p1, p2, action, null); + ClientResult result = HelloAgain(p2, p1, action, null); return ClientResult.FromValue((RoundTripModel)result, result.GetRawResponse()); } /// Return hi again. - /// /// + /// /// - /// , or is null. + /// , or is null. /// Service returned a non-success status code. - public virtual async Task> HelloAgainAsync(string p1, string p2, RoundTripModel action) + public virtual async Task> HelloAgainAsync(string p2, string p1, RoundTripModel action) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(action, nameof(action)); - ClientResult result = await HelloAgainAsync(p1, p2, action, null).ConfigureAwait(false); + ClientResult result = await HelloAgainAsync(p2, p1, action, null).ConfigureAwait(false); return ClientResult.FromValue((RoundTripModel)result, result.GetRawResponse()); } @@ -219,20 +221,20 @@ public virtual async Task> HelloAgainAsync(string p /// /// /// - /// /// + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// , or is null. + /// , or is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult NoContentType(string p1, string p2, BinaryContent content, RequestOptions options) + public virtual ClientResult NoContentType(string p2, string p1, BinaryContent content, RequestOptions options) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(content, nameof(content)); - using PipelineMessage message = CreateNoContentTypeRequest(p1, p2, content, options); + using PipelineMessage message = CreateNoContentTypeRequest(p2, p1, content, options); return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options)); } @@ -244,52 +246,52 @@ public virtual ClientResult NoContentType(string p1, string p2, BinaryContent co /// /// /// - /// /// + /// /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. - /// , or is null. + /// , or is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task NoContentTypeAsync(string p1, string p2, BinaryContent content, RequestOptions options) + public virtual async Task NoContentTypeAsync(string p2, string p1, BinaryContent content, RequestOptions options) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(content, nameof(content)); - using PipelineMessage message = CreateNoContentTypeRequest(p1, p2, content, options); + using PipelineMessage message = CreateNoContentTypeRequest(p2, p1, content, options); return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } /// Return hi again. - /// /// + /// /// - /// , or is null. + /// , or is null. /// Service returned a non-success status code. - public virtual ClientResult NoContentType(string p1, string p2, RoundTripModel action) + public virtual ClientResult NoContentType(string p2, string p1, RoundTripModel action) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(action, nameof(action)); - ClientResult result = NoContentType(p1, p2, action, null); + ClientResult result = NoContentType(p2, p1, action, null); return ClientResult.FromValue((RoundTripModel)result, result.GetRawResponse()); } /// Return hi again. - /// /// + /// /// - /// , or is null. + /// , or is null. /// Service returned a non-success status code. - public virtual async Task> NoContentTypeAsync(string p1, string p2, RoundTripModel action) + public virtual async Task> NoContentTypeAsync(string p2, string p1, RoundTripModel action) { - Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(p2, nameof(p2)); + Argument.AssertNotNull(p1, nameof(p1)); Argument.AssertNotNull(action, nameof(action)); - ClientResult result = await NoContentTypeAsync(p1, p2, action, null).ConfigureAwait(false); + ClientResult result = await NoContentTypeAsync(p2, p1, action, null).ConfigureAwait(false); return ClientResult.FromValue((RoundTripModel)result, result.GetRawResponse()); } @@ -637,12 +639,16 @@ public virtual async Task> PatchActionAsync(Thing body) /// /// /// + /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult AnonymousBody(RequestOptions options) + public virtual ClientResult AnonymousBody(BinaryContent content, RequestOptions options) { - using PipelineMessage message = CreateAnonymousBodyRequest(options); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateAnonymousBodyRequest(content, options); return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options)); } @@ -654,28 +660,98 @@ public virtual ClientResult AnonymousBody(RequestOptions options) /// /// /// + /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task AnonymousBodyAsync(RequestOptions options) + public virtual async Task AnonymousBodyAsync(BinaryContent content, RequestOptions options) { - using PipelineMessage message = CreateAnonymousBodyRequest(options); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateAnonymousBodyRequest(content, options); return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } /// body parameter without body decorator. - /// Service returned a non-success status code. - public virtual ClientResult AnonymousBody() - { - ClientResult result = AnonymousBody(null); + /// name of the Thing. + /// required Union. + /// required literal string. + /// required literal int. + /// required literal float. + /// required literal bool. + /// description with xml <|endoftext|>. + /// required nullable collection. + /// optional literal string. + /// optional literal int. + /// optional literal float. + /// optional literal bool. + /// optional nullable collection. + /// , or is null. + /// Service returned a non-success status code. + public virtual ClientResult AnonymousBody(string name, BinaryData requiredUnion, AnonymousBodyRequestRequiredLiteralString requiredLiteralString, AnonymousBodyRequestRequiredLiteralInt requiredLiteralInt, AnonymousBodyRequestRequiredLiteralFloat requiredLiteralFloat, bool requiredLiteralBool, string requiredBadDescription, IEnumerable requiredNullableList, AnonymousBodyRequestOptionalLiteralString? optionalLiteralString = default, AnonymousBodyRequestOptionalLiteralInt? optionalLiteralInt = default, AnonymousBodyRequestOptionalLiteralFloat? optionalLiteralFloat = default, bool? optionalLiteralBool = default, IEnumerable optionalNullableList = default) + { + Argument.AssertNotNull(name, nameof(name)); + Argument.AssertNotNull(requiredUnion, nameof(requiredUnion)); + Argument.AssertNotNull(requiredBadDescription, nameof(requiredBadDescription)); + + AnonymousBodyRequest spreadModel = new AnonymousBodyRequest( + name, + requiredUnion, + requiredLiteralString, + requiredLiteralInt, + requiredLiteralFloat, + requiredLiteralBool, + optionalLiteralString, + optionalLiteralInt, + optionalLiteralFloat, + optionalLiteralBool, + requiredBadDescription, + optionalNullableList?.ToList() as IList ?? new ChangeTrackingList(), + requiredNullableList?.ToList() as IList ?? new ChangeTrackingList(), + null); + ClientResult result = AnonymousBody(spreadModel, null); return ClientResult.FromValue((Thing)result, result.GetRawResponse()); } /// body parameter without body decorator. - /// Service returned a non-success status code. - public virtual async Task> AnonymousBodyAsync() - { - ClientResult result = await AnonymousBodyAsync(null).ConfigureAwait(false); + /// name of the Thing. + /// required Union. + /// required literal string. + /// required literal int. + /// required literal float. + /// required literal bool. + /// description with xml <|endoftext|>. + /// required nullable collection. + /// optional literal string. + /// optional literal int. + /// optional literal float. + /// optional literal bool. + /// optional nullable collection. + /// , or is null. + /// Service returned a non-success status code. + public virtual async Task> AnonymousBodyAsync(string name, BinaryData requiredUnion, AnonymousBodyRequestRequiredLiteralString requiredLiteralString, AnonymousBodyRequestRequiredLiteralInt requiredLiteralInt, AnonymousBodyRequestRequiredLiteralFloat requiredLiteralFloat, bool requiredLiteralBool, string requiredBadDescription, IEnumerable requiredNullableList, AnonymousBodyRequestOptionalLiteralString? optionalLiteralString = default, AnonymousBodyRequestOptionalLiteralInt? optionalLiteralInt = default, AnonymousBodyRequestOptionalLiteralFloat? optionalLiteralFloat = default, bool? optionalLiteralBool = default, IEnumerable optionalNullableList = default) + { + Argument.AssertNotNull(name, nameof(name)); + Argument.AssertNotNull(requiredUnion, nameof(requiredUnion)); + Argument.AssertNotNull(requiredBadDescription, nameof(requiredBadDescription)); + + AnonymousBodyRequest spreadModel = new AnonymousBodyRequest( + name, + requiredUnion, + requiredLiteralString, + requiredLiteralInt, + requiredLiteralFloat, + requiredLiteralBool, + optionalLiteralString, + optionalLiteralInt, + optionalLiteralFloat, + optionalLiteralBool, + requiredBadDescription, + optionalNullableList?.ToList() as IList ?? new ChangeTrackingList(), + requiredNullableList?.ToList() as IList ?? new ChangeTrackingList(), + null); + ClientResult result = await AnonymousBodyAsync(spreadModel, null).ConfigureAwait(false); return ClientResult.FromValue((Thing)result, result.GetRawResponse()); } @@ -687,12 +763,16 @@ public virtual async Task> AnonymousBodyAsync() /// /// /// + /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult FriendlyModel(RequestOptions options) + public virtual ClientResult FriendlyModel(BinaryContent content, RequestOptions options) { - using PipelineMessage message = CreateFriendlyModelRequest(options); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateFriendlyModelRequest(content, options); return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options)); } @@ -704,28 +784,42 @@ public virtual ClientResult FriendlyModel(RequestOptions options) /// /// /// + /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task FriendlyModelAsync(RequestOptions options) + public virtual async Task FriendlyModelAsync(BinaryContent content, RequestOptions options) { - using PipelineMessage message = CreateFriendlyModelRequest(options); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateFriendlyModelRequest(content, options); return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } /// Model can have its friendly name. + /// name of the NotFriend. + /// is null. /// Service returned a non-success status code. - public virtual ClientResult FriendlyModel() + public virtual ClientResult FriendlyModel(string name) { - ClientResult result = FriendlyModel(null); + Argument.AssertNotNull(name, nameof(name)); + + FriendlyModelRequest spreadModel = new FriendlyModelRequest(name, null); + ClientResult result = FriendlyModel(spreadModel, null); return ClientResult.FromValue((Friend)result, result.GetRawResponse()); } /// Model can have its friendly name. + /// name of the NotFriend. + /// is null. /// Service returned a non-success status code. - public virtual async Task> FriendlyModelAsync() + public virtual async Task> FriendlyModelAsync(string name) { - ClientResult result = await FriendlyModelAsync(null).ConfigureAwait(false); + Argument.AssertNotNull(name, nameof(name)); + + FriendlyModelRequest spreadModel = new FriendlyModelRequest(name, null); + ClientResult result = await FriendlyModelAsync(spreadModel, null).ConfigureAwait(false); return ClientResult.FromValue((Friend)result, result.GetRawResponse()); } @@ -785,12 +879,16 @@ public virtual async Task AddTimeHeaderAsync() /// /// /// + /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual ClientResult ProjectedNameModel(RequestOptions options) + public virtual ClientResult ProjectedNameModel(BinaryContent content, RequestOptions options) { - using PipelineMessage message = CreateProjectedNameModelRequest(options); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateProjectedNameModelRequest(content, options); return ClientResult.FromResponse(Pipeline.ProcessMessage(message, options)); } @@ -802,28 +900,42 @@ public virtual ClientResult ProjectedNameModel(RequestOptions options) /// /// /// + /// The content to send as the body of the request. /// The request options, which can override default behaviors of the client pipeline on a per-call basis. + /// is null. /// Service returned a non-success status code. /// The response returned from the service. - public virtual async Task ProjectedNameModelAsync(RequestOptions options) + public virtual async Task ProjectedNameModelAsync(BinaryContent content, RequestOptions options) { - using PipelineMessage message = CreateProjectedNameModelRequest(options); + Argument.AssertNotNull(content, nameof(content)); + + using PipelineMessage message = CreateProjectedNameModelRequest(content, options); return ClientResult.FromResponse(await Pipeline.ProcessMessageAsync(message, options).ConfigureAwait(false)); } /// Model can have its projected name. + /// name of the ModelWithProjectedName. + /// is null. /// Service returned a non-success status code. - public virtual ClientResult ProjectedNameModel() + public virtual ClientResult ProjectedNameModel(string name) { - ClientResult result = ProjectedNameModel(null); + Argument.AssertNotNull(name, nameof(name)); + + ProjectedNameModelRequest spreadModel = new ProjectedNameModelRequest(name, null); + ClientResult result = ProjectedNameModel(spreadModel, null); return ClientResult.FromValue((ProjectedModel)result, result.GetRawResponse()); } /// Model can have its projected name. + /// name of the ModelWithProjectedName. + /// is null. /// Service returned a non-success status code. - public virtual async Task> ProjectedNameModelAsync() + public virtual async Task> ProjectedNameModelAsync(string name) { - ClientResult result = await ProjectedNameModelAsync(null).ConfigureAwait(false); + Argument.AssertNotNull(name, nameof(name)); + + ProjectedNameModelRequest spreadModel = new ProjectedNameModelRequest(name, null); + ClientResult result = await ProjectedNameModelAsync(spreadModel, null).ConfigureAwait(false); return ClientResult.FromValue((ProjectedModel)result, result.GetRawResponse()); }