Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[http-client-csharp] adopt http\resiliency\srv-driven #5142

Merged
merged 7 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/http-client-csharp/eng/scripts/Generate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ function IsSpecDir {

$failingSpecs = @(
Join-Path 'http' 'payload' 'pageable'
Join-Path 'http' 'resiliency' 'srv-driven'
Join-Path 'http' 'special-headers' 'conditional-request'
Join-Path 'http' 'type' 'model' 'flatten'
Join-Path 'http' 'type' 'model' 'templated'
Expand Down Expand Up @@ -94,7 +93,7 @@ foreach ($directory in $directories) {
$generationDir = Join-Path $generationDir $folder
}

#create the directory if it doesn't exist
# create the directory if it doesn't exist
if (-not (Test-Path $generationDir)) {
New-Item -ItemType Directory -Path $generationDir | Out-Null
}
Expand All @@ -111,6 +110,11 @@ foreach ($directory in $directories) {
exit $LASTEXITCODE
}

# srv-driven contains two separate specs, for two separate clients. We need to generate both.
if ($folders.Contains("srv-driven")) {
Generate-Srv-Driven $directory.FullName $generationDir -generateStub $stubbed
}

# TODO need to build but depends on https://github.com/Azure/autorest.csharp/issues/4463
}

Expand Down
33 changes: 32 additions & 1 deletion packages/http-client-csharp/eng/scripts/Generation.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ function Get-TspCommand {
param (
[string]$specFile,
[string]$generationDir,
[bool]$generateStub = $false
[bool]$generateStub = $false,
[string]$namespaceOverride = $null
)
$command = "npx tsp compile $specFile"
$command += " --trace @typespec/http-client-csharp"
Expand All @@ -38,6 +39,11 @@ function Get-TspCommand {
if ($generateStub) {
$command += " --option @typespec/http-client-csharp.plugin-name=StubLibraryPlugin"
}

if ($namespaceOverride) {
$command += " --option @typespec/http-client-csharp.namespace=$namespaceOverride"
}

return $command
}

Expand Down Expand Up @@ -72,7 +78,32 @@ function Compare-Paths {
return $normalizedPath1.Contains($normalizedPath2)
}

function Generate-Srv-Driven {
param (
[string]$specFilePath,
[string]$outputDir,
[bool]$generateStub = $false,
[bool]$createOutputDirIfNotExist = $true
)

$specFilePath = $(Join-Path $specFilePath "old.tsp")
$outputDir = $(Join-Path $outputDir "v1")
if ($createOutputDirIfNotExist -and -not (Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir | Out-Null
}

Write-Host "Generating http\resiliency\srv-driven\v1" -ForegroundColor Cyan
Invoke (Get-TspCommand $specFilePath $outputDir -generateStub $generateStub -namespaceOverride "Resiliency.ServiceDriven.V1")

# exit if the generation failed
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
}


Export-ModuleMember -Function "Invoke"
Export-ModuleMember -Function "Get-TspCommand"
Export-ModuleMember -Function "Refresh-Build"
Export-ModuleMember -Function "Compare-Paths"
Export-ModuleMember -Function "Generate-Srv-Driven"
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ foreach ($directory in $directories) {
$outputDir = $directory.FullName.Substring(0, $directory.FullName.IndexOf("src") - 1)
$subPath = $outputDir.Substring($cadlRanchRoot.Length + 1)

if ($subPath.Contains($(Join-Path 'srv-driven' 'v1'))) {
continue
}

Write-Host "Regenerating $subPath" -ForegroundColor Cyan

$specFile = Join-Path $specsDirectory $subPath "client.tsp"
Expand All @@ -40,6 +44,11 @@ foreach ($directory in $directories) {
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}

# srv-driven contains two separate specs, for two separate clients. We need to generate both.
if ($subPath.Contains('srv-driven')) {
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved
Generate-Srv-Driven $(Join-Path $specsDirectory $subPath) $outputDir -createOutputDirIfNotExist $false
}
}

# test all
Expand Down
10 changes: 10 additions & 0 deletions packages/http-client-csharp/eng/scripts/Test-CadlRanch.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ foreach ($directory in $directories) {
if (-not (Compare-Paths $subPath $filter)) {
continue
}

if ($subPath.Contains($(Join-Path 'srv-driven' 'v1'))) {
continue
}

$testPath = "$cadlRanchRoot.Tests"
$testFilter = "TestProjects.CadlRanch.Tests"
Expand Down Expand Up @@ -64,6 +68,12 @@ foreach ($directory in $directories) {
exit $LASTEXITCODE
}

# srv-driven contains two separate specs, for two separate clients. We need to generate both.
if ($subPath.Contains("srv-driven")) {
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved
Generate-Srv-Driven $(Join-Path $specsDirectory $subPath) $outputDir -createOutputDirIfNotExist $false
}


Write-Host "Testing $subPath" -ForegroundColor Cyan
$command = "dotnet test $cadlRanchCsproj --filter `"FullyQualifiedName~$testFilter`""
Invoke $command
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ public class ScmMethodProviderCollection : MethodProviderCollection
private string _cleanOperationName;
private readonly MethodProvider _createRequestMethod;

private readonly string _createRequestMethodName;

private ClientProvider Client { get; }

public ScmMethodProviderCollection(InputOperation operation, TypeProvider enclosingType)
: base(operation, enclosingType)
{
_cleanOperationName = operation.Name.ToCleanName();
_createRequestMethodName = "Create" + _cleanOperationName + "Request";
Client = enclosingType as ClientProvider ?? throw new InvalidOperationException("Scm methods can only be built for client types.");
_createRequestMethod = Client.RestClient.GetCreateRequestMethod(Operation);
}
Expand Down Expand Up @@ -388,6 +385,7 @@ private ScmMethodProvider BuildProtocolMethod(MethodProvider createRequestMethod

var requiredParameters = new List<ParameterProvider>();
var optionalParameters = new List<ParameterProvider>();

for (var i = 0; i < ProtocolMethodParameters.Count; i++)
{
var parameter = ProtocolMethodParameters[i];
Expand All @@ -407,14 +405,17 @@ private ScmMethodProvider BuildProtocolMethod(MethodProvider createRequestMethod
{
// If there are optional parameters, but the request options parameter is not optional, make the optional parameters nullable required.
// This is to ensure that the request options parameter is always the last parameter.
foreach (var parameter in optionalParameters)

for (var i = 0; i < optionalParameters.Count; i++)
{
parameter.DefaultValue = null;
parameter.Type = parameter.Type.WithNullable(true);
// Ensure a new copy is made to avoid modifying the original reference.
var optionalParam = optionalParameters[i].ToPublicInputParameter();
jorgerangel-msft marked this conversation as resolved.
Show resolved Hide resolved

optionalParam.DefaultValue = null;
optionalParam.Type = optionalParameters[i].Type.WithNullable(true);
requiredParameters.Add(optionalParam);
}
// Now, the request options parameter can be optional due to the above changes to the method signature.
requestOptionsParameter = ScmKnownParameters.OptionalRequestOptions;
requiredParameters.AddRange(optionalParameters);

optionalParameters.Clear();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ public static IEnumerable<TestCaseData> RequestOptionsParameterInSignatureTestCa
]), false, false);

// Protocol & convenience methods will have the same parameters.
// One of the parameter is optional, so it should be make required in the protocol method, and RequestOptions can be optional.
// One of the parameter is optional, so it should be made required in the protocol method.
yield return new TestCaseData(
InputFactory.Operation(
"TestOperation",
Expand All @@ -755,7 +755,7 @@ public static IEnumerable<TestCaseData> RequestOptionsParameterInSignatureTestCa
InputPrimitiveType.Int64,
location: RequestLocation.None,
isRequired: true),
]), true, true);
]), false, true);

// Protocol & convenience methods will have the same parameters.
// One of the parameter is optional value type, so it should be made nullable required in the protocol method, and RequestOptions can be optional.
Expand All @@ -774,7 +774,7 @@ public static IEnumerable<TestCaseData> RequestOptionsParameterInSignatureTestCa
InputPrimitiveType.Int64,
location: RequestLocation.None,
isRequired: true),
]), true, true);
]), false, true);

// convenience method only has a body param, so RequestOptions should be optional in protocol method.
yield return new TestCaseData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@
"commandName": "Executable",
"executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe"
},
"http-resiliency-srv-driven": {
"commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/resiliency/srv-driven -p StubLibraryPlugin",
"commandName": "Executable",
"executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe"
},
"http-resiliency-srv-driven-v1": {
"commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/resiliency/srv-driven/v1 -p StubLibraryPlugin",
"commandName": "Executable",
"executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe"
},
"http-routes": {
"commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/routes -p StubLibraryPlugin",
"commandName": "Executable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ private ParameterProvider BuildInputVariant()
wireInfo: WireInfo,
validation: Validation)
{
_asVariable = AsExpression
_asVariable = AsExpression,
SpreadSource = SpreadSource
};
}

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

using NUnit.Framework;
using Resiliency.ServiceDriven.V1;
using System.Threading.Tasks;

namespace TestProjects.CadlRanch.Tests.Http.Resiliency.SrvDriven
{
/// <summary>
/// Contains tests for the service-driven resiliency V1 client.
/// </summary>
public partial class SrvDrivenTests : CadlRanchTestBase
{
// This test validates the v1 client behavior when both the service deployment and api version are set to V1.
[CadlRanchTest]
public Task AddOptionalParamFromNone_V1Client_V1Service_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV1, options);
var response = await client.FromNoneAsync();

Assert.AreEqual(204, response.GetRawResponse().Status);
});

// This test validates the v1 client behavior when the service deployment is set to V2 and the api version is set to V1.
[CadlRanchTest]
public Task AddOptionalParamFromNone_V1Client_V2Service_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromNoneAsync();

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneOptional_V1Client_V1Service_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV1, options);
var response = await client.FromOneOptionalAsync("optional");

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneOptional_V1Client_V2Service_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromOneOptionalAsync("optional", cancellationToken: default);

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneRequired_V1Client_V1Service_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV1, options);
var response = await client.FromOneRequiredAsync("required");

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneRequired_V1Client_V2Service_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromOneRequiredAsync("required");

Assert.AreEqual(204, response.GetRawResponse().Status);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using NUnit.Framework;
using System.Threading.Tasks;
using Resiliency.ServiceDriven;

namespace TestProjects.CadlRanch.Tests.Http.Resiliency.SrvDriven
{
public partial class SrvDrivenTests : CadlRanchTestBase
{
private const string ServiceDeploymentV1 = "v1";
private const string ServiceDeploymentV2 = "v2";

[CadlRanchTest]
public Task AddOperation() => Test(async (host) =>
{
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2);
var response = await client.AddOperationAsync();

Assert.AreEqual(204, response.GetRawResponse().Status);
});

// This test validates the "new" client behavior when the api version is set to V1.
[CadlRanchTest]
public Task AddOptionalParamFromNone_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromNoneAsync();

Assert.AreEqual(204, response.GetRawResponse().Status);
});

// This test validates the "new" client behavior when the api version is set to V2.
[CadlRanchTest]
public Task AddOptionalParamFromNone_WithApiVersionV2() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V2);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromNoneAsync("new", cancellationToken: default);

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneOptional_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromOneOptionalAsync("optional");

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneOptional_WithApiVersionV2() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V2);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromOneOptionalAsync("optional", "new", cancellationToken: default);

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneRequired_WithApiVersionV1() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V1);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromOneRequiredAsync("required");

Assert.AreEqual(204, response.GetRawResponse().Status);
});

[CadlRanchTest]
public Task AddOptionalParamFromOneRequired_WithApiVersionV2() => Test(async (host) =>
{
var options = new ResiliencyServiceDrivenClientOptions(ResiliencyServiceDrivenClientOptions.ServiceVersion.V2);
var client = new ResiliencyServiceDrivenClient(host, ServiceDeploymentV2, options);
var response = await client.FromOneRequiredAsync("required", "new", cancellationToken: default);

Assert.AreEqual(204, response.GetRawResponse().Status);
});
}
}
Loading