Skip to content

Commit 763ca4a

Browse files
Simplify paging API and implementation (microsoft#7621)
Fixes microsoft#7618
1 parent 2d139cc commit 763ca4a

19 files changed

+173
-199
lines changed

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/CollectionResultDefinition.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ private MethodBodyStatement[] BuildGetRawPagesForNextLink()
314314
// Declare the initial request message
315315
Declare(
316316
"message",
317-
InvokeCreateRequestForNextLink(_requestFields[0].As<Uri>()),
317+
InvokeCreateInitialRequest(),
318318
out ScopedApi<PipelineMessage> message),
319319

320320
// Declare nextPageUri variable
@@ -388,7 +388,7 @@ private MethodBodyStatement[] BuildGetRawPagesForSingle()
388388
{
389389
var pipelineMessageDeclaration = Declare(
390390
"message",
391-
InvokeCreateRequestForSingle(),
391+
InvokeCreateInitialRequest(),
392392
out ScopedApi<PipelineMessage> m);
393393
var pipelineResponse = ScmCodeModelGenerator.Instance.TypeFactory.ClientResponseApi.ToExpression().FromResponse(
394394
_clientField.Property("Pipeline").ToApi<ClientPipelineApi>().ProcessMessage(
@@ -435,11 +435,15 @@ private MethodBodyStatement[] AssignAndCheckNextPageVariable(ClientResponseApi r
435435
}
436436
}
437437

438-
private ScopedApi<PipelineMessage> InvokeCreateRequestForNextLink(ValueExpression nextPageUri) => _clientField.Invoke(
439-
_createRequestMethodName,
440-
// we replace the first argument (the initialUri) with the nextPageUri
441-
[nextPageUri, .. _requestFields.Skip(1)])
442-
.As<PipelineMessage>();
438+
private ScopedApi<PipelineMessage> InvokeCreateRequestForNextLink(ValueExpression nextPageUri)
439+
{
440+
var createNextLinkRequestMethodName =
441+
_client.RestClient.GetCreateNextLinkRequestMethod(_operation).Signature.Name;
442+
return _clientField.Invoke(
443+
createNextLinkRequestMethodName,
444+
[nextPageUri, .._requestFields])
445+
.As<PipelineMessage>();
446+
}
443447

444448
private ScopedApi<PipelineMessage> InvokeCreateRequestForContinuationToken(ValueExpression nextToken)
445449
{
@@ -451,7 +455,7 @@ private ScopedApi<PipelineMessage> InvokeCreateRequestForContinuationToken(Value
451455
return _clientField.Invoke(_createRequestMethodName, arguments).As<PipelineMessage>();
452456
}
453457

454-
private ScopedApi<PipelineMessage> InvokeCreateRequestForSingle()
458+
private ScopedApi<PipelineMessage> InvokeCreateInitialRequest()
455459
{
456460
ValueExpression[] arguments = [.. _requestFields.Select(f => f.AsValueExpression)];
457461

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public class RestClientProvider : TypeProvider
3131
};
3232
private Dictionary<InputOperation, MethodProvider>? _methodCache;
3333
private Dictionary<InputOperation, MethodProvider> MethodCache => _methodCache ??= [];
34+
private Dictionary<InputOperation, MethodProvider>? _nextMethodCache;
35+
private Dictionary<InputOperation, MethodProvider> NextMethodCache => _nextMethodCache ??= [];
3436

3537
private readonly Dictionary<List<int>, PropertyProvider> _pipelineMessage20xClassifiers;
3638
private readonly InputClient _inputClient;
@@ -81,20 +83,35 @@ protected override ScmMethodProvider[] BuildMethods()
8183
var method = BuildCreateRequestMethod(serviceMethod);
8284
methods.Add(method);
8385
MethodCache[operation] = method;
86+
87+
// For paging operations with next link, also generate a CreateNextXXXRequest method
88+
if (serviceMethod is InputPagingServiceMethod { PagingMetadata.NextLink: not null })
89+
{
90+
var nextMethod = BuildCreateRequestMethod(serviceMethod, isNextLinkRequest: true);
91+
methods.Add(nextMethod);
92+
NextMethodCache[operation] = nextMethod;
93+
}
8494
}
8595

8696
return [.. methods];
8797
}
8898

89-
private ScmMethodProvider BuildCreateRequestMethod(InputServiceMethod serviceMethod)
99+
private ScmMethodProvider BuildCreateRequestMethod(InputServiceMethod serviceMethod, bool isNextLinkRequest = false)
90100
{
91101
var pipelineField = ClientProvider.PipelineProperty.ToApi<ClientPipelineApi>();
92102

93103
var options = ScmKnownParameters.RequestOptions;
94104
var parameters = GetMethodParameters(serviceMethod, MethodType.CreateRequest);
105+
if (isNextLinkRequest)
106+
{
107+
parameters = [ScmKnownParameters.NextPage, .. parameters];
108+
}
95109
var operation = serviceMethod.Operation;
110+
var methodName = isNextLinkRequest
111+
? $"CreateNext{operation.Name.ToIdentifierName()}Request"
112+
: $"Create{operation.Name.ToIdentifierName()}Request";
96113
var signature = new MethodSignature(
97-
$"Create{operation.Name.ToIdentifierName()}Request",
114+
methodName,
98115
null,
99116
MethodSignatureModifiers.Internal,
100117
ScmCodeModelGenerator.Instance.TypeFactory.HttpMessageApi.HttpMessageType,
@@ -117,7 +134,7 @@ private ScmMethodProvider BuildCreateRequestMethod(InputServiceMethod serviceMet
117134
message.ApplyResponseClassifier(classifier.ToApi<StatusCodeClassifierApi>()),
118135
Declare("request", message.Request().ToApi<HttpRequestApi>(), out HttpRequestApi request),
119136
request.SetMethod(operation.HttpMethod),
120-
BuildRequest(serviceMethod, request, paramMap, signature),
137+
BuildRequest(serviceMethod, request, paramMap, signature, isNextLinkRequest: isNextLinkRequest),
121138
message.ApplyRequestOptions(options.ToApi<HttpRequestOptionsApi>()),
122139
Return(message)
123140
]),
@@ -130,7 +147,8 @@ private MethodBodyStatement BuildRequest(
130147
InputServiceMethod serviceMethod,
131148
HttpRequestApi request,
132149
Dictionary<string, ParameterProvider> paramMap,
133-
MethodSignature signature)
150+
MethodSignature signature,
151+
bool isNextLinkRequest = false)
134152
{
135153
InputPagingServiceMethod? pagingServiceMethod = serviceMethod as InputPagingServiceMethod;
136154
var operation = serviceMethod.Operation;
@@ -145,20 +163,16 @@ .. AppendQueryParameters(uri, operation, paramMap),
145163
.. AppendHeaderParameters(request, operation, paramMap),
146164
.. GetSetContent(request, signature.Parameters)
147165
];
148-
if (pagingServiceMethod?.PagingMetadata.NextLink != null)
166+
167+
// For next request methods, handle URI differently
168+
if (isNextLinkRequest && pagingServiceMethod?.PagingMetadata.NextLink != null)
149169
{
150-
return new[]
151-
{
152-
declareUri,
153-
new IfElseStatement(
154-
new IfStatement(ScmKnownParameters.NextPage.NotEqual(Null))
155-
{
156-
uri.Reset(ScmKnownParameters.NextPage.AsExpression()).Terminate(),
157-
request.SetUri(uri),
158-
new MethodBodyStatements(AppendHeaderParameters(request, operation, paramMap, isNextLink: true).ToList())
159-
},
160-
new MethodBodyStatements([..statements]))
161-
};
170+
return new MethodBodyStatements([
171+
declareUri,
172+
uri.Reset(ScmKnownParameters.NextPage.AsExpression()).Terminate(),
173+
request.SetUri(uri),
174+
new MethodBodyStatements(AppendHeaderParameters(request, operation, paramMap, isNextLink: true).ToList())
175+
]);
162176
}
163177

164178
return new MethodBodyStatements([declareUri, .. statements]);
@@ -600,11 +614,16 @@ public MethodProvider GetCreateRequestMethod(InputOperation operation)
600614
return MethodCache[operation];
601615
}
602616

617+
public MethodProvider GetCreateNextLinkRequestMethod(InputOperation operation)
618+
{
619+
_ = Methods; // Ensure methods are built
620+
return NextMethodCache[operation];
621+
}
622+
603623
internal static List<ParameterProvider> GetMethodParameters(InputServiceMethod serviceMethod, MethodType methodType)
604624
{
605625
SortedList<int, ParameterProvider> sortedParams = [];
606-
int paging = 0;
607-
int path = 1;
626+
int path = 0;
608627
int required = 100;
609628
int bodyRequired = 200;
610629
int bodyOptional = 300;
@@ -706,16 +725,6 @@ internal static List<ParameterProvider> GetMethodParameters(InputServiceMethod s
706725
{
707726
parameter.DefaultValue = null;
708727
}
709-
710-
InputPagingServiceMetadata? inputPagingServiceMetadata = serviceMethod is InputPagingServiceMethod pagingServiceMethod
711-
? pagingServiceMethod.PagingMetadata
712-
: null;
713-
714-
if (inputPagingServiceMetadata?.NextLink != null)
715-
{
716-
// Next link operations will always have an endpoint parameter in the CreateRequest method
717-
sortedParams.Add(paging, ScmKnownParameters.NextPage);
718-
}
719728
}
720729

721730
return [.. sortedParams.Values];

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ScmMethodProviderCollection.cs

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ private ScmMethodProvider BuildConvenienceMethod(MethodProvider protocolMethod,
130130
if (_pagingServiceMethod != null)
131131
{
132132
collection = ScmCodeModelGenerator.Instance.TypeFactory.ClientResponseApi.CreateClientCollectionResultDefinition(Client, _pagingServiceMethod, responseBodyType, isAsync);
133-
methodBody = GetPagingMethodBody(collection, ConvenienceMethodParameters, true);
133+
methodBody = [.. GetPagingMethodBody(collection, ConvenienceMethodParameters, true)];
134134
}
135135
else if (responseBodyType is null)
136136
{
@@ -564,7 +564,7 @@ private ScmMethodProvider BuildProtocolMethod(MethodProvider createRequestMethod
564564
if (_pagingServiceMethod != null)
565565
{
566566
collection = ScmCodeModelGenerator.Instance.TypeFactory.ClientResponseApi.CreateClientCollectionResultDefinition(Client, _pagingServiceMethod, null, isAsync);
567-
methodBody = GetPagingMethodBody(collection, parameters, false);
567+
methodBody = [.. GetPagingMethodBody(collection, parameters, false)];
568568
}
569569
else
570570
{
@@ -599,54 +599,28 @@ private ScmMethodProvider BuildProtocolMethod(MethodProvider createRequestMethod
599599
return protocolMethod;
600600
}
601601

602-
private MethodBodyStatement[] GetPagingMethodBody(
602+
private MethodBodyStatement GetPagingMethodBody(
603603
TypeProvider collection,
604604
IReadOnlyList<ParameterProvider> parameters,
605605
bool isConvenience)
606606
{
607-
return (_pagingServiceMethod!.PagingMetadata.NextLink, isConvenience) switch
607+
if (isConvenience)
608608
{
609-
(not null, true) =>
610-
[
611-
Return(New.Instance(
612-
collection.Type,
613-
[
614-
This,
615-
Null,
616-
.. parameters,
617-
IHttpRequestOptionsApiSnippets.FromCancellationToken(ScmKnownParameters.CancellationToken)
618-
]))
619-
],
620-
(not null, false) =>
621-
[
622-
Return(New.Instance(
623-
collection.Type,
624-
[
625-
This,
626-
Null,
627-
.. parameters
628-
]))
629-
],
630-
(null, true) =>
631-
[
632-
Return(New.Instance(
633-
collection.Type,
634-
[
635-
This,
636-
.. parameters,
637-
IHttpRequestOptionsApiSnippets.FromCancellationToken(ScmKnownParameters.CancellationToken)
638-
]))
639-
],
640-
(null, false) =>
609+
return Return(New.Instance(
610+
collection.Type,
611+
[
612+
This,
613+
.. parameters,
614+
IHttpRequestOptionsApiSnippets.FromCancellationToken(ScmKnownParameters.CancellationToken)
615+
]));
616+
}
617+
618+
return Return(New.Instance(
619+
collection.Type,
641620
[
642-
Return(New.Instance(
643-
collection.Type,
644-
[
645-
This,
646-
.. parameters
647-
]))
648-
]
649-
};
621+
This,
622+
.. parameters
623+
]));
650624
}
651625

652626
private CSharpType GetResponseType(IReadOnlyList<InputOperationResponse> responses, bool isConvenience, bool isAsync, out CSharpType? responseBodyType)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/CollectionResultDefinitions/TestData/NextLinkTests/NextLinkInBody.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,17 @@ namespace Sample
1313
internal partial class CatClientGetCatsCollectionResult : global::System.ClientModel.Primitives.CollectionResult
1414
{
1515
private readonly global::Sample.CatClient _client;
16-
private readonly global::System.Uri _nextPage;
1716
private readonly global::System.ClientModel.Primitives.RequestOptions _options;
1817

19-
public CatClientGetCatsCollectionResult(global::Sample.CatClient client, global::System.Uri nextPage, global::System.ClientModel.Primitives.RequestOptions options)
18+
public CatClientGetCatsCollectionResult(global::Sample.CatClient client, global::System.ClientModel.Primitives.RequestOptions options)
2019
{
2120
_client = client;
22-
_nextPage = nextPage;
2321
_options = options;
2422
}
2523

2624
public override global::System.Collections.Generic.IEnumerable<global::System.ClientModel.ClientResult> GetRawPages()
2725
{
28-
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_nextPage, _options);
26+
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_options);
2927
global::System.Uri nextPageUri = null;
3028
while (true)
3129
{
@@ -37,7 +35,7 @@ public CatClientGetCatsCollectionResult(global::Sample.CatClient client, global:
3735
{
3836
yield break;
3937
}
40-
message = _client.CreateGetCatsRequest(nextPageUri, _options);
38+
message = _client.CreateNextGetCatsRequest(nextPageUri, _options);
4139
}
4240
}
4341

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/CollectionResultDefinitions/TestData/NextLinkTests/NextLinkInBodyAsync.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,17 @@ namespace Sample
1313
internal partial class CatClientGetCatsAsyncCollectionResult : global::System.ClientModel.Primitives.AsyncCollectionResult
1414
{
1515
private readonly global::Sample.CatClient _client;
16-
private readonly global::System.Uri _nextPage;
1716
private readonly global::System.ClientModel.Primitives.RequestOptions _options;
1817

19-
public CatClientGetCatsAsyncCollectionResult(global::Sample.CatClient client, global::System.Uri nextPage, global::System.ClientModel.Primitives.RequestOptions options)
18+
public CatClientGetCatsAsyncCollectionResult(global::Sample.CatClient client, global::System.ClientModel.Primitives.RequestOptions options)
2019
{
2120
_client = client;
22-
_nextPage = nextPage;
2321
_options = options;
2422
}
2523

2624
public override async global::System.Collections.Generic.IAsyncEnumerable<global::System.ClientModel.ClientResult> GetRawPagesAsync()
2725
{
28-
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_nextPage, _options);
26+
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_options);
2927
global::System.Uri nextPageUri = null;
3028
while (true)
3129
{
@@ -37,7 +35,7 @@ public CatClientGetCatsAsyncCollectionResult(global::Sample.CatClient client, gl
3735
{
3836
yield break;
3937
}
40-
message = _client.CreateGetCatsRequest(nextPageUri, _options);
38+
message = _client.CreateNextGetCatsRequest(nextPageUri, _options);
4139
}
4240
}
4341

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/CollectionResultDefinitions/TestData/NextLinkTests/NextLinkInBodyOfT.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,17 @@ namespace Sample
1313
internal partial class CatClientGetCatsCollectionResultOfT : global::System.ClientModel.CollectionResult<global::Sample.Models.Cat>
1414
{
1515
private readonly global::Sample.CatClient _client;
16-
private readonly global::System.Uri _nextPage;
1716
private readonly global::System.ClientModel.Primitives.RequestOptions _options;
1817

19-
public CatClientGetCatsCollectionResultOfT(global::Sample.CatClient client, global::System.Uri nextPage, global::System.ClientModel.Primitives.RequestOptions options)
18+
public CatClientGetCatsCollectionResultOfT(global::Sample.CatClient client, global::System.ClientModel.Primitives.RequestOptions options)
2019
{
2120
_client = client;
22-
_nextPage = nextPage;
2321
_options = options;
2422
}
2523

2624
public override global::System.Collections.Generic.IEnumerable<global::System.ClientModel.ClientResult> GetRawPages()
2725
{
28-
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_nextPage, _options);
26+
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_options);
2927
global::System.Uri nextPageUri = null;
3028
while (true)
3129
{
@@ -37,7 +35,7 @@ public CatClientGetCatsCollectionResultOfT(global::Sample.CatClient client, glob
3735
{
3836
yield break;
3937
}
40-
message = _client.CreateGetCatsRequest(nextPageUri, _options);
38+
message = _client.CreateNextGetCatsRequest(nextPageUri, _options);
4139
}
4240
}
4341

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/CollectionResultDefinitions/TestData/NextLinkTests/NextLinkInBodyOfTAsync.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,17 @@ namespace Sample
1414
internal partial class CatClientGetCatsAsyncCollectionResultOfT : global::System.ClientModel.AsyncCollectionResult<global::Sample.Models.Cat>
1515
{
1616
private readonly global::Sample.CatClient _client;
17-
private readonly global::System.Uri _nextPage;
1817
private readonly global::System.ClientModel.Primitives.RequestOptions _options;
1918

20-
public CatClientGetCatsAsyncCollectionResultOfT(global::Sample.CatClient client, global::System.Uri nextPage, global::System.ClientModel.Primitives.RequestOptions options)
19+
public CatClientGetCatsAsyncCollectionResultOfT(global::Sample.CatClient client, global::System.ClientModel.Primitives.RequestOptions options)
2120
{
2221
_client = client;
23-
_nextPage = nextPage;
2422
_options = options;
2523
}
2624

2725
public override async global::System.Collections.Generic.IAsyncEnumerable<global::System.ClientModel.ClientResult> GetRawPagesAsync()
2826
{
29-
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_nextPage, _options);
27+
global::System.ClientModel.Primitives.PipelineMessage message = _client.CreateGetCatsRequest(_options);
3028
global::System.Uri nextPageUri = null;
3129
while (true)
3230
{
@@ -38,7 +36,7 @@ public CatClientGetCatsAsyncCollectionResultOfT(global::Sample.CatClient client,
3836
{
3937
yield break;
4038
}
41-
message = _client.CreateGetCatsRequest(nextPageUri, _options);
39+
message = _client.CreateNextGetCatsRequest(nextPageUri, _options);
4240
}
4341
}
4442

0 commit comments

Comments
 (0)