Skip to content

Commit

Permalink
[http-client-csharp] adopt http\resiliency\srv-driven (#5142)
Browse files Browse the repository at this point in the history
fixes: #3983
  • Loading branch information
jorgerangel-msft authored Nov 20, 2024
1 parent 432ca32 commit 1cf8601
Show file tree
Hide file tree
Showing 23 changed files with 1,170 additions and 22 deletions.
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')) {
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")) {
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 @@ -395,6 +392,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 @@ -419,8 +417,7 @@ private ScmMethodProvider BuildProtocolMethod(MethodProvider createRequestMethod
parameter.DefaultValue = null;
parameter.Type = parameter.Type.WithNullable(true);
}
// 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 Expand Up @@ -494,14 +491,6 @@ private bool ShouldAddOptionalRequestOptionsParameter()
{
return true;
}
if (ProtocolMethodParameters[i].DefaultValue == null && ConvenienceMethodParameters[i].DefaultValue != null)
{
return true;
}
if (ProtocolMethodParameters[i].DefaultValue != null && ConvenienceMethodParameters[i].DefaultValue == null)
{
return true;
}
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,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 @@ -761,7 +761,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 @@ -780,7 +780,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,11 @@
"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-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

0 comments on commit 1cf8601

Please sign in to comment.