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

ClientModel: Updates to paging types to support sub-client enumerator pattern #44825

Merged
merged 55 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
bb32d2e
Initial checkin of changes to paging APIs
annelo-msft Jun 25, 2024
d6913bd
nits
annelo-msft Jun 25, 2024
271540a
rename ToValueCollection method to GetAllValues
annelo-msft Jun 25, 2024
50a82ac
nits
annelo-msft Jun 26, 2024
c16ebe0
Add back simplified PageToken; otherwise implementations must always …
annelo-msft Jun 26, 2024
48cadd9
rename to ClientToken for general use with LRO rehydration as well
annelo-msft Jun 26, 2024
614375f
nits
annelo-msft Jun 26, 2024
fbd085f
make ClientToken non-abstract
annelo-msft Jun 26, 2024
0c23dfc
nits
annelo-msft Jun 26, 2024
5f4ae59
proposed renames
annelo-msft Jun 26, 2024
ed35bf9
backup idea; I'm wondering if RequestOptions can always be handled by…
annelo-msft Jun 26, 2024
7f18c2a
remove RequestOptions from signatures
annelo-msft Jun 26, 2024
104c8f6
nits
annelo-msft Jun 26, 2024
cbd529f
GetPageCore - enable an overload that doesn't take a page token
annelo-msft Jun 27, 2024
31b9990
Change GetPage with no parameters to GetFirstPage
annelo-msft Jun 27, 2024
6a56d39
rename ClientToken to ContinuationToken and remove GetFirstPage method
annelo-msft Jun 27, 2024
ed7562e
Merge remote-tracking branch 'upstream/main' into scm-paging-types
annelo-msft Jun 28, 2024
6c64f45
Merge remote-tracking branch 'upstream/main' into scm-paging-types
annelo-msft Jul 1, 2024
bb41536
some updates based on feedback from Krzysztof
annelo-msft Jul 1, 2024
5b846cd
add Async suffix
annelo-msft Jul 1, 2024
875be9f
move to CurrentPageToken instead of FirstPageToken
annelo-msft Jul 1, 2024
844c62a
simplified page collection
annelo-msft Jul 1, 2024
c3bac5a
tidy
annelo-msft Jul 1, 2024
8768463
tidy
annelo-msft Jul 1, 2024
3d5ac74
implement interface explicitly
annelo-msft Jul 1, 2024
cd5aaa5
reimplement GetAllValues to account for possibility GetCurrentPage wa…
annelo-msft Jul 2, 2024
91126e3
add internal emitted files that generated code will use
annelo-msft Jul 2, 2024
fa8b2f4
Add stubs for mock paging client
annelo-msft Jul 2, 2024
d9020f2
flush out client a bit
annelo-msft Jul 2, 2024
ba5ea50
updates
annelo-msft Jul 2, 2024
9bee2fe
Backup of mock client WIP
annelo-msft Jul 3, 2024
3ca6e24
rework/rename and add tests prior to implementation
annelo-msft Jul 3, 2024
d9f743a
make first test pass
annelo-msft Jul 3, 2024
bcb7d69
sync tests all pass
annelo-msft Jul 3, 2024
c7ea4a5
backup sync async test base paging tests idea
annelo-msft Jul 3, 2024
a36fd22
separate unit and scenario tests
annelo-msft Jul 3, 2024
5c0fc3e
clean up sync unit tests
annelo-msft Jul 3, 2024
f0ec18b
add async unit tests
annelo-msft Jul 3, 2024
a30a9db
Add GetCurrentPageCore so that GetCurrentPage doesn't advance enumerator
annelo-msft Jul 3, 2024
4a64e56
complete sync scenario tests
annelo-msft Jul 3, 2024
ff84661
Add async tests
annelo-msft Jul 3, 2024
77388d3
nits
annelo-msft Jul 3, 2024
6642da1
Add refdocs
annelo-msft Jul 3, 2024
e85bbd5
Merge remote-tracking branch 'upstream/main' into scm-pageenumerator-…
annelo-msft Jul 5, 2024
1ade11c
address pr feedback on clarifying refdoc comments
annelo-msft Jul 5, 2024
c9707ba
Merge remote-tracking branch 'upstream/main' into scm-pageenumerator-…
annelo-msft Jul 5, 2024
f46a9a9
Merge remote-tracking branch 'upstream/main' into scm-pageenumerator-…
annelo-msft Jul 8, 2024
6344e11
Merge remote-tracking branch 'upstream/main' into scm-pageenumerator-…
annelo-msft Jul 8, 2024
361eaf2
update OAI Test csproj to opt out of project reference pipeline
annelo-msft Jul 8, 2024
ba3485a
add more ExcludeFromProjectReferenceToConversion
annelo-msft Jul 8, 2024
6d88d0e
add more ExcludeFromProjectReferenceToConversion
annelo-msft Jul 8, 2024
c3e9703
move build variables
annelo-msft Jul 8, 2024
c467fcb
temporarily opt-out Azure.Core.TestFramework
annelo-msft Jul 9, 2024
a312bec
revert TestFramework change and do it for Azure.Core instead
annelo-msft Jul 9, 2024
a974063
try it without OAI csproj mods
annelo-msft Jul 9, 2024
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
2 changes: 2 additions & 0 deletions sdk/core/Azure.Core/src/Azure.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
<PackageReference Include="System.Text.Json" />
<PackageReference Include="System.Text.Encodings.Web" />
<PackageReference Include="System.Memory.Data" />

<ExcludeFromProjectReferenceToConversion Include="System.ClientModel" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
Expand Down
57 changes: 36 additions & 21 deletions sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ public ApiKeyCredential(string key) { }
public static implicit operator System.ClientModel.ApiKeyCredential (string key) { throw null; }
public void Update(string key) { }
}
public abstract partial class AsyncPageableCollection<T> : System.ClientModel.AsyncResultCollection<T>
public abstract partial class AsyncCollectionResult<T> : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable<T>
{
protected AsyncPageableCollection() { }
public abstract System.Collections.Generic.IAsyncEnumerable<System.ClientModel.ResultPage<T>> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?));
public override System.Collections.Generic.IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected internal AsyncCollectionResult() { }
protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
}
public abstract partial class AsyncResultCollection<T> : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable<T>
public abstract partial class AsyncPageCollection<T> : System.Collections.Generic.IAsyncEnumerable<System.ClientModel.PageResult<T>>
{
protected internal AsyncResultCollection() { }
protected internal AsyncResultCollection(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
protected AsyncPageCollection() { }
public System.Collections.Generic.IAsyncEnumerable<T> GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected abstract System.Collections.Generic.IAsyncEnumerator<System.ClientModel.PageResult<T>> GetAsyncEnumeratorCore(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
public System.Threading.Tasks.Task<System.ClientModel.PageResult<T>> GetCurrentPageAsync() { throw null; }
protected abstract System.Threading.Tasks.Task<System.ClientModel.PageResult<T>> GetCurrentPageAsyncCore();
System.Collections.Generic.IAsyncEnumerator<System.ClientModel.PageResult<T>> System.Collections.Generic.IAsyncEnumerable<System.ClientModel.PageResult<T>>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; }
}
public abstract partial class BinaryContent : System.IDisposable
{
Expand Down Expand Up @@ -54,25 +57,37 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR
public virtual T Value { get { throw null; } }
public static implicit operator T (System.ClientModel.ClientResult<T> result) { throw null; }
}
public abstract partial class PageableCollection<T> : System.ClientModel.ResultCollection<T>
public abstract partial class CollectionResult<T> : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable
{
protected PageableCollection() { }
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.ResultPage<T>> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?));
public override System.Collections.Generic.IEnumerator<T> GetEnumerator() { throw null; }
protected internal CollectionResult() { }
protected internal CollectionResult(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IEnumerator<T> GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
}
public abstract partial class ResultCollection<T> : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable
public partial class ContinuationToken
{
protected internal ResultCollection() { }
protected internal ResultCollection(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IEnumerator<T> GetEnumerator();
protected ContinuationToken() { }
protected ContinuationToken(System.BinaryData bytes) { }
public static System.ClientModel.ContinuationToken FromBytes(System.BinaryData bytes) { throw null; }
public virtual System.BinaryData ToBytes() { throw null; }
}
public abstract partial class PageCollection<T> : System.Collections.Generic.IEnumerable<System.ClientModel.PageResult<T>>, System.Collections.IEnumerable
{
protected PageCollection() { }
public System.Collections.Generic.IEnumerable<T> GetAllValues() { throw null; }
public System.ClientModel.PageResult<T> GetCurrentPage() { throw null; }
protected abstract System.ClientModel.PageResult<T> GetCurrentPageCore();
protected abstract System.Collections.Generic.IEnumerator<System.ClientModel.PageResult<T>> GetEnumeratorCore();
System.Collections.Generic.IEnumerator<System.ClientModel.PageResult<T>> System.Collections.Generic.IEnumerable<System.ClientModel.PageResult<T>>.GetEnumerator() { throw null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
}
public partial class ResultPage<T> : System.ClientModel.ResultCollection<T>
public partial class PageResult<T> : System.ClientModel.ClientResult
{
internal ResultPage() { }
public string? ContinuationToken { get { throw null; } }
public static System.ClientModel.ResultPage<T> Create(System.Collections.Generic.IEnumerable<T> values, string? continuationToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; }
public override System.Collections.Generic.IEnumerator<T> GetEnumerator() { throw null; }
internal PageResult() { }
public System.ClientModel.ContinuationToken? NextPageToken { get { throw null; } }
public System.ClientModel.ContinuationToken PageToken { get { throw null; } }
public System.Collections.Generic.IReadOnlyList<T> Values { get { throw null; } }
public static System.ClientModel.PageResult<T> Create(System.Collections.Generic.IReadOnlyList<T> values, System.ClientModel.ContinuationToken pageToken, System.ClientModel.ContinuationToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; }
}
}
namespace System.ClientModel.Primitives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ public ApiKeyCredential(string key) { }
public static implicit operator System.ClientModel.ApiKeyCredential (string key) { throw null; }
public void Update(string key) { }
}
public abstract partial class AsyncPageableCollection<T> : System.ClientModel.AsyncResultCollection<T>
public abstract partial class AsyncCollectionResult<T> : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable<T>
{
protected AsyncPageableCollection() { }
public abstract System.Collections.Generic.IAsyncEnumerable<System.ClientModel.ResultPage<T>> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?));
public override System.Collections.Generic.IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected internal AsyncCollectionResult() { }
protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
}
public abstract partial class AsyncResultCollection<T> : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable<T>
public abstract partial class AsyncPageCollection<T> : System.Collections.Generic.IAsyncEnumerable<System.ClientModel.PageResult<T>>
{
protected internal AsyncResultCollection() { }
protected internal AsyncResultCollection(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IAsyncEnumerator<T> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
protected AsyncPageCollection() { }
public System.Collections.Generic.IAsyncEnumerable<T> GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected abstract System.Collections.Generic.IAsyncEnumerator<System.ClientModel.PageResult<T>> GetAsyncEnumeratorCore(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
public System.Threading.Tasks.Task<System.ClientModel.PageResult<T>> GetCurrentPageAsync() { throw null; }
protected abstract System.Threading.Tasks.Task<System.ClientModel.PageResult<T>> GetCurrentPageAsyncCore();
System.Collections.Generic.IAsyncEnumerator<System.ClientModel.PageResult<T>> System.Collections.Generic.IAsyncEnumerable<System.ClientModel.PageResult<T>>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; }
}
public abstract partial class BinaryContent : System.IDisposable
{
Expand Down Expand Up @@ -54,25 +57,37 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR
public virtual T Value { get { throw null; } }
public static implicit operator T (System.ClientModel.ClientResult<T> result) { throw null; }
}
public abstract partial class PageableCollection<T> : System.ClientModel.ResultCollection<T>
public abstract partial class CollectionResult<T> : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable
{
protected PageableCollection() { }
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.ResultPage<T>> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?));
public override System.Collections.Generic.IEnumerator<T> GetEnumerator() { throw null; }
protected internal CollectionResult() { }
protected internal CollectionResult(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IEnumerator<T> GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
}
public abstract partial class ResultCollection<T> : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable<T>, System.Collections.IEnumerable
public partial class ContinuationToken
{
protected internal ResultCollection() { }
protected internal ResultCollection(System.ClientModel.Primitives.PipelineResponse response) { }
public abstract System.Collections.Generic.IEnumerator<T> GetEnumerator();
protected ContinuationToken() { }
protected ContinuationToken(System.BinaryData bytes) { }
public static System.ClientModel.ContinuationToken FromBytes(System.BinaryData bytes) { throw null; }
public virtual System.BinaryData ToBytes() { throw null; }
}
public abstract partial class PageCollection<T> : System.Collections.Generic.IEnumerable<System.ClientModel.PageResult<T>>, System.Collections.IEnumerable
{
protected PageCollection() { }
public System.Collections.Generic.IEnumerable<T> GetAllValues() { throw null; }
public System.ClientModel.PageResult<T> GetCurrentPage() { throw null; }
protected abstract System.ClientModel.PageResult<T> GetCurrentPageCore();
protected abstract System.Collections.Generic.IEnumerator<System.ClientModel.PageResult<T>> GetEnumeratorCore();
System.Collections.Generic.IEnumerator<System.ClientModel.PageResult<T>> System.Collections.Generic.IEnumerable<System.ClientModel.PageResult<T>>.GetEnumerator() { throw null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
}
public partial class ResultPage<T> : System.ClientModel.ResultCollection<T>
public partial class PageResult<T> : System.ClientModel.ClientResult
{
internal ResultPage() { }
public string? ContinuationToken { get { throw null; } }
public static System.ClientModel.ResultPage<T> Create(System.Collections.Generic.IEnumerable<T> values, string? continuationToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; }
public override System.Collections.Generic.IEnumerator<T> GetEnumerator() { throw null; }
internal PageResult() { }
public System.ClientModel.ContinuationToken? NextPageToken { get { throw null; } }
public System.ClientModel.ContinuationToken PageToken { get { throw null; } }
public System.Collections.Generic.IReadOnlyList<T> Values { get { throw null; } }
public static System.ClientModel.PageResult<T> Create(System.Collections.Generic.IReadOnlyList<T> values, System.ClientModel.ContinuationToken pageToken, System.ClientModel.ContinuationToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; }
}
}
namespace System.ClientModel.Primitives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
namespace System.ClientModel;

/// <summary>
/// Represents a collection of results returned from a cloud service operation.
/// Represents a collection of values returned from a cloud service operation.
/// The collection values may be returned by one or more service responses.
/// </summary>
public abstract class AsyncResultCollection<T> : ClientResult, IAsyncEnumerable<T>
public abstract class AsyncCollectionResult<T> : ClientResult, IAsyncEnumerable<T>
{
/// <summary>
/// Create a new instance of <see cref="AsyncResultCollection{T}"/>.
/// Create a new instance of <see cref="AsyncCollectionResult{T}"/>.
/// </summary>
/// <remarks>If no <see cref="PipelineResponse"/> is provided when the
/// <see cref="ClientResult"/> instance is created, it is expected that
Expand All @@ -24,17 +25,17 @@ public abstract class AsyncResultCollection<T> : ClientResult, IAsyncEnumerable<
/// is called. Such implementations will typically be returned from client
/// convenience methods so that callers of the methods don't need to
/// dispose the return value. </remarks>
protected internal AsyncResultCollection() : base()
protected internal AsyncCollectionResult() : base()
{
}

/// <summary>
/// Create a new instance of <see cref="AsyncResultCollection{T}"/>.
/// Create a new instance of <see cref="AsyncCollectionResult{T}"/>.
/// </summary>
/// <param name="response">The <see cref="PipelineResponse"/> holding the
/// items in the collection, or the first set of the items in the collection.
/// </param>
protected internal AsyncResultCollection(PipelineResponse response) : base(response)
protected internal AsyncCollectionResult(PipelineResponse response) : base(response)
{
}

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

using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace System.ClientModel;

/// <summary>
/// An asynchronous collection of page results returned by a cloud service.
/// Cloud services use pagination to return a collection of items over multiple
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
/// responses. Each response from the service returns a page of items in the
/// collection, as well as the information needed to obtain the next page of
/// items, until all the items in the requested collection have been returned.
/// To enumerate the items in the collection, instead of the pages in the
/// collection, call <see cref="GetAllValuesAsync"/>. To get the current
/// collection page, call <see cref="GetCurrentPageAsync"/>.
/// </summary>
public abstract class AsyncPageCollection<T> : IAsyncEnumerable<PageResult<T>>
{
/// <summary>
/// Create a new instance of <see cref="AsyncPageCollection{T}"/>.
/// </summary>
protected AsyncPageCollection() : base()
{
// Note that page collections delay making a first request until either
// GetCurrentPageAsync is called or the collection returned by
// GetAllValuesAsync is enumerated, so this constructor calls the base
// class constructor that does not take a PipelineResponse.
}

/// <summary>
/// Get the current page of the collection.
/// </summary>
/// <returns>The current page in the collection.</returns>
public async Task<PageResult<T>> GetCurrentPageAsync()
=> await GetCurrentPageAsyncCore().ConfigureAwait(false);

/// <summary>
/// Get a collection of all the values in the collection requested from the
/// cloud service, rather than the pages of values.
/// </summary>
/// <returns>The values requested from the cloud service.</returns>
public async IAsyncEnumerable<T> GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
{
await foreach (PageResult<T> page in this.WithCancellation(cancellationToken).ConfigureAwait(false))
{
foreach (T value in page.Values)
{
cancellationToken.ThrowIfCancellationRequested();
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved

yield return value;
}
}
}

/// <summary>
/// Get the current page of the collection.
/// </summary>
/// <returns>The current page in the collection.</returns>
protected abstract Task<PageResult<T>> GetCurrentPageAsyncCore();

/// <summary>
/// Get an async enumerator that can enumerate the pages of values returned
/// by the cloud service.
/// </summary>
/// <returns>An async enumerator of pages holding the items in the value
/// collection.</returns>
protected abstract IAsyncEnumerator<PageResult<T>> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default);

IAsyncEnumerator<PageResult<T>> IAsyncEnumerable<PageResult<T>>.GetAsyncEnumerator(CancellationToken cancellationToken)
=> GetAsyncEnumeratorCore(cancellationToken);
}
Loading