Skip to content

Commit

Permalink
Add support for paged LROs (#809)
Browse files Browse the repository at this point in the history
  • Loading branch information
pakrym authored Jun 16, 2020
1 parent c175a9a commit fac2bc4
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 76 deletions.
33 changes: 25 additions & 8 deletions src/AutoRest.CSharp.V3/Generation/Writers/ClientWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private void WriteClientCtors(CodeWriter writer, Client client)
private void WritePagingOperation(CodeWriter writer, PagingMethod pagingMethod, bool async)
{
var pageType = pagingMethod.ItemType;
var pageType = pagingMethod.PagingResponse.ItemType;
CSharpType responseType = async ? new CSharpType(typeof(AsyncPageable<>), pageType) : new CSharpType(typeof(Pageable<>), pageType);
var parameters = pagingMethod.Method.Parameters;
Expand All @@ -202,13 +202,15 @@ private void WritePagingOperation(CodeWriter writer, PagingMethod pagingMethod,
var pageWrappedType = new CSharpType(typeof(Page<>), pageType);
var funcType = async ? new CSharpType(typeof(Task<>), pageWrappedType) : pageWrappedType;
var nullableInt = new CSharpType(typeof(int), true);
var continuationTokenText = pagingMethod.NextLinkName != null ? $"response.Value.{pagingMethod.NextLinkName}" : "null";
var nextLinkName = pagingMethod.PagingResponse.NextLinkProperty?.Declaration.Name;
var itemName = pagingMethod.PagingResponse.ItemProperty.Declaration.Name;
var continuationTokenText = nextLinkName != null ? $"response.Value.{nextLinkName}" : "null";
var asyncText = async ? "async" : string.Empty;
var awaitText = async ? "await" : string.Empty;
var configureAwaitText = async ? ".ConfigureAwait(false)" : string.Empty;
using (writer.Scope($"{asyncText} {funcType} FirstPageFunc({nullableInt} pageSizeHint)"))
using (writer.Scope($"{asyncText} {funcType} FirstPageFunc({typeof(int?)} pageSizeHint)"))
{
WriteDiagnosticScope(writer, pagingMethod.Diagnostics, writer =>
{
Expand All @@ -219,7 +221,7 @@ private void WritePagingOperation(CodeWriter writer, PagingMethod pagingMethod,
}
writer.Line($"cancellationToken){configureAwaitText};");
writer.Line($"return {typeof(Page)}.FromValues(response.Value.{pagingMethod.ItemName}, {continuationTokenText}, response.GetRawResponse());");
writer.Line($"return {typeof(Page)}.FromValues(response.Value.{itemName}, {continuationTokenText}, response.GetRawResponse());");
});
}
Expand All @@ -228,7 +230,7 @@ private void WritePagingOperation(CodeWriter writer, PagingMethod pagingMethod,
{
nextPageFunctionName = "NextPageFunc";
var nextPageParameters = pagingMethod.NextPageMethod.Parameters;
using (writer.Scope($"{asyncText} {funcType} {nextPageFunctionName}({typeof(string)} nextLink, {nullableInt} pageSizeHint)"))
using (writer.Scope($"{asyncText} {funcType} {nextPageFunctionName}({typeof(string)} nextLink, {typeof(int?)} pageSizeHint)"))
{
WriteDiagnosticScope(writer, pagingMethod.Diagnostics, writer =>
{
Expand All @@ -238,7 +240,7 @@ private void WritePagingOperation(CodeWriter writer, PagingMethod pagingMethod,
writer.Append($"{parameter.Name}, ");
}
writer.Line($"cancellationToken){configureAwaitText};");
writer.Line($"return {typeof(Page)}.FromValues(response.Value.{pagingMethod.ItemName}, {continuationTokenText}, response.GetRawResponse());");
writer.Line($"return {typeof(Page)}.FromValues(response.Value.{itemName}, {continuationTokenText}, response.GetRawResponse());");
});
}
}
Expand Down Expand Up @@ -317,7 +319,22 @@ private void WriteStartOperationOperation(CodeWriter writer, LongRunningOperatio
writer.Append($"{parameter.Name}, ");
}
writer.RemoveTrailingComma();
writer.Line($").Request, originalResponse);");
writer.Append($").Request, originalResponse");
var nextPageMethod = lroMethod.Operation.NextPageMethod;
if (nextPageMethod != null)
{
writer.Append($", nextLink => RestClient.{CreateMethodName(nextPageMethod.Name, true)}(nextLink, ");
foreach (Parameter parameter in parameters)
{
writer.Append($"{parameter.Name}, ");
}
writer.Append($"cancellationToken)");
}
writer.Line($");");
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using AutoRest.CSharp.V3.Generation.Types;
using AutoRest.CSharp.V3.Output.Models;
using AutoRest.CSharp.V3.Output.Models.Requests;
using AutoRest.CSharp.V3.Output.Models.Serialization;
using Azure;
Expand All @@ -19,16 +21,37 @@ internal class LongRunningOperationWriter
public static void Write(CodeWriter writer, LongRunningOperation operation)
{
var responseVariable = "response";
var pagingResponse = operation.PagingResponse;

void WriteResultFunction(bool async)
{
if (operation.ResultSerialization != null)
{
writer.WriteDeserializationForMethods(
operation.ResultSerialization,
async: async,
(w, v) => w.Line($"return {v};"),
responseVariable);
if (pagingResponse != null)
{
var itemPropertyName = pagingResponse.ItemProperty.Declaration.Name;
var nextLinkPropertyName = pagingResponse.NextLinkProperty?.Declaration.Name;

writer.Line($"{pagingResponse.ResponseType} firstPageResult;");
writer.WriteDeserializationForMethods(
operation.ResultSerialization,
async: async,
(w, v) => w.Line($"firstPageResult = {v};"),
responseVariable);

writer.Line($"{pagingResponse.PageType} firstPage = {typeof(Page)}.FromValues(firstPageResult.{itemPropertyName}, firstPageResult.{nextLinkPropertyName}, {responseVariable});");
writer.Line();

writer.Line($"return {typeof(PageableHelpers)}.CreateAsyncEnumerable(_ => Task.FromResult(firstPage), (nextLink, _) => GetNextPage(nextLink, cancellationToken));");
}
else
{
writer.WriteDeserializationForMethods(
operation.ResultSerialization,
async: async,
(w, v) => w.Line($"return {v};"),
responseVariable);
}
}
else
{
Expand Down Expand Up @@ -57,9 +80,27 @@ void WriteResultFunction(bool async)
{
writer.Line($"private readonly {helperType} _operation;");

using (writer.Scope($"internal {cs.Name}({typeof(ClientDiagnostics)} clientDiagnostics, {typeof(HttpPipeline)} pipeline, {typeof(Request)} request, {typeof(Response)} response)"))
if (pagingResponse != null)
{
writer.Line($"private readonly {typeof(Func<string, Task<Response>>)} _nextPageFunc;");
}

writer.Append($"internal {cs.Name}({typeof(ClientDiagnostics)} clientDiagnostics, {typeof(HttpPipeline)} pipeline, {typeof(Request)} request, {typeof(Response)} response");

if (pagingResponse != null)
{
writer.Append($", {typeof(Func<string, Task<Response>>)} nextPageFunc");
}
writer.Line($")");

using (writer.Scope())
{
writer.Line($"_operation = new {helperType}(this, clientDiagnostics, pipeline, request, response, {typeof(OperationFinalStateVia)}.{operation.FinalStateVia}, {operation.Diagnostics.ScopeName:L});");

if (pagingResponse != null)
{
writer.Line($"_nextPageFunc = nextPageFunc;");
}
}

writer.WriteXmlDocumentationInheritDoc();
Expand Down Expand Up @@ -108,6 +149,30 @@ void WriteResultFunction(bool async)
{
WriteResultFunction(true);
}

if (pagingResponse != null)
{
writer.Line();

Debug.Assert(operation.ResultSerialization != null);

var funcType = new CSharpType(typeof(Task<>), pagingResponse.PageType);
var itemPropertyName = pagingResponse.ItemProperty.Declaration.Name;
var nextLinkPropertyName = pagingResponse.NextLinkProperty?.Declaration.Name;

using (writer.Scope($"private async {funcType} GetNextPage({typeof(string)} nextLink, {typeof(CancellationToken)} cancellationToken)"))
{
writer.Line($"{typeof(Response)} {responseVariable} = await _nextPageFunc(nextLink);");
writer.Line($"{pagingResponse.ResponseType} nextPageResult;");
writer.WriteDeserializationForMethods(
operation.ResultSerialization,
async: true,
(w, v) => w.Line($"nextPageResult = {v};"),
responseVariable);

writer.Line($"return {typeof(Page)}.FromValues(nextPageResult.{itemPropertyName}, nextPageResult.{nextLinkPropertyName}, {responseVariable});");
}
}
}
}
}
Expand Down
29 changes: 2 additions & 27 deletions src/AutoRest.CSharp.V3/Output/Models/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,37 +65,12 @@ private IEnumerable<PagingMethod> BuildPagingMethods()
throw new InvalidOperationException($"Method {method.Name} has to have a return value");
}

TypeProvider implementation = objectResponseBody.Type.Implementation;
if (!(implementation is ObjectType type))
{
throw new InvalidOperationException($"The return type of {method.Name} has to be an object schema to be used in paging");
}

string? nextLinkName = paging.NextLinkName;
string itemName = paging.ItemName ?? "value";

ObjectTypeProperty itemProperty = type.GetPropertyBySerializedName(itemName);

ObjectTypeProperty? nextLinkProperty = null;
if (!string.IsNullOrWhiteSpace(nextLinkName))
{
nextLinkProperty = type.GetPropertyBySerializedName(nextLinkName);
}

if (!(itemProperty.SchemaProperty?.Schema is ArraySchema arraySchema))
{
throw new InvalidOperationException($"{itemName} property has to be an array schema, actual {itemProperty.SchemaProperty?.Schema}");
}

CSharpType itemType = _context.TypeFactory.CreateType(arraySchema.ElementType, false);
yield return new PagingMethod(
method,
nextPageMethod,
method.Name,
nextLinkProperty?.Declaration.Name,
itemProperty.Declaration.Name,
itemType,
new Diagnostic($"{Declaration.Name}.{method.Name}"));
new Diagnostic($"{Declaration.Name}.{method.Name}"),
new PagingResponseInfo(paging, objectResponseBody.Type));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Diagnostics;
using System.Linq;
using AutoRest.CSharp.V3.Generation.Types;
using AutoRest.CSharp.V3.Input;
using AutoRest.CSharp.V3.Output.Builders;
Expand All @@ -23,6 +24,7 @@ public LongRunningOperation(OperationGroup operationGroup, Operation operation,

Debug.Assert(clientClass != null, "clientClass != null, LROs should be disabled when public clients are disables");

Client = clientClass;
DefaultName = clientClass.RestClient.ClientPrefix + operation.CSharpName() + "Operation";
FinalStateVia = operation.LongRunningFinalStateVia switch
{
Expand All @@ -40,6 +42,14 @@ public LongRunningOperation(OperationGroup operationGroup, Operation operation,
{
ResultType = TypeFactory.GetOutputType(context.TypeFactory.CreateType(finalResponseSchema, false));
ResultSerialization = new SerializationBuilder().Build(finalResponse.HttpResponse.KnownMediaType, finalResponseSchema, ResultType);

Paging? paging = operation.Language.Default.Paging;
if (paging != null)
{
NextPageMethod = Client.RestClient.GetNextOperationMethod(operation.Requests.Single());
PagingResponse = new PagingResponseInfo(paging, ResultType);
ResultType = new CSharpType(typeof(AsyncPageable<>), PagingResponse.ItemType);
}
}
else
{
Expand All @@ -51,9 +61,12 @@ public LongRunningOperation(OperationGroup operationGroup, Operation operation,
}

public CSharpType ResultType { get; }
public Client Client { get; }
public OperationFinalStateVia FinalStateVia { get; }
public Diagnostic Diagnostics => new Diagnostic(Declaration.Name);
public ObjectSerialization? ResultSerialization { get; }
public ObjectSerialization? ResultSerialization { get; }
public RestClientMethod? NextPageMethod { get; }
public PagingResponseInfo? PagingResponse { get; }
public string Description { get; }
protected override string DefaultName { get; }
protected override string DefaultAccessibility { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ public LongRunningOperationMethod(string name, LongRunningOperation operation, R

public RestClientMethod StartMethod { get; }

public Diagnostic Diagnostics { get; set; }
public Diagnostic Diagnostics { get; }
}
}
14 changes: 4 additions & 10 deletions src/AutoRest.CSharp.V3/Output/Models/Requests/PagingMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,23 @@
// Licensed under the MIT License. See License.txt in the project root for license information.


using AutoRest.CSharp.V3.Generation.Types;

namespace AutoRest.CSharp.V3.Output.Models.Requests
{
internal class PagingMethod
{
public PagingMethod(RestClientMethod method, RestClientMethod? nextPageMethod, string name, string? nextLinkName, string itemName, CSharpType itemType, Diagnostic diagnostics)
public PagingMethod(RestClientMethod method, RestClientMethod? nextPageMethod, string name, Diagnostic diagnostics, PagingResponseInfo pagingResponse)
{
Method = method;
NextPageMethod = nextPageMethod;
Name = name;
NextLinkName = nextLinkName;
ItemName = itemName;
ItemType = itemType;
Diagnostics = diagnostics;
PagingResponse = pagingResponse;
}

public string Name { get; }
public RestClientMethod Method { get; }
public RestClientMethod? NextPageMethod { get; }
public string? NextLinkName { get; }
public string ItemName { get; }
public CSharpType ItemType { get; }
public Diagnostic Diagnostics { get; set; }
public PagingResponseInfo PagingResponse { get; }
public Diagnostic Diagnostics { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using AutoRest.CSharp.V3.Generation.Types;
using AutoRest.CSharp.V3.Input;
using AutoRest.CSharp.V3.Output.Models.Types;
using Azure;

namespace AutoRest.CSharp.V3.Output.Models.Requests
{
internal class PagingResponseInfo
{
public PagingResponseInfo(Paging paging, CSharpType type)
{
ResponseType = type;

TypeProvider implementation = type.Implementation;
if (!(implementation is ObjectType objectType))
{
throw new InvalidOperationException($"The type '{type}' has to be an object schema to be used in paging");
}

string? nextLinkName = paging.NextLinkName;
string itemName = paging.ItemName ?? "value";

ObjectTypeProperty itemProperty = objectType.GetPropertyBySerializedName(itemName);

ObjectTypeProperty? nextLinkProperty = null;
if (!string.IsNullOrWhiteSpace(nextLinkName))
{
nextLinkProperty = objectType.GetPropertyBySerializedName(nextLinkName);
}

if (!TypeFactory.IsList(itemProperty.Declaration.Type))
{
throw new InvalidOperationException($"{itemName} property has to be an array schema, actual {itemProperty.SchemaProperty?.Schema}");
}

CSharpType itemType = TypeFactory.GetElementType(itemProperty.Declaration.Type);
NextLinkProperty = nextLinkProperty;
ItemProperty = itemProperty;
ItemType = itemType;
}

public CSharpType ResponseType { get; }
public ObjectTypeProperty? NextLinkProperty { get; }
public ObjectTypeProperty ItemProperty { get; }
public CSharpType PageType => new CSharpType(typeof(Page<>), ItemType);
public CSharpType ItemType { get; }
}
}
Loading

0 comments on commit fac2bc4

Please sign in to comment.