From b956e825161f154c60cb4a401f5f4a8fb46cdc98 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Fri, 11 Oct 2024 15:24:00 +0800 Subject: [PATCH 01/11] support apiVersion as path parameter --- .../eng/scripts/Generate.ps1 | 1 - .../src/StubLibraryVisitor.cs | 9 + .../src/Providers/ClientOptionsProvider.cs | 6 +- .../src/Providers/ClientProvider.cs | 7 +- .../src/Providers/RestClientProvider.cs | 3 +- .../src/Properties/launchSettings.json | 5 + .../Server/Path/Multiple/MultipleTests.cs | 26 +++ .../server/path/multiple/Configuration.json | 6 + .../path/multiple/Server.Path.Multiple.sln | 48 +++++ .../multiple/src/Generated/MultipleClient.cs | 38 ++++ .../src/Generated/MultipleClientOptions.cs | 21 ++ .../multiple/src/Server.Path.Multiple.csproj | 16 ++ .../server/path/multiple/tspCodeModel.json | 200 ++++++++++++++++++ 13 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Server/Path/Multiple/MultipleTests.cs create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Configuration.json create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Server.Path.Multiple.sln create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClient.cs create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Server.Path.Multiple.csproj create mode 100644 packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/tspCodeModel.json diff --git a/packages/http-client-csharp/eng/scripts/Generate.ps1 b/packages/http-client-csharp/eng/scripts/Generate.ps1 index de0bd2347f..40d1ff3cc9 100644 --- a/packages/http-client-csharp/eng/scripts/Generate.ps1 +++ b/packages/http-client-csharp/eng/scripts/Generate.ps1 @@ -64,7 +64,6 @@ $failingSpecs = @( Join-Path 'http' 'routes' Join-Path 'http' 'serialization' 'encoded-name' 'json' Join-Path 'http' 'server' 'endpoint' 'not-defined' - Join-Path 'http' 'server' 'path' 'multiple' Join-Path 'http' 'server' 'versions' 'versioned' Join-Path 'http' 'special-headers' 'conditional-request' Join-Path 'http' 'special-headers' 'repeatability' diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs index 337bb3d8fb..a4ae9aa1a1 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using Microsoft.Generator.CSharp.ClientModel.Providers; using Microsoft.Generator.CSharp.Expressions; using Microsoft.Generator.CSharp.Primitives; @@ -37,6 +38,14 @@ internal class StubLibraryVisitor : ScmLibraryVisitor return null; } + if (type is ClientOptionsProvider clientOptions) + { + if (clientOptions.LatestVersionField is not null) + { + var fields = new List { clientOptions.LatestVersionField }; + type.Update(fields: fields); + } + } return type; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs index fff48b17ef..cdcb32dedd 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.Generator.CSharp.ClientModel.Providers { - internal class ClientOptionsProvider : TypeProvider + public class ClientOptionsProvider : TypeProvider { private const string LatestVersionFieldName = "LatestVersion"; private const string VersionPropertyName = "Version"; @@ -45,7 +45,7 @@ public ClientOptionsProvider(InputClient inputClient, ClientProvider clientProvi internal PropertyProvider? VersionProperty => _versionProperty; private TypeProvider? ServiceVersionEnum => _serviceVersionEnum?.Value; - private FieldProvider? LatestVersionField => _latestVersionField ??= BuildLatestVersionField(); + public FieldProvider? LatestVersionField => _latestVersionField ??= BuildLatestVersionField(); private FieldProvider? BuildLatestVersionField() { @@ -122,7 +122,7 @@ protected override PropertyProvider[] BuildProperties() foreach (var p in _inputClient.Parameters) { - if (!p.IsEndpoint && p.DefaultValue != null) + if (!p.IsEndpoint && !p.IsApiVersion && p.DefaultValue != null) { FormattableString? description = null; if (p.Description != null) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs index 67e1153b12..86a0623411 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs @@ -168,7 +168,7 @@ protected override FieldProvider[] BuildFields() { if (!p.IsEndpoint) { - var type = ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(p.Type); + var type = p is { IsApiVersion: true, Type: InputEnumType enumType } ? ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(enumType.ValueType) : ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(p.Type); if (type != null) { FieldProvider field = new( @@ -251,11 +251,12 @@ private IReadOnlyList GetRequiredParameters() ParameterProvider? currentParam = null; foreach (var parameter in _allClientParameters) { - if (parameter.IsRequired && !parameter.IsEndpoint && !parameter.IsApiVersion) + currentParam = null; + if (parameter.IsRequired && !parameter.IsEndpoint) { currentParam = ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter); currentParam.Field = Fields.FirstOrDefault(f => f.Name == "_" + parameter.Name); - requiredParameters.Add(currentParam); + if (!parameter.IsApiVersion) requiredParameters.Add(currentParam); } if (parameter.Location == RequestLocation.Uri) { 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 d12660cf08..08ad948d33 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 @@ -341,7 +341,8 @@ private static void GetParamInfo(Dictionary paramMap, else { var paramProvider = paramMap[inputParam.Name]; - if (paramProvider.Type.IsEnum) + type = paramProvider.Field is null ? paramProvider.Type : paramProvider.Field.Type; + if (type.IsEnum) { var csharpType = paramProvider.Field is null ? paramProvider.Type : paramProvider.Field.Type; valueExpression = csharpType.ToSerial(paramProvider); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json index cc6da7a4c7..917b535ba3 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json @@ -75,6 +75,11 @@ "commandName": "Executable", "executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe" }, + "http-server-path-multiple": { + "commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/server/path/multiple -p StubLibraryPlugin", + "commandName": "Executable", + "executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe" + }, "http-server-path-single": { "commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/server/path/single -p StubLibraryPlugin", "commandName": "Executable", diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Server/Path/Multiple/MultipleTests.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Server/Path/Multiple/MultipleTests.cs new file mode 100644 index 0000000000..cb2acfe3fe --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Server/Path/Multiple/MultipleTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using NUnit.Framework; +using Server.Path.Multiple; + +namespace TestProjects.CadlRanch.Tests.Http.Server.Path.Multiple +{ + internal class MultipleTests : CadlRanchTestBase + { + [CadlRanchTest] + public Task NoOperationParams() => Test(async (host) => + { + var result = await new MultipleClient(host, null).NoOperationParamsAsync(); + Assert.AreEqual(204, result.GetRawResponse().Status); + }); + + [CadlRanchTest] + public Task WithOperationPathParam() => Test(async (host) => + { + var result = await new MultipleClient(host, null).WithOperationPathParamAsync("test"); + Assert.AreEqual(204, result.GetRawResponse().Status); + }); + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Configuration.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Configuration.json new file mode 100644 index 0000000000..d5f19b7204 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Configuration.json @@ -0,0 +1,6 @@ +{ + "output-folder": ".", + "namespace": "Server.Path.Multiple", + "library-name": "Server.Path.Multiple", + "use-model-reader-writer": true +} diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Server.Path.Multiple.sln b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Server.Path.Multiple.sln new file mode 100644 index 0000000000..3b848b66f3 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/Server.Path.Multiple.sln @@ -0,0 +1,48 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server.Path.Multiple", "src\Server.Path.Multiple.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0C276D1-2930-4887-B29A-D1A33E7009A2}.Release|Any CPU.Build.0 = Release|Any CPU + {8E9A77AC-792A-4432-8320-ACFD46730401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E9A77AC-792A-4432-8320-ACFD46730401}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E9A77AC-792A-4432-8320-ACFD46730401}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E9A77AC-792A-4432-8320-ACFD46730401}.Release|Any CPU.Build.0 = Release|Any CPU + {A4241C1F-A53D-474C-9E4E-075054407E74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4241C1F-A53D-474C-9E4E-075054407E74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4241C1F-A53D-474C-9E4E-075054407E74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4241C1F-A53D-474C-9E4E-075054407E74}.Release|Any CPU.Build.0 = Release|Any CPU + {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA8BD3F1-8616-47B6-974C-7576CDF4717E}.Release|Any CPU.Build.0 = Release|Any CPU + {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85677AD3-C214-42FA-AE6E-49B956CAC8DC}.Release|Any CPU.Build.0 = Release|Any CPU + {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.Build.0 = Release|Any CPU + {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A97F4B90-2591-4689-B1F8-5F21FE6D6CAE} + EndGlobalSection +EndGlobal diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClient.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClient.cs new file mode 100644 index 0000000000..07c484befd --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClient.cs @@ -0,0 +1,38 @@ +// + +#nullable disable + +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Threading.Tasks; + +namespace Server.Path.Multiple +{ + public partial class MultipleClient + { + protected MultipleClient() => throw null; + + public MultipleClient(Uri endpoint) : this(endpoint, new MultipleClientOptions()) => throw null; + + public MultipleClient(Uri endpoint, MultipleClientOptions options) => throw null; + + public ClientPipeline Pipeline => throw null; + + public virtual ClientResult NoOperationParams(RequestOptions options) => throw null; + + public virtual Task NoOperationParamsAsync(RequestOptions options) => throw null; + + public virtual ClientResult NoOperationParams() => throw null; + + public virtual Task NoOperationParamsAsync() => throw null; + + public virtual ClientResult WithOperationPathParam(string keyword, RequestOptions options) => throw null; + + public virtual Task WithOperationPathParamAsync(string keyword, RequestOptions options) => throw null; + + public virtual ClientResult WithOperationPathParam(string keyword) => throw null; + + public virtual Task WithOperationPathParamAsync(string keyword) => throw null; + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs new file mode 100644 index 0000000000..3c63332cfb --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs @@ -0,0 +1,21 @@ +// + +#nullable disable + +using System.ClientModel.Primitives; + +namespace Server.Path.Multiple +{ + public partial class MultipleClientOptions : ClientPipelineOptions + { + private const ServiceVersion LatestVersion = ServiceVersion.V1_0; + + public MultipleClientOptions(ServiceVersion version = LatestVersion) => throw null; + + public enum ServiceVersion + { + /// Version 1.0. + V1_0 = 1 + } + } +} diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Server.Path.Multiple.csproj b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Server.Path.Multiple.csproj new file mode 100644 index 0000000000..6f4cb2cf50 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Server.Path.Multiple.csproj @@ -0,0 +1,16 @@ + + + This is the Server.Path.Multiple client library for developing .NET applications with rich experience. + SDK Code Generation Server.Path.Multiple + 1.0.0-beta.1 + Server.Path.Multiple + netstandard2.0 + latest + true + + + + + + + diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/tspCodeModel.json new file mode 100644 index 0000000000..53ff2d2789 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/tspCodeModel.json @@ -0,0 +1,200 @@ +{ + "$id": "1", + "Name": "Server.Path.Multiple", + "ApiVersions": [ + "v1.0" + ], + "Enums": [ + { + "$id": "2", + "kind": "enum", + "name": "Versions", + "crossLanguageDefinitionId": "Server.Path.Multiple.Versions", + "valueType": { + "$id": "3", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "values": [ + { + "$id": "4", + "kind": "enumvalue", + "name": "v1_0", + "value": "v1.0", + "valueType": { + "$id": "5", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "enumType": { + "$ref": "2" + }, + "description": "Version 1.0", + "decorators": [] + } + ], + "description": "Service versions", + "isFixed": true, + "isFlags": false, + "usage": "Input,ApiVersionEnum", + "decorators": [] + } + ], + "Models": [], + "Clients": [ + { + "$id": "6", + "Name": "MultipleClient", + "Operations": [ + { + "$id": "7", + "Name": "noOperationParams", + "ResourceName": "Multiple", + "Accessibility": "public", + "Parameters": [ + { + "$id": "8", + "Name": "endpoint", + "NameInRequest": "endpoint", + "Description": "Pass in http://localhost:3000 for endpoint.", + "Type": { + "$id": "9", + "kind": "url", + "name": "url", + "crossLanguageDefinitionId": "TypeSpec.url" + }, + "Location": "Uri", + "IsApiVersion": false, + "IsResourceParameter": false, + "IsContentType": false, + "IsRequired": true, + "IsEndpoint": true, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Client" + }, + { + "$id": "10", + "Name": "apiVersion", + "NameInRequest": "apiVersion", + "Description": "Pass in v1.0 for API version.", + "Type": { + "$ref": "2" + }, + "Location": "Uri", + "IsApiVersion": true, + "IsResourceParameter": false, + "IsContentType": false, + "IsRequired": true, + "IsEndpoint": false, + "SkipUrlEncoding": false, + "Explode": false, + "Kind": "Client", + "DefaultValue": { + "$id": "11", + "Type": { + "$id": "12", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string" + }, + "Value": "v1.0" + } + } + ], + "Responses": [ + { + "$id": "13", + "StatusCodes": [ + 204 + ], + "BodyMediaType": "Json", + "Headers": [], + "IsErrorResponse": false + } + ], + "HttpMethod": "GET", + "RequestBodyMediaType": "None", + "Uri": "{endpoint}/server/path/multiple/{apiVersion}", + "Path": "/", + "BufferResponse": true, + "GenerateProtocolMethod": true, + "GenerateConvenienceMethod": true, + "CrossLanguageDefinitionId": "Server.Path.Multiple.noOperationParams", + "Decorators": [] + }, + { + "$id": "14", + "Name": "withOperationPathParam", + "ResourceName": "Multiple", + "Accessibility": "public", + "Parameters": [ + { + "$ref": "8" + }, + { + "$ref": "10" + }, + { + "$id": "15", + "Name": "keyword", + "NameInRequest": "keyword", + "Type": { + "$id": "16", + "kind": "string", + "name": "string", + "crossLanguageDefinitionId": "TypeSpec.string", + "decorators": [] + }, + "Location": "Path", + "IsApiVersion": false, + "IsContentType": false, + "IsEndpoint": false, + "Explode": false, + "IsRequired": true, + "Kind": "Method", + "Decorators": [], + "SkipUrlEncoding": false + } + ], + "Responses": [ + { + "$id": "17", + "StatusCodes": [ + 204 + ], + "BodyMediaType": "Json", + "Headers": [], + "IsErrorResponse": false + } + ], + "HttpMethod": "GET", + "RequestBodyMediaType": "None", + "Uri": "{endpoint}/server/path/multiple/{apiVersion}", + "Path": "/{keyword}", + "BufferResponse": true, + "GenerateProtocolMethod": true, + "GenerateConvenienceMethod": true, + "CrossLanguageDefinitionId": "Server.Path.Multiple.withOperationPathParam", + "Decorators": [] + } + ], + "Protocol": { + "$id": "18" + }, + "Parameters": [ + { + "$ref": "8" + }, + { + "$ref": "10" + } + ], + "Decorators": [] + } + ] +} From 9e853be6ac19c1d073b734667c97ea82caed1e8a Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Sat, 12 Oct 2024 10:11:37 +0800 Subject: [PATCH 02/11] add UT --- .../ClientProviders/ClientProviderTests.cs | 72 +++++++++- .../RestClientProviderTests.cs | 124 ++++++++++++++++++ .../test/common/InputFactory.cs | 13 +- 3 files changed, 203 insertions(+), 6 deletions(-) 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 eb66522855..2a85e48fc1 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 @@ -406,7 +406,7 @@ public void TestApiVersionOfClient() var client = InputFactory.Client(TestClientName, operations: [ InputFactory.Operation("OperationWithApiVersion", - parameters: [InputFactory.Parameter("apiVersion", InputPrimitiveType.String, isRequired: true, location: RequestLocation.Query, kind: InputOperationParameterKind.Client)]) + parameters: [InputFactory.Parameter("apiVersion", InputPrimitiveType.String, isRequired: true, location: RequestLocation.Query, kind: InputOperationParameterKind.Client, isApiVersion: true)]) ]); var clientProvider = new ClientProvider(client); Assert.IsNotNull(clientProvider); @@ -414,12 +414,34 @@ public void TestApiVersionOfClient() /* verify that the client has apiVersion field */ Assert.IsNotNull(clientProvider.Fields.FirstOrDefault(f => f.Name.Equals("_apiVersion"))); + /* verify that there is no apiVersion parameter in constructor. */ + var apiVersionParameter = clientProvider.Constructors.Select(c => c.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))).FirstOrDefault(); + Assert.IsNull(apiVersionParameter); + + var primaryConstructor = clientProvider.Constructors.FirstOrDefault( + c => c.Signature?.Initializer == null && c.Signature?.Modifiers == MethodSignatureModifiers.Public); var method = clientProvider.Methods.FirstOrDefault(m => m.Signature.Name.Equals("OperationWithApiVersion")); Assert.IsNotNull(method); /* verify that the method does not have apiVersion parameter */ Assert.IsNull(method?.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))); } + [TestCaseSource(nameof(ValidateApiVersionPathParameterTestCases))] + public void TestApiVersionPathParameterOfClient(InputClient inputClient) + { + var clientProvider = new ClientProvider(inputClient); + Assert.IsNotNull(clientProvider); + + /* verify that the client has apiVersion field */ + var apiVersionField = clientProvider.Fields.FirstOrDefault(f => f.Name.Equals("_apiVersion")); + Assert.IsNotNull(apiVersionField); + Assert.AreEqual(new CSharpType(typeof(string)), apiVersionField?.Type); + + var method = clientProvider.Methods.FirstOrDefault(m => m.Signature.Name.Equals("TestOperation")); + Assert.IsNotNull(method); + /* verify that the method does not have apiVersion parameter */ + Assert.IsNull(method?.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))); + } private static InputClient GetEnumQueryParamClient() => InputFactory.Client( TestClientName, @@ -599,5 +621,53 @@ private static IEnumerable EndpointParamInitializationValueTestCas defaultValue: InputFactory.Constant.String("mockValue")), New.Instance(KnownParameters.Endpoint.Type, Literal("mockvalue"))); } + + private static IEnumerable ValidateApiVersionPathParameterTestCases() + { + yield return new TestCaseData( + InputFactory.Client( + TestClientName, + operations: + [ + InputFactory.Operation( + "TestOperation") + ], + parameters: [ + InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true) + ])); + + yield return new TestCaseData( + InputFactory.Client( + TestClientName, + operations: + [ + InputFactory.Operation( + "TestOperation") + ], + parameters: [ + InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true) + ])); + } } } 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 5197c96e1f..9bd8f62e90 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 @@ -216,6 +216,21 @@ public void ValidateClientWithApiVersion() Assert.IsTrue(bodyStatements!.Statements.Any(s => s.ToDisplayString() == "uri.AppendQuery(\"apiVersion\", _apiVersion, true);\n")); } + [TestCaseSource(nameof(ValidateApiVersionPathParameterTestCases))] + public void ValidateClientWithApiVersionPathParameter(InputClient inputClient) + { + var clientProvider = new ClientProvider(inputClient); + var restClientProvider = new MockClientProvider(inputClient, clientProvider); + var method = restClientProvider.Methods.FirstOrDefault(m => m.Signature.Name == "CreateTestOperationRequest"); + Assert.IsNotNull(method); + /* verify that there is no apiVersion parameter in method signature. */ + Assert.IsNull(method?.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))); + var bodyStatements = method?.BodyStatements as MethodBodyStatements; + Assert.IsNotNull(bodyStatements); + /* verify that it will use client _apiVersion field to append query parameter. */ + Assert.IsTrue(bodyStatements!.Statements.Any(s => s.ToDisplayString() == "uri.AppendPath(_apiVersion, true);\n")); + } + private readonly static InputOperation BasicOperation = InputFactory.Operation( "CreateMessage", parameters: @@ -370,5 +385,114 @@ protected override MethodProvider[] BuildMethods() protected override TypeProvider[] BuildNestedTypes() => []; } + + private static IEnumerable ValidateApiVersionPathParameterTestCases() + { + yield return new TestCaseData( + InputFactory.Client( + "TestClient", + operations: + [ + InputFactory.Operation( + "TestOperation", + uri: "{endpoint}/{apiVersion}", + parameters: [ + InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false), + InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true) + ]) + ], + parameters: [ + InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false), + InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true) + ])); + + yield return new TestCaseData( + InputFactory.Client( + "TestClient", + operations: + [ + InputFactory.Operation( + "TestOperation", + parameters: [ + InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false), + InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true) + ], + uri: "{endpoint}/{apiVersion}") + ], + parameters: [ + InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false), + InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true) + ])); + } } } 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 ea1f6c67b4..1625a83357 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 @@ -74,7 +74,8 @@ public static InputParameter Parameter( InputOperationParameterKind kind = InputOperationParameterKind.Method, bool isEndpoint = false, bool isResourceParameter = false, - bool isContentType = false) + bool isContentType = false, + bool isApiVersion = false) { return new InputParameter( name, @@ -85,7 +86,7 @@ public static InputParameter Parameter( defaultValue, kind, isRequired, - false, + isApiVersion, isResourceParameter, isContentType, isEndpoint, @@ -199,7 +200,9 @@ public static InputOperation Operation( string access = "public", IEnumerable? parameters = null, IEnumerable? responses = null, - IEnumerable? requestMediaTypes = null) + IEnumerable? requestMediaTypes = null, + string uri = "", + string path = "") { return new InputOperation( name, @@ -211,8 +214,8 @@ public static InputOperation Operation( responses is null ? [OperationResponse()] : [.. responses], "GET", BodyMediaType.Json, - "", - "", + uri, + path, null, requestMediaTypes is null ? null : [.. requestMediaTypes], false, From a10c7fbc164f61471af8949b8dc2aa7a8295ba0b Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Sat, 12 Oct 2024 10:38:00 +0800 Subject: [PATCH 03/11] add more check --- .../ClientProviders/ClientProviderTests.cs | 90 ++++++++---- .../RestClientProviderTests.cs | 131 +++++++----------- 2 files changed, 109 insertions(+), 112 deletions(-) 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 2a85e48fc1..2366eedda4 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 @@ -418,8 +418,14 @@ public void TestApiVersionOfClient() var apiVersionParameter = clientProvider.Constructors.Select(c => c.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))).FirstOrDefault(); Assert.IsNull(apiVersionParameter); + /* verify the apiVersion assignment in constructor body */ var primaryConstructor = clientProvider.Constructors.FirstOrDefault( c => c.Signature?.Initializer == null && c.Signature?.Modifiers == MethodSignatureModifiers.Public); + Assert.IsNotNull(primaryConstructor); + var bodyStatements = primaryConstructor?.BodyStatements as MethodBodyStatements; + Assert.IsNotNull(bodyStatements); + Assert.IsTrue(bodyStatements!.Statements.Any(s => s.ToDisplayString() == "_apiVersion = options.Version;\n")); + var method = clientProvider.Methods.FirstOrDefault(m => m.Signature.Name.Equals("OperationWithApiVersion")); Assert.IsNotNull(method); /* verify that the method does not have apiVersion parameter */ @@ -624,50 +630,76 @@ private static IEnumerable EndpointParamInitializationValueTestCas private static IEnumerable ValidateApiVersionPathParameterTestCases() { + InputParameter endpointParameter = InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false); + + InputParameter stringApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + + InputParameter enumApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + yield return new TestCaseData( InputFactory.Client( - TestClientName, + "TestClient", operations: [ InputFactory.Operation( - "TestOperation") + "TestOperation", + uri: "{endpoint}/{apiVersion}", + parameters: + [ + endpointParameter, + stringApiVersionParameter + ]) ], parameters: [ - InputFactory.Parameter( - "apiVersion", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + stringApiVersionParameter + ])); yield return new TestCaseData( InputFactory.Client( - TestClientName, + "TestClient", operations: [ InputFactory.Operation( - "TestOperation") + "TestOperation", + parameters: [ + endpointParameter, + enumApiVersionParameter + ], + uri: "{endpoint}/{apiVersion}") ], parameters: [ - InputFactory.Parameter( - "apiVersion", - InputFactory.Enum( - "InputEnum", - InputPrimitiveType.String, - usage: InputModelTypeUsage.Input, - isExtensible: true, - values: - [ - InputFactory.EnumMember.String("value1", "value1"), - InputFactory.EnumMember.String("value2", "value2") - ]), - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + enumApiVersionParameter + ])); } } } 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 9bd8f62e90..802ee58fe1 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 @@ -388,49 +388,58 @@ protected override MethodProvider[] BuildMethods() private static IEnumerable ValidateApiVersionPathParameterTestCases() { + InputParameter endpointParameter = InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false); + + InputParameter stringApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + + InputParameter enumApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + yield return new TestCaseData( InputFactory.Client( "TestClient", operations: [ InputFactory.Operation( - "TestOperation", - uri: "{endpoint}/{apiVersion}", - parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) + "TestOperation", + uri: "{endpoint}/{apiVersion}", + parameters: + [ + endpointParameter, + stringApiVersionParameter ]) ], parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + stringApiVersionParameter + ])); yield return new TestCaseData( InputFactory.Client( @@ -440,59 +449,15 @@ private static IEnumerable ValidateApiVersionPathParameterTestCase InputFactory.Operation( "TestOperation", parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputFactory.Enum( - "InputEnum", - InputPrimitiveType.String, - usage: InputModelTypeUsage.Input, - isExtensible: true, - values: - [ - InputFactory.EnumMember.String("value1", "value1"), - InputFactory.EnumMember.String("value2", "value2") - ]), - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) + endpointParameter, + enumApiVersionParameter ], uri: "{endpoint}/{apiVersion}") ], parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputFactory.Enum( - "InputEnum", - InputPrimitiveType.String, - usage: InputModelTypeUsage.Input, - isExtensible: true, - values: - [ - InputFactory.EnumMember.String("value1", "value1"), - InputFactory.EnumMember.String("value2", "value2") - ]), - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + enumApiVersionParameter + ])); } } } From 85cf97789aad37e8123bb1200a1ae1e74668bca3 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Sat, 12 Oct 2024 10:38:00 +0800 Subject: [PATCH 04/11] add more check --- .../ClientProviders/ClientProviderTests.cs | 97 +++++++++---- .../RestClientProviderTests.cs | 131 +++++++----------- 2 files changed, 116 insertions(+), 112 deletions(-) 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 2a85e48fc1..660e301654 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 @@ -403,6 +403,13 @@ public void ValidateClientWithSpread(InputClient inputClient) [Test] public void TestApiVersionOfClient() { + List apiVersions = ["1.0", "2.0"]; + var enumValues = apiVersions.Select(a => InputFactory.EnumMember.String(a, a)); + var inputEnum = InputFactory.Enum("ServiceVersion", InputPrimitiveType.Int64, values: [.. enumValues], usage: InputModelTypeUsage.ApiVersionEnum); + + MockHelpers.LoadMockPlugin( + apiVersions: () => apiVersions, + inputEnums: () => [inputEnum]); var client = InputFactory.Client(TestClientName, operations: [ InputFactory.Operation("OperationWithApiVersion", @@ -418,8 +425,14 @@ public void TestApiVersionOfClient() var apiVersionParameter = clientProvider.Constructors.Select(c => c.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))).FirstOrDefault(); Assert.IsNull(apiVersionParameter); + /* verify the apiVersion assignment in constructor body */ var primaryConstructor = clientProvider.Constructors.FirstOrDefault( c => c.Signature?.Initializer == null && c.Signature?.Modifiers == MethodSignatureModifiers.Public); + Assert.IsNotNull(primaryConstructor); + var bodyStatements = primaryConstructor?.BodyStatements as MethodBodyStatements; + Assert.IsNotNull(bodyStatements); + Assert.IsTrue(bodyStatements!.Statements.Any(s => s.ToDisplayString().IndexOf("_apiVersion = options.Version;\n") != -1)); + var method = clientProvider.Methods.FirstOrDefault(m => m.Signature.Name.Equals("OperationWithApiVersion")); Assert.IsNotNull(method); /* verify that the method does not have apiVersion parameter */ @@ -624,50 +637,76 @@ private static IEnumerable EndpointParamInitializationValueTestCas private static IEnumerable ValidateApiVersionPathParameterTestCases() { + InputParameter endpointParameter = InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false); + + InputParameter stringApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + + InputParameter enumApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + yield return new TestCaseData( InputFactory.Client( - TestClientName, + "TestClient", operations: [ InputFactory.Operation( - "TestOperation") + "TestOperation", + uri: "{endpoint}/{apiVersion}", + parameters: + [ + endpointParameter, + stringApiVersionParameter + ]) ], parameters: [ - InputFactory.Parameter( - "apiVersion", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + stringApiVersionParameter + ])); yield return new TestCaseData( InputFactory.Client( - TestClientName, + "TestClient", operations: [ InputFactory.Operation( - "TestOperation") + "TestOperation", + parameters: [ + endpointParameter, + enumApiVersionParameter + ], + uri: "{endpoint}/{apiVersion}") ], parameters: [ - InputFactory.Parameter( - "apiVersion", - InputFactory.Enum( - "InputEnum", - InputPrimitiveType.String, - usage: InputModelTypeUsage.Input, - isExtensible: true, - values: - [ - InputFactory.EnumMember.String("value1", "value1"), - InputFactory.EnumMember.String("value2", "value2") - ]), - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + enumApiVersionParameter + ])); } } } 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 9bd8f62e90..802ee58fe1 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 @@ -388,49 +388,58 @@ protected override MethodProvider[] BuildMethods() private static IEnumerable ValidateApiVersionPathParameterTestCases() { + InputParameter endpointParameter = InputFactory.Parameter( + "endpoint", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isEndpoint: true, + isApiVersion: false); + + InputParameter stringApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputPrimitiveType.String, + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + + InputParameter enumApiVersionParameter = InputFactory.Parameter( + "apiVersion", + InputFactory.Enum( + "InputEnum", + InputPrimitiveType.String, + usage: InputModelTypeUsage.Input, + isExtensible: true, + values: + [ + InputFactory.EnumMember.String("value1", "value1"), + InputFactory.EnumMember.String("value2", "value2") + ]), + location: RequestLocation.Uri, + isRequired: true, + kind: InputOperationParameterKind.Client, + isApiVersion: true); + yield return new TestCaseData( InputFactory.Client( "TestClient", operations: [ InputFactory.Operation( - "TestOperation", - uri: "{endpoint}/{apiVersion}", - parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) + "TestOperation", + uri: "{endpoint}/{apiVersion}", + parameters: + [ + endpointParameter, + stringApiVersionParameter ]) ], parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + stringApiVersionParameter + ])); yield return new TestCaseData( InputFactory.Client( @@ -440,59 +449,15 @@ private static IEnumerable ValidateApiVersionPathParameterTestCase InputFactory.Operation( "TestOperation", parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputFactory.Enum( - "InputEnum", - InputPrimitiveType.String, - usage: InputModelTypeUsage.Input, - isExtensible: true, - values: - [ - InputFactory.EnumMember.String("value1", "value1"), - InputFactory.EnumMember.String("value2", "value2") - ]), - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) + endpointParameter, + enumApiVersionParameter ], uri: "{endpoint}/{apiVersion}") ], parameters: [ - InputFactory.Parameter( - "endpoint", - InputPrimitiveType.String, - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isEndpoint: true, - isApiVersion: false), - InputFactory.Parameter( - "apiVersion", - InputFactory.Enum( - "InputEnum", - InputPrimitiveType.String, - usage: InputModelTypeUsage.Input, - isExtensible: true, - values: - [ - InputFactory.EnumMember.String("value1", "value1"), - InputFactory.EnumMember.String("value2", "value2") - ]), - location: RequestLocation.Uri, - isRequired: true, - kind: InputOperationParameterKind.Client, - isApiVersion: true) - ])); + endpointParameter, + enumApiVersionParameter + ])); } } } From ede51a54040f7bbc060e307a711b121bcca7dca3 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Sat, 12 Oct 2024 14:36:15 +0800 Subject: [PATCH 05/11] add empty line --- .../test/Providers/ClientProviders/ClientProviderTests.cs | 1 + 1 file changed, 1 insertion(+) 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 dd6a7bc703..ec587b6cb9 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 @@ -474,6 +474,7 @@ public void TestApiVersionPathParameterOfClient(InputClient inputClient) /* verify that the method does not have apiVersion parameter */ Assert.IsNull(method?.Signature.Parameters.FirstOrDefault(p => p.Name.Equals("apiVersion"))); } + private static InputClient GetEnumQueryParamClient() => InputFactory.Client( TestClientName, From eb2ac87ab0a8facf7748ec7f0a250a37dea443ed Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Tue, 15 Oct 2024 10:15:43 +0800 Subject: [PATCH 06/11] format --- .../src/Providers/ClientProvider.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs index 86a0623411..dd216acea8 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs @@ -168,7 +168,10 @@ protected override FieldProvider[] BuildFields() { if (!p.IsEndpoint) { - var type = p is { IsApiVersion: true, Type: InputEnumType enumType } ? ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(enumType.ValueType) : ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(p.Type); + var type = p is { IsApiVersion: true, Type: InputEnumType enumType } + ? ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(enumType.ValueType) + : ClientModelPlugin.Instance.TypeFactory.CreateCSharpType(p.Type); + if (type != null) { FieldProvider field = new( @@ -256,7 +259,10 @@ private IReadOnlyList GetRequiredParameters() { currentParam = ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter); currentParam.Field = Fields.FirstOrDefault(f => f.Name == "_" + parameter.Name); - if (!parameter.IsApiVersion) requiredParameters.Add(currentParam); + if (!parameter.IsApiVersion) + { + requiredParameters.Add(currentParam); + } } if (parameter.Location == RequestLocation.Uri) { From 7b20e6b07edf33e6373412a1ce1f14b68b4c9ea3 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Tue, 15 Oct 2024 15:23:41 +0800 Subject: [PATCH 07/11] remove constructor for clientoptions --- .../src/StubLibraryVisitor.cs | 7 ++----- .../src/Providers/ClientOptionsProvider.cs | 2 +- .../path/multiple/src/Generated/MultipleClientOptions.cs | 9 --------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs index a4ae9aa1a1..8b4acc91c2 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Generator.CSharp.ClientModel.Providers; using Microsoft.Generator.CSharp.Expressions; using Microsoft.Generator.CSharp.Primitives; @@ -40,11 +41,7 @@ internal class StubLibraryVisitor : ScmLibraryVisitor if (type is ClientOptionsProvider clientOptions) { - if (clientOptions.LatestVersionField is not null) - { - var fields = new List { clientOptions.LatestVersionField }; - type.Update(fields: fields); - } + type.Update(constructors: []); } return type; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs index cdcb32dedd..c5928dca91 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -45,7 +45,7 @@ public ClientOptionsProvider(InputClient inputClient, ClientProvider clientProvi internal PropertyProvider? VersionProperty => _versionProperty; private TypeProvider? ServiceVersionEnum => _serviceVersionEnum?.Value; - public FieldProvider? LatestVersionField => _latestVersionField ??= BuildLatestVersionField(); + private FieldProvider? LatestVersionField => _latestVersionField ??= BuildLatestVersionField(); private FieldProvider? BuildLatestVersionField() { diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs index 3c63332cfb..8d4b7c0f62 100644 --- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs +++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/server/path/multiple/src/Generated/MultipleClientOptions.cs @@ -8,14 +8,5 @@ namespace Server.Path.Multiple { public partial class MultipleClientOptions : ClientPipelineOptions { - private const ServiceVersion LatestVersion = ServiceVersion.V1_0; - - public MultipleClientOptions(ServiceVersion version = LatestVersion) => throw null; - - public enum ServiceVersion - { - /// Version 1.0. - V1_0 = 1 - } } } From 2f4a940cd7843ed0c33afa19d006e489b7cf283a Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Wed, 16 Oct 2024 09:27:45 +0800 Subject: [PATCH 08/11] check the implements to identify if it is ClientOptions --- .../src/StubLibraryVisitor.cs | 4 +++- .../src/Providers/ClientOptionsProvider.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs index 8b4acc91c2..f93fe22002 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.ClientModel.Primitives; using System.Collections.Generic; using System.Linq; using Microsoft.Generator.CSharp.ClientModel.Providers; @@ -39,7 +40,8 @@ internal class StubLibraryVisitor : ScmLibraryVisitor return null; } - if (type is ClientOptionsProvider clientOptions) + /* remove constructors for ClientOptions */ + if (type.Implements.Any(i => i.Equals(typeof(ClientPipelineOptions)))) { type.Update(constructors: []); } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs index c5928dca91..7d20070968 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientOptionsProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.Generator.CSharp.ClientModel.Providers { - public class ClientOptionsProvider : TypeProvider + internal class ClientOptionsProvider : TypeProvider { private const string LatestVersionFieldName = "LatestVersion"; private const string VersionPropertyName = "Version"; From a0f3f8187c16c7ea37b8413e870bd06f4b558c38 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Wed, 16 Oct 2024 10:23:53 +0800 Subject: [PATCH 09/11] remove unused import --- .../src/StubLibraryVisitor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs index f93fe22002..75fa2e3d76 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel.StubLibrary/src/StubLibraryVisitor.cs @@ -3,7 +3,6 @@ using System; using System.ClientModel.Primitives; -using System.Collections.Generic; using System.Linq; using Microsoft.Generator.CSharp.ClientModel.Providers; using Microsoft.Generator.CSharp.Expressions; From 0cfff3acf529f8235ee46f8a2aee6d21fa23ba89 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Wed, 16 Oct 2024 14:42:10 +0800 Subject: [PATCH 10/11] resolve comments --- .../src/Providers/ClientProvider.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs index dd216acea8..69e6067fcb 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs @@ -255,18 +255,14 @@ private IReadOnlyList GetRequiredParameters() foreach (var parameter in _allClientParameters) { currentParam = null; - if (parameter.IsRequired && !parameter.IsEndpoint) + if (parameter.IsRequired && !parameter.IsEndpoint && !parameter.IsApiVersion) { - currentParam = ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter); - currentParam.Field = Fields.FirstOrDefault(f => f.Name == "_" + parameter.Name); - if (!parameter.IsApiVersion) - { - requiredParameters.Add(currentParam); - } + currentParam = CreateParameter(parameter); + requiredParameters.Add(currentParam); } if (parameter.Location == RequestLocation.Uri) { - _uriParameters.Add(currentParam ?? ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter)); + _uriParameters.Add(currentParam ?? CreateParameter(parameter)); } } @@ -276,6 +272,13 @@ private IReadOnlyList GetRequiredParameters() return requiredParameters; } + private ParameterProvider CreateParameter(InputParameter parameter) + { + var param = ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter); + param.Field = Fields.FirstOrDefault(f => f.Name == "_" + parameter.Name); + return param; + } + private MethodBodyStatement[] BuildPrimaryConstructorBody(IReadOnlyList primaryConstructorParameters) { if (ClientOptions is null || ClientOptionsParameter is null) From 7feaa0d3921ab47dffb6000c736737644f048089 Mon Sep 17 00:00:00 2001 From: "FAREAST\\chunyu" Date: Wed, 16 Oct 2024 14:42:10 +0800 Subject: [PATCH 11/11] resolve comments --- .../src/Providers/ClientProvider.cs | 19 +++++++++++-------- .../src/Providers/RestClientProvider.cs | 3 +-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs index dd216acea8..69e6067fcb 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.ClientModel/src/Providers/ClientProvider.cs @@ -255,18 +255,14 @@ private IReadOnlyList GetRequiredParameters() foreach (var parameter in _allClientParameters) { currentParam = null; - if (parameter.IsRequired && !parameter.IsEndpoint) + if (parameter.IsRequired && !parameter.IsEndpoint && !parameter.IsApiVersion) { - currentParam = ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter); - currentParam.Field = Fields.FirstOrDefault(f => f.Name == "_" + parameter.Name); - if (!parameter.IsApiVersion) - { - requiredParameters.Add(currentParam); - } + currentParam = CreateParameter(parameter); + requiredParameters.Add(currentParam); } if (parameter.Location == RequestLocation.Uri) { - _uriParameters.Add(currentParam ?? ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter)); + _uriParameters.Add(currentParam ?? CreateParameter(parameter)); } } @@ -276,6 +272,13 @@ private IReadOnlyList GetRequiredParameters() return requiredParameters; } + private ParameterProvider CreateParameter(InputParameter parameter) + { + var param = ClientModelPlugin.Instance.TypeFactory.CreateParameter(parameter); + param.Field = Fields.FirstOrDefault(f => f.Name == "_" + parameter.Name); + return param; + } + private MethodBodyStatement[] BuildPrimaryConstructorBody(IReadOnlyList primaryConstructorParameters) { if (ClientOptions is null || ClientOptionsParameter is null) 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 08ad948d33..cf342b0f74 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 @@ -344,8 +344,7 @@ private static void GetParamInfo(Dictionary paramMap, type = paramProvider.Field is null ? paramProvider.Type : paramProvider.Field.Type; if (type.IsEnum) { - var csharpType = paramProvider.Field is null ? paramProvider.Type : paramProvider.Field.Type; - valueExpression = csharpType.ToSerial(paramProvider); + valueExpression = type.ToSerial(paramProvider); format = null; } else