From bb32d2e25c82a697a11d159ac709f755f2d6498f Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 25 Jun 2024 15:36:19 -0700 Subject: [PATCH 01/49] Initial checkin of changes to paging APIs --- .../api/System.ClientModel.net6.0.cs | 48 ++- .../api/System.ClientModel.netstandard2.0.cs | 48 ++- .../src/Convenience/AsyncPageCollectionOfT.cs | 80 ++++ .../Convenience/AsyncPageableCollectionOfT.cs | 62 --- ...fT.cs => AsyncResultValueCollectionOfT.cs} | 10 +- .../src/Convenience/ClientPageOfT.cs | 42 ++ .../src/Convenience/ClientResult.cs | 2 +- .../src/Convenience/PageCollectionOfT.cs | 74 ++++ .../src/Convenience/PageableCollectionOfT.cs | 57 --- .../src/Convenience/ResultPageOfT.cs | 50 --- ...tionOfT.cs => ResultValueCollectionOfT.cs} | 10 +- .../tests/Convenience/PageCollectionTests.cs | 360 ++++++++++++++++++ .../Convenience/PageableCollectionTests.cs | 271 ------------- .../TestFramework/Mocks/MockPagCollection.cs | 65 ++++ .../TestFramework/Mocks/MockPageableClient.cs | 238 ++++++------ .../TestFramework/PageableResultHelpers.cs | 146 +++---- .../SSE/ClientResultCollectionTests.cs | 12 +- .../TestFramework/Mocks/MockSseClient.cs | 4 +- .../Mocks/MockSseClientExtensions.cs | 4 +- 19 files changed, 888 insertions(+), 695 deletions(-) create mode 100644 sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs delete mode 100644 sdk/core/System.ClientModel/src/Convenience/AsyncPageableCollectionOfT.cs rename sdk/core/System.ClientModel/src/Convenience/{AsyncResultCollectionOfT.cs => AsyncResultValueCollectionOfT.cs} (78%) create mode 100644 sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs create mode 100644 sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs delete mode 100644 sdk/core/System.ClientModel/src/Convenience/PageableCollectionOfT.cs delete mode 100644 sdk/core/System.ClientModel/src/Convenience/ResultPageOfT.cs rename sdk/core/System.ClientModel/src/Convenience/{ResultCollectionOfT.cs => ResultValueCollectionOfT.cs} (79%) create mode 100644 sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs delete mode 100644 sdk/core/System.ClientModel/tests/Convenience/PageableCollectionTests.cs create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPagCollection.cs diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 53cdd65eb9a97..0ccb32466945a 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -7,16 +7,18 @@ 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 : System.ClientModel.AsyncResultCollection + public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { - protected AsyncPageableCollection() { } - public abstract System.Collections.Generic.IAsyncEnumerable> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?)); - public override System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + protected AsyncPageCollection() { } + public abstract System.BinaryData FirstPageToken { get; } + public abstract System.Threading.Tasks.Task> GetPageAsync(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Collections.Generic.IAsyncEnumerable ToValueCollectionAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } - public abstract partial class AsyncResultCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable + public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable { - protected internal AsyncResultCollection() { } - protected internal AsyncResultCollection(System.ClientModel.Primitives.PipelineResponse response) { } + protected internal AsyncResultValueCollection() { } + protected internal AsyncResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } public abstract System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } public abstract partial class BinaryContent : System.IDisposable @@ -30,6 +32,14 @@ protected BinaryContent() { } public abstract void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public abstract System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } + public partial class ClientPage : System.ClientModel.ClientResult + { + internal ClientPage() { } + public System.BinaryData? NextPageToken { get { throw null; } } + public System.BinaryData PageToken { get { throw null; } } + public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } + public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.BinaryData pageToken, System.BinaryData? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + } public partial class ClientResult { protected ClientResult() { } @@ -54,26 +64,22 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } - public abstract partial class PageableCollection : System.ClientModel.ResultCollection + public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { - protected PageableCollection() { } - public abstract System.Collections.Generic.IEnumerable> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?)); - public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + protected PageCollection() { } + public abstract System.BinaryData FirstPageToken { get; } + public abstract System.ClientModel.ClientPage GetPage(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public System.Collections.Generic.IEnumerable ToValueCollection() { throw null; } } - public abstract partial class ResultCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + public abstract partial class ResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { - protected internal ResultCollection() { } - protected internal ResultCollection(System.ClientModel.Primitives.PipelineResponse response) { } + protected internal ResultValueCollection() { } + protected internal ResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } public abstract System.Collections.Generic.IEnumerator GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } - public partial class ResultPage : System.ClientModel.ResultCollection - { - internal ResultPage() { } - public string? ContinuationToken { get { throw null; } } - public static System.ClientModel.ResultPage Create(System.Collections.Generic.IEnumerable values, string? continuationToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } - public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } - } } namespace System.ClientModel.Primitives { diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index e43b75c72c1e4..d20d2b74552fe 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -7,16 +7,18 @@ 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 : System.ClientModel.AsyncResultCollection + public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { - protected AsyncPageableCollection() { } - public abstract System.Collections.Generic.IAsyncEnumerable> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?)); - public override System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + protected AsyncPageCollection() { } + public abstract System.BinaryData FirstPageToken { get; } + public abstract System.Threading.Tasks.Task> GetPageAsync(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Collections.Generic.IAsyncEnumerable ToValueCollectionAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } - public abstract partial class AsyncResultCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable + public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable { - protected internal AsyncResultCollection() { } - protected internal AsyncResultCollection(System.ClientModel.Primitives.PipelineResponse response) { } + protected internal AsyncResultValueCollection() { } + protected internal AsyncResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } public abstract System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } public abstract partial class BinaryContent : System.IDisposable @@ -30,6 +32,14 @@ protected BinaryContent() { } public abstract void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public abstract System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } + public partial class ClientPage : System.ClientModel.ClientResult + { + internal ClientPage() { } + public System.BinaryData? NextPageToken { get { throw null; } } + public System.BinaryData PageToken { get { throw null; } } + public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } + public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.BinaryData pageToken, System.BinaryData? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + } public partial class ClientResult { protected ClientResult() { } @@ -54,26 +64,22 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } - public abstract partial class PageableCollection : System.ClientModel.ResultCollection + public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { - protected PageableCollection() { } - public abstract System.Collections.Generic.IEnumerable> AsPages(string? continuationToken = null, int? pageSizeHint = default(int?)); - public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } + protected PageCollection() { } + public abstract System.BinaryData FirstPageToken { get; } + public abstract System.ClientModel.ClientPage GetPage(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + public System.Collections.Generic.IEnumerable ToValueCollection() { throw null; } } - public abstract partial class ResultCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + public abstract partial class ResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { - protected internal ResultCollection() { } - protected internal ResultCollection(System.ClientModel.Primitives.PipelineResponse response) { } + protected internal ResultValueCollection() { } + protected internal ResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } public abstract System.Collections.Generic.IEnumerator GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } - public partial class ResultPage : System.ClientModel.ResultCollection - { - internal ResultPage() { } - public string? ContinuationToken { get { throw null; } } - public static System.ClientModel.ResultPage Create(System.Collections.Generic.IEnumerable values, string? continuationToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } - public override System.Collections.Generic.IEnumerator GetEnumerator() { throw null; } - } } namespace System.ClientModel.Primitives { diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs new file mode 100644 index 0000000000000..68a8c55bc7236 --- /dev/null +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +namespace System.ClientModel; + +#pragma warning disable CS1591 + +public abstract class AsyncPageCollection : IAsyncEnumerable> +{ + protected AsyncPageCollection() : base() + { + } + + // I like this being abstract rather than providing the field in the base + // type because it means the implementation can hold the field as a subtype + // instance in the implementation and not have to cast it. + public abstract BinaryData FirstPageToken { get; } + + public abstract Task> GetPageAsync(BinaryData pageToken, RequestOptions? options = default); + + //public AsyncResultValueCollection ToValueCollectionAsync(CancellationToken cancellationToken = default) + // => new AsyncPagedValueCollection(this); + public async IAsyncEnumerable ToValueCollectionAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) + { + await foreach (ClientPage page in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + { + foreach (T value in page.Values) + { + yield return value; + } + } + } + + async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + { + RequestOptions? options = cancellationToken == default ? + default : + new RequestOptions() { CancellationToken = cancellationToken }; + + ClientPage page = await GetPageAsync(FirstPageToken, options).ConfigureAwait(false); + yield return page; + + while (page.NextPageToken != null) + { + page = await GetPageAsync(page.NextPageToken, options).ConfigureAwait(false); + yield return page; + } + } + + //private class AsyncPagedValueCollection : AsyncResultValueCollection + //{ + // private readonly AsyncPageCollection _pages; + + // public AsyncPagedValueCollection(AsyncPageCollection pages) + // { + // _pages = pages; + // } + + // public async override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + // { + // await foreach (ClientPage page in _pages.ConfigureAwait(false).WithCancellation(cancellationToken)) + // { + // foreach (T value in page.Values) + // { + // SetRawResponse(page.GetRawResponse()); + + // yield return value; + // } + // } + // } + //} +} + +#pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageableCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageableCollectionOfT.cs deleted file mode 100644 index 1a36bab43a6b2..0000000000000 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageableCollectionOfT.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.ClientModel.Primitives; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace System.ClientModel; - -/// -/// Represents a collection of results returned from a cloud service operation -/// sequentially over one or more calls to the service. -/// -public abstract class AsyncPageableCollection : AsyncResultCollection -{ - /// - /// Create a new instance of . - /// - /// This constructor does not take a - /// because derived types are expected to defer the first service call - /// until the collection is enumerated using await foreach. - /// - protected AsyncPageableCollection() : base() - { - } - - /// - /// Return an enumerable of that aynchronously - /// enumerates the collection's pages instead of the collection's individual - /// values. This may make multiple service requests. - /// - /// A token indicating where the collection - /// of results returned from the service should begin. Passing null - /// will start the collection at the first page of values. - /// The number of items to request that the - /// service return in a , if the service supports - /// such requests. - /// An async sequence of , each holding - /// the subset of collection values contained in a given service response. - /// - public abstract IAsyncEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default); - - /// - /// Return an enumerator that iterates asynchronously through the collection - /// values. This may make multiple service requests. - /// - /// The used - /// with requests made while enumerating asynchronously. - /// An that can iterate - /// asynchronously through the collection values. - public override async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - { - await foreach (ResultPage page in AsPages().ConfigureAwait(false).WithCancellation(cancellationToken)) - { - foreach (T value in page) - { - yield return value; - } - } - } -} diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncResultCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs similarity index 78% rename from sdk/core/System.ClientModel/src/Convenience/AsyncResultCollectionOfT.cs rename to sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs index da0f411799f0e..98a7b93d0350e 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncResultCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs @@ -10,10 +10,10 @@ namespace System.ClientModel; /// /// Represents a collection of results returned from a cloud service operation. /// -public abstract class AsyncResultCollection : ClientResult, IAsyncEnumerable +public abstract class AsyncResultValueCollection : ClientResult, IAsyncEnumerable { /// - /// Create a new instance of . + /// Create a new instance of . /// /// If no is provided when the /// instance is created, it is expected that @@ -24,17 +24,17 @@ public abstract class AsyncResultCollection : 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. - protected internal AsyncResultCollection() : base() + protected internal AsyncResultValueCollection() : base() { } /// - /// Create a new instance of . + /// Create a new instance of . /// /// The holding the /// items in the collection, or the first set of the items in the collection. /// - protected internal AsyncResultCollection(PipelineResponse response) : base(response) + protected internal AsyncResultValueCollection(PipelineResponse response) : base(response) { } diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs new file mode 100644 index 0000000000000..26061538a48eb --- /dev/null +++ b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel.Primitives; +using System.Collections.Generic; + +// Lives in .Primitives because it's intended to be inherited from to create +// a service-specific instance. +namespace System.ClientModel; + +#pragma warning disable CS1591 +public class ClientPage : ClientResult +{ + private ClientPage(IReadOnlyList values, + BinaryData pageToken, + BinaryData? nextPageToken, + PipelineResponse response) : base(response) + { + Values = values; + PageToken = pageToken; + NextPageToken = nextPageToken; + } + + // The values in the page + public IReadOnlyList Values { get; } + + // The token used to retrieve this page -- can uniquely request + // the page AND uniquely rehydrate a page collection that this is + // a page in (first, page 5, whatever). + // i.e. it completely describes a collection where this is a page + // in it. + // This is useful because I can cache this and retrive both the + // full collection this page is in and/or the current page. + public BinaryData PageToken { get; } + + // If this is null, the current page is the last page in a collection. + public BinaryData? NextPageToken { get; } + + public static ClientPage Create(IReadOnlyList values, BinaryData pageToken, BinaryData? nextPageToken, PipelineResponse response) + => new(values, pageToken, nextPageToken, response); +} +#pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs b/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs index 7205c9165fdbe..551988e9c3422 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs @@ -45,7 +45,7 @@ protected ClientResult(PipelineResponse response) /// No /// value is currently available for this /// instance. This can happen when the instance - /// is a collection type like + /// is a collection type like /// that has not yet been enumerated. public PipelineResponse GetRawResponse() { diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs new file mode 100644 index 0000000000000..7d15e36e9c625 --- /dev/null +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel.Primitives; +using System.Collections; +using System.Collections.Generic; + +namespace System.ClientModel; + +#pragma warning disable CS1591 + +// This type is a client that defines a collection of elements and can +// make service requests to retrieve specific pages +public abstract class PageCollection : IEnumerable> +{ + // Note - assumes we don't make a request initially, so don't call + // response constructor + protected PageCollection() : base() + { + } + + public abstract BinaryData FirstPageToken { get; } + + public abstract ClientPage GetPage(BinaryData pageToken, RequestOptions? options = default); + + public IEnumerable ToValueCollection() + { + foreach (ClientPage page in this) + { + foreach (T value in page.Values) + { + yield return value; + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); + + IEnumerator> IEnumerable>.GetEnumerator() + { + ClientPage page = GetPage(FirstPageToken); + yield return page; + + while (page.NextPageToken != null) + { + page = GetPage(page.NextPageToken); + yield return page; + } + } + + //private class PagedValueCollection : ResultValueCollection + //{ + // private readonly PageCollection _pages; + + // public PagedValueCollection(PageCollection pages) + // { + // _pages = pages; + // } + + // public override IEnumerator GetEnumerator() + // { + // foreach (ClientPage page in _pages) + // { + // foreach (T value in page.Values) + // { + // SetRawResponse(page.GetRawResponse()); + + // yield return value; + // } + // } + // } + //} +} +#pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageableCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageableCollectionOfT.cs deleted file mode 100644 index eff5d4f5e0c8b..0000000000000 --- a/sdk/core/System.ClientModel/src/Convenience/PageableCollectionOfT.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.ClientModel.Primitives; -using System.Collections.Generic; - -namespace System.ClientModel; - -/// -/// Represents a collection of results returned from a cloud service operation -/// sequentially over one or more calls to the service. -/// -public abstract class PageableCollection : ResultCollection -{ - /// - /// Create a new instance of . - /// - /// This constructor does not take a - /// because derived types are expected to defer the first service call - /// until the collection is enumerated using foreach. - protected PageableCollection() : base() - { - } - - /// - /// Return an enumerable of that enumerates the - /// collection's pages instead of the collection's individual values. This - /// may make multiple service requests. - /// - /// A token indicating where the collection - /// of results returned from the service should begin. Passing null - /// will start the collection at the first page of values. - /// The number of items to request that the - /// service return in a , if the service supports - /// such requests. - /// A sequence of , each holding the - /// subset of collection values contained in a given service response. - /// - public abstract IEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default); - - /// - /// Return an enumerator that iterates through the collection values. This - /// may make multiple service requests. - /// - /// An that can iterate through the - /// collection values. - public override IEnumerator GetEnumerator() - { - foreach (ResultPage page in AsPages()) - { - foreach (T value in page) - { - yield return value; - } - } - } -} diff --git a/sdk/core/System.ClientModel/src/Convenience/ResultPageOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ResultPageOfT.cs deleted file mode 100644 index 9542295127ca2..0000000000000 --- a/sdk/core/System.ClientModel/src/Convenience/ResultPageOfT.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.ClientModel.Primitives; -using System.Collections.Generic; - -namespace System.ClientModel; - -/// -/// Represents the subset (or page) of results contained in a single response -/// from a cloud service returning a collection of results sequentially over -/// one or more calls to the service (i.e. a paged collection). -/// -public class ResultPage : ResultCollection -{ - private readonly IEnumerable _values; - - private ResultPage(IEnumerable values, string? continuationToken, PipelineResponse response) - : base(response) - { - _values = values; - ContinuationToken = continuationToken; - } - - /// - /// Creates a new . - /// - /// The values contained in . - /// - /// The token that can be used to request - /// the next page of results from the service, or null if this page - /// holds the final subset of values. - /// The holding the - /// collection values returned by the service. - /// An instance of holding the provided - /// values. - public static ResultPage Create(IEnumerable values, string? continuationToken, PipelineResponse response) - => new(values, continuationToken, response); - - /// - /// Gets the continuation token used to request the next - /// . May be null or empty when no values - /// remain to be returned from the collection. - /// - public string? ContinuationToken { get; } - - /// - public override IEnumerator GetEnumerator() - => _values.GetEnumerator(); -} diff --git a/sdk/core/System.ClientModel/src/Convenience/ResultCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs similarity index 79% rename from sdk/core/System.ClientModel/src/Convenience/ResultCollectionOfT.cs rename to sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs index 5943cc8438f95..3738c6fa213ff 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ResultCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs @@ -10,10 +10,10 @@ namespace System.ClientModel; /// /// Represents a collection of results returned from a cloud service operation. /// -public abstract class ResultCollection : ClientResult, IEnumerable +public abstract class ResultValueCollection : ClientResult, IEnumerable { /// - /// Create a new instance of . + /// Create a new instance of . /// /// If no is provided when the /// instance is created, it is expected that @@ -24,17 +24,17 @@ public abstract class ResultCollection : ClientResult, IEnumerable /// 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. - protected internal ResultCollection() : base() + protected internal ResultValueCollection() : base() { } /// - /// Create a new instance of . + /// Create a new instance of . /// /// The holding the /// items in the collection, or the first set of the items in the collection. /// - protected internal ResultCollection(PipelineResponse response) : base(response) + protected internal ResultValueCollection(PipelineResponse response) : base(response) { } diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs new file mode 100644 index 0000000000000..00067683c21b5 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -0,0 +1,360 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections; +using System.Collections.Generic; +using ClientModel.Tests.Mocks; +using NUnit.Framework; + +namespace System.ClientModel.Tests.Results; + +public class PageCollectionTests +{ + [Test] + public void CanEnumeratePages() + { + List values = new() { 0, 1, 2, 3 }; + int pageSize = 2; + + List mockResults = new() { + new MockClientResult(new MockPipelineResponse(0)), + new MockClientResult(new MockPipelineResponse(1)) + }; + + PageCollection pages = new MockPageCollection(values, mockResults, pageSize); + + int i = 0; + foreach (ClientPage page in pages) + { + Assert.AreEqual(i++, page.Values[0]); + Assert.AreEqual(i++, page.Values[1]); + } + + Assert.AreEqual(4, i); + } + + [Test] + public void CanEnumerateClientResults() + { + List mockResults = new() { + new MockClientResult(new MockPipelineResponse(0)), + new MockClientResult(new MockPipelineResponse(1)) + }; + + IEnumerable results = new ProtocolMockPageCollection(mockResults); + + int i = 0; + foreach (ClientResult result in results) + { + Assert.AreEqual(i++, result.GetRawResponse().Status); + } + + Assert.AreEqual(2, i); + } + + [Test] + public void CanEvolveFromProtocol() + { + List values = new() { 0, 1, 2, 3 }; + int pageSize = 2; + + List mockResults = new() { + new MockClientResult(new MockPipelineResponse(0)), + new MockClientResult(new MockPipelineResponse(1)) + }; + + // Showing that we can use the same code as protocol-only + // with a convenience return type. + IEnumerable results = new MockPageCollection(values, mockResults, pageSize); + + int i = 0; + foreach (ClientResult result in results) + { + Assert.AreEqual(i++, result.GetRawResponse().Status); + } + + Assert.AreEqual(2, i); + } + + //private static readonly string[] MockPageContents = { """ + // [ + // { "intValue" : 0, "stringValue" : "0" }, + // { "intValue" : 1, "stringValue" : "1" }, + // { "intValue" : 2, "stringValue" : "2" } + // ] + // """,""" + // [ + // { "intValue" : 3, "stringValue" : "3" }, + // { "intValue" : 4, "stringValue" : "4" }, + // { "intValue" : 5, "stringValue" : "5" } + // ] + // """,""" + // [ + // { "intValue" : 6, "stringValue" : "6" }, + // { "intValue" : 7, "stringValue" : "7" }, + // { "intValue" : 8, "stringValue" : "8" } + // ] + // """, + // }; + + //private static readonly int PageCount = MockPageContents.Length; + //private static readonly int ItemCount = 9; + + //[Test] + //public void CanEnumerateValues() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int i = 0; + // foreach (MockJsonModel model in models) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // Assert.AreEqual(ItemCount, i); + //} + + //[Test] + //public void CanEnumeratePages() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + //[Test] + //public void CanStartPageEnumerationMidwayThrough() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int pageCount = 0; + // int i = 6; + + // // Request just the last page by starting at the last seen value + // // on the prior page -- i.e. item 5. + // foreach (ResultPage page in models.AsPages(continuationToken: "5")) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, i); + // Assert.AreEqual(1, pageCount); + //} + + //[Test] + //public void CanSetPageSizeHint() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + // var pages = models.AsPages(pageSizeHint: 10); + // foreach (var _ in pages) + // { + // // page size hint is ignored in this mock + // } + + // Assert.AreEqual(10, client.RequestedPageSize); + //} + + //[Test] + //public void CanGetRawResponses() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // PipelineResponse collectionResponse = models.GetRawResponse(); + // PipelineResponse pageResponse = page.GetRawResponse(); + + // Assert.AreEqual(pageResponse, collectionResponse); + // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); + // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + //[Test] + //public async Task CanEnumerateValuesAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int i = 0; + // await foreach (MockJsonModel model in models) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // Assert.AreEqual(ItemCount, i); + //} + + //[Test] + //public async Task CanEnumeratePagesAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // await foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + //[Test] + //public async Task CanStartPageEnumerationMidwayThroughAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int pageCount = 0; + // int i = 6; + + // // Request just the last page by starting at the last seen value + // // on the prior page -- i.e. item 5. + // await foreach (ResultPage page in models.AsPages(continuationToken: "5")) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, i); + // Assert.AreEqual(1, pageCount); + //} + + //[Test] + //public async Task CanSetPageSizeHintAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + // var pages = models.AsPages(pageSizeHint: 10); + // await foreach (var _ in pages) + // { + // // page size hint is ignored in this mock + // } + + // Assert.AreEqual(10, client.RequestedPageSize); + //} + + //[Test] + //public async Task CanGetRawResponsesAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // await foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // PipelineResponse collectionResponse = models.GetRawResponse(); + // PipelineResponse pageResponse = page.GetRawResponse(); + + // Assert.AreEqual(pageResponse, collectionResponse); + // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); + // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + #region Helpers + + internal class ProtocolMockPageCollection : IEnumerable + { + private readonly List _results; + + public ProtocolMockPageCollection(List results) + { + _results = results; + } + + public IEnumerator GetEnumerator() + { + foreach (ClientResult result in _results) + { + yield return result; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + #endregion +} diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageableCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageableCollectionTests.cs deleted file mode 100644 index 48092dd8b27d8..0000000000000 --- a/sdk/core/System.ClientModel/tests/Convenience/PageableCollectionTests.cs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.ClientModel.Primitives; -using System.Threading.Tasks; -using Azure.Core.TestFramework; -using ClientModel.Tests.Mocks; -using NUnit.Framework; - -namespace System.ClientModel.Tests.Results; - -public class PageableCollectionTests -{ - private static readonly string[] MockPageContents = { """ - [ - { "intValue" : 0, "stringValue" : "0" }, - { "intValue" : 1, "stringValue" : "1" }, - { "intValue" : 2, "stringValue" : "2" } - ] - """,""" - [ - { "intValue" : 3, "stringValue" : "3" }, - { "intValue" : 4, "stringValue" : "4" }, - { "intValue" : 5, "stringValue" : "5" } - ] - """,""" - [ - { "intValue" : 6, "stringValue" : "6" }, - { "intValue" : 7, "stringValue" : "7" }, - { "intValue" : 8, "stringValue" : "8" } - ] - """, - }; - - private static readonly int PageCount = MockPageContents.Length; - private static readonly int ItemCount = 9; - - [Test] - public void CanEnumerateValues() - { - MockPageableClient client = new(); - PageableCollection models = client.GetModels(MockPageContents); - - int i = 0; - foreach (MockJsonModel model in models) - { - Assert.AreEqual(i, model.IntValue); - Assert.AreEqual(i.ToString(), model.StringValue); - - i++; - } - - Assert.AreEqual(ItemCount, i); - } - - [Test] - public void CanEnumeratePages() - { - MockPageableClient client = new(); - PageableCollection models = client.GetModels(MockPageContents); - - int pageCount = 0; - int itemCount = 0; - foreach (ResultPage page in models.AsPages()) - { - foreach (MockJsonModel model in page) - { - Assert.AreEqual(itemCount, model.IntValue); - Assert.AreEqual(itemCount.ToString(), model.StringValue); - - itemCount++; - } - - pageCount++; - } - - Assert.AreEqual(ItemCount, itemCount); - Assert.AreEqual(PageCount, pageCount); - } - - [Test] - public void CanStartPageEnumerationMidwayThrough() - { - MockPageableClient client = new(); - PageableCollection models = client.GetModels(MockPageContents); - - int pageCount = 0; - int i = 6; - - // Request just the last page by starting at the last seen value - // on the prior page -- i.e. item 5. - foreach (ResultPage page in models.AsPages(continuationToken: "5")) - { - foreach (MockJsonModel model in page) - { - Assert.AreEqual(i, model.IntValue); - Assert.AreEqual(i.ToString(), model.StringValue); - - i++; - } - - pageCount++; - } - - Assert.AreEqual(ItemCount, i); - Assert.AreEqual(1, pageCount); - } - - [Test] - public void CanSetPageSizeHint() - { - MockPageableClient client = new(); - PageableCollection models = client.GetModels(MockPageContents); - var pages = models.AsPages(pageSizeHint: 10); - foreach (var _ in pages) - { - // page size hint is ignored in this mock - } - - Assert.AreEqual(10, client.RequestedPageSize); - } - - [Test] - public void CanGetRawResponses() - { - MockPageableClient client = new(); - PageableCollection models = client.GetModels(MockPageContents); - - int pageCount = 0; - int itemCount = 0; - foreach (ResultPage page in models.AsPages()) - { - foreach (MockJsonModel model in page) - { - Assert.AreEqual(itemCount, model.IntValue); - Assert.AreEqual(itemCount.ToString(), model.StringValue); - - itemCount++; - } - - PipelineResponse collectionResponse = models.GetRawResponse(); - PipelineResponse pageResponse = page.GetRawResponse(); - - Assert.AreEqual(pageResponse, collectionResponse); - Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); - Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); - - pageCount++; - } - - Assert.AreEqual(ItemCount, itemCount); - Assert.AreEqual(PageCount, pageCount); - } - - [Test] - public async Task CanEnumerateValuesAsync() - { - MockPageableClient client = new(); - AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - int i = 0; - await foreach (MockJsonModel model in models) - { - Assert.AreEqual(i, model.IntValue); - Assert.AreEqual(i.ToString(), model.StringValue); - - i++; - } - - Assert.AreEqual(ItemCount, i); - } - - [Test] - public async Task CanEnumeratePagesAsync() - { - MockPageableClient client = new(); - AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - int pageCount = 0; - int itemCount = 0; - await foreach (ResultPage page in models.AsPages()) - { - foreach (MockJsonModel model in page) - { - Assert.AreEqual(itemCount, model.IntValue); - Assert.AreEqual(itemCount.ToString(), model.StringValue); - - itemCount++; - } - - pageCount++; - } - - Assert.AreEqual(ItemCount, itemCount); - Assert.AreEqual(PageCount, pageCount); - } - - [Test] - public async Task CanStartPageEnumerationMidwayThroughAsync() - { - MockPageableClient client = new(); - AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - int pageCount = 0; - int i = 6; - - // Request just the last page by starting at the last seen value - // on the prior page -- i.e. item 5. - await foreach (ResultPage page in models.AsPages(continuationToken: "5")) - { - foreach (MockJsonModel model in page) - { - Assert.AreEqual(i, model.IntValue); - Assert.AreEqual(i.ToString(), model.StringValue); - - i++; - } - - pageCount++; - } - - Assert.AreEqual(ItemCount, i); - Assert.AreEqual(1, pageCount); - } - - [Test] - public async Task CanSetPageSizeHintAsync() - { - MockPageableClient client = new(); - AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - var pages = models.AsPages(pageSizeHint: 10); - await foreach (var _ in pages) - { - // page size hint is ignored in this mock - } - - Assert.AreEqual(10, client.RequestedPageSize); - } - - [Test] - public async Task CanGetRawResponsesAsync() - { - MockPageableClient client = new(); - AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - int pageCount = 0; - int itemCount = 0; - await foreach (ResultPage page in models.AsPages()) - { - foreach (MockJsonModel model in page) - { - Assert.AreEqual(itemCount, model.IntValue); - Assert.AreEqual(itemCount.ToString(), model.StringValue); - - itemCount++; - } - - PipelineResponse collectionResponse = models.GetRawResponse(); - PipelineResponse pageResponse = page.GetRawResponse(); - - Assert.AreEqual(pageResponse, collectionResponse); - Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); - Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); - - pageCount++; - } - - Assert.AreEqual(ItemCount, itemCount); - Assert.AreEqual(PageCount, pageCount); - } -} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPagCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPagCollection.cs new file mode 100644 index 0000000000000..b48be05997171 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPagCollection.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Diagnostics; + +namespace ClientModel.Tests.Mocks; + +public class MockPageCollection : PageCollection +{ + private readonly List _values; + private readonly int _pageSize; + private readonly List _results; + + private int _current; + private int _currentPage; + + public MockPageCollection(List values, List results, int pageSize) + { + Debug.Assert(results.Count % pageSize == 0); + Debug.Assert(values.Count / pageSize == results.Count); + + _values = values; + _results = results; + _pageSize = pageSize; + _current = 0; + } + + public override BinaryData FirstPageToken + => BinaryData.FromString(string.Empty); + + public override ClientPage GetPage(BinaryData pageToken, RequestOptions? options = null) + { + int currPageSize = Math.Min(_pageSize, _values.Count - _current); + + List values = _values.GetRange(_current, currPageSize); + _current += _pageSize; + + BinaryData? nextPageToken = (_current >= _values.Count) ? + null : + new MockPageToken(_current).ToBytes(); + + PipelineResponse response = _results[_currentPage++].GetRawResponse(); + + return ClientPage.Create(values, pageToken, nextPageToken, response); + } + + private class MockPageToken + { + public MockPageToken(int index) + { + Index = index; + } + + public int Index { get; } + + public BinaryData ToBytes() => BinaryData.FromString($"{Index}"); + + public static MockPageToken FromBytes(BinaryData data) + => new(data.ToMemory().Length == 0 ? 0 : int.Parse(data.ToString())); + } +} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs index 0ad116f70c945..9bb7a6008aedb 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs @@ -12,122 +12,122 @@ namespace ClientModel.Tests.Mocks; -public class MockPageableClient -{ - public bool ProtocolMethodCalled { get; private set; } - public int? RequestedPageSize { get; private set; } - - // mock convenience method - async - public virtual AsyncPageableCollection GetModelsAsync(string[] pageContents) - { - PipelineResponse? lastResponse = default; - - // The contract for this pageable implementation is that the last seen - // value id (where the id is StringValue) provides the continuation token - // for the page. - - int pageNumber = 0; - JsonModelList values = new(); - - async Task> firstPageFuncAsync(int? pageSize) - { - ClientResult result = await GetModelsAsync(pageContents[pageNumber++], options: null).ConfigureAwait(false); - lastResponse = result.GetRawResponse(); - values = ModelReaderWriter.Read>(lastResponse.Content)!; - string? continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; - return ResultPage.Create(values, continuationToken, lastResponse); - } - - async Task> nextPageFuncAsync(string? continuationToken, int? pageSize) - { - RequestedPageSize = pageSize; - - bool atRequestedPage = values.Count > 0 && values.Last().StringValue == continuationToken; - while (!atRequestedPage && pageNumber < pageContents.Length) - { - BinaryData content = BinaryData.FromString(pageContents[pageNumber++]); - JsonModelList pageValues = ModelReaderWriter.Read>(content)!; - atRequestedPage = pageValues[pageValues.Count - 1].StringValue == continuationToken; - } - - Debug.Assert(atRequestedPage is true); - - ClientResult result = await GetModelsAsync(pageContents[pageNumber++], options: null).ConfigureAwait(false); - lastResponse = result.GetRawResponse(); - values = ModelReaderWriter.Read>(lastResponse.Content)!; - continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; - return ResultPage.Create(values, continuationToken, lastResponse); - } - - return PageableResultHelpers.Create(firstPageFuncAsync, nextPageFuncAsync); - } - - // mock convenience method - sync - public virtual PageableCollection GetModels(string[] pageContents) - { - PipelineResponse? lastResponse = default; - - // The contract for this pageable implementation is that the last seen - // value id (where the id is StringValue) provides the continuation token - // for the page. - - int pageNumber = 0; - JsonModelList values = new(); - - ResultPage firstPageFunc(int? pageSize) - { - ClientResult result = GetModels(pageContents[pageNumber++], options: null); - lastResponse = result.GetRawResponse(); - values = ModelReaderWriter.Read>(lastResponse.Content)!; - string? continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; - return ResultPage.Create(values, continuationToken, lastResponse); - } - - ResultPage nextPageFunc(string? continuationToken, int? pageSize) - { - RequestedPageSize = pageSize; - - bool atRequestedPage = values.Count > 0 && values.Last().StringValue == continuationToken; - while (!atRequestedPage && pageNumber < pageContents.Length) - { - BinaryData content = BinaryData.FromString(pageContents[pageNumber++]); - JsonModelList pageValues = ModelReaderWriter.Read>(content)!; - atRequestedPage = pageValues[pageValues.Count - 1].StringValue == continuationToken; - } - - Debug.Assert(atRequestedPage is true); - - ClientResult result = GetModels(pageContents[pageNumber++], options: null); - lastResponse = result.GetRawResponse(); - values = ModelReaderWriter.Read>(lastResponse.Content)!; - continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; - return ResultPage.Create(values, continuationToken, lastResponse); - } - - return PageableResultHelpers.Create(firstPageFunc, nextPageFunc); - } - - // mock protocol method - async - public virtual async Task GetModelsAsync(string pageContent, RequestOptions? options = default) - { - await Task.Delay(0); - - MockPipelineResponse response = new(200); - response.SetContent(pageContent); - - ProtocolMethodCalled = true; - - return ClientResult.FromResponse(response); - } - - // mock protocol method - sync - public virtual ClientResult GetModels(string pageContent, RequestOptions? options = default) - { - MockPipelineResponse response = new(200); - response.SetContent(pageContent); - - ProtocolMethodCalled = true; - - return ClientResult.FromResponse(response); - } -} +//public class MockPageableClient +//{ +// public bool ProtocolMethodCalled { get; private set; } +// public int? RequestedPageSize { get; private set; } + +// // mock convenience method - async +// public virtual AsyncPageableCollection GetModelsAsync(string[] pageContents) +// { +// PipelineResponse? lastResponse = default; + +// // The contract for this pageable implementation is that the last seen +// // value id (where the id is StringValue) provides the continuation token +// // for the page. + +// int pageNumber = 0; +// JsonModelList values = new(); + +// async Task> firstPageFuncAsync(int? pageSize) +// { +// ClientResult result = await GetModelsAsync(pageContents[pageNumber++], options: null).ConfigureAwait(false); +// lastResponse = result.GetRawResponse(); +// values = ModelReaderWriter.Read>(lastResponse.Content)!; +// string? continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; +// return ResultPage.Create(values, continuationToken, lastResponse); +// } + +// async Task> nextPageFuncAsync(string? continuationToken, int? pageSize) +// { +// RequestedPageSize = pageSize; + +// bool atRequestedPage = values.Count > 0 && values.Last().StringValue == continuationToken; +// while (!atRequestedPage && pageNumber < pageContents.Length) +// { +// BinaryData content = BinaryData.FromString(pageContents[pageNumber++]); +// JsonModelList pageValues = ModelReaderWriter.Read>(content)!; +// atRequestedPage = pageValues[pageValues.Count - 1].StringValue == continuationToken; +// } + +// Debug.Assert(atRequestedPage is true); + +// ClientResult result = await GetModelsAsync(pageContents[pageNumber++], options: null).ConfigureAwait(false); +// lastResponse = result.GetRawResponse(); +// values = ModelReaderWriter.Read>(lastResponse.Content)!; +// continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; +// return ResultPage.Create(values, continuationToken, lastResponse); +// } + +// return PageableResultHelpers.Create(firstPageFuncAsync, nextPageFuncAsync); +// } + +// // mock convenience method - sync +// public virtual PageableCollection GetModels(string[] pageContents) +// { +// PipelineResponse? lastResponse = default; + +// // The contract for this pageable implementation is that the last seen +// // value id (where the id is StringValue) provides the continuation token +// // for the page. + +// int pageNumber = 0; +// JsonModelList values = new(); + +// ResultPage firstPageFunc(int? pageSize) +// { +// ClientResult result = GetModels(pageContents[pageNumber++], options: null); +// lastResponse = result.GetRawResponse(); +// values = ModelReaderWriter.Read>(lastResponse.Content)!; +// string? continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; +// return ResultPage.Create(values, continuationToken, lastResponse); +// } + +// ResultPage nextPageFunc(string? continuationToken, int? pageSize) +// { +// RequestedPageSize = pageSize; + +// bool atRequestedPage = values.Count > 0 && values.Last().StringValue == continuationToken; +// while (!atRequestedPage && pageNumber < pageContents.Length) +// { +// BinaryData content = BinaryData.FromString(pageContents[pageNumber++]); +// JsonModelList pageValues = ModelReaderWriter.Read>(content)!; +// atRequestedPage = pageValues[pageValues.Count - 1].StringValue == continuationToken; +// } + +// Debug.Assert(atRequestedPage is true); + +// ClientResult result = GetModels(pageContents[pageNumber++], options: null); +// lastResponse = result.GetRawResponse(); +// values = ModelReaderWriter.Read>(lastResponse.Content)!; +// continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; +// return ResultPage.Create(values, continuationToken, lastResponse); +// } + +// return PageableResultHelpers.Create(firstPageFunc, nextPageFunc); +// } + +// // mock protocol method - async +// public virtual async Task GetModelsAsync(string pageContent, RequestOptions? options = default) +// { +// await Task.Delay(0); + +// MockPipelineResponse response = new(200); +// response.SetContent(pageContent); + +// ProtocolMethodCalled = true; + +// return ClientResult.FromResponse(response); +// } + +// // mock protocol method - sync +// public virtual ClientResult GetModels(string pageContent, RequestOptions? options = default) +// { +// MockPipelineResponse response = new(200); +// response.SetContent(pageContent); + +// ProtocolMethodCalled = true; + +// return ClientResult.FromResponse(response); +// } +//} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs b/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs index 6013ede13cdc9..0dc4b82be00de 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs @@ -8,87 +8,87 @@ namespace ClientModel.Tests.Internal; -internal class PageableResultHelpers -{ - public static PageableCollection Create(Func> firstPageFunc, Func>? nextPageFunc, int? pageSize = default) where T : notnull - { - ResultPage first(string? _, int? pageSizeHint) => firstPageFunc(pageSizeHint); - return new FuncPageable(first, nextPageFunc, pageSize); - } +//internal class PageableResultHelpers +//{ +// public static PageableCollection Create(Func> firstPageFunc, Func>? nextPageFunc, int? pageSize = default) where T : notnull +// { +// ResultPage first(string? _, int? pageSizeHint) => firstPageFunc(pageSizeHint); +// return new FuncPageable(first, nextPageFunc, pageSize); +// } - public static AsyncPageableCollection Create(Func>> firstPageFunc, Func>>? nextPageFunc, int? pageSize = default) where T : notnull - { - Task> first(string? _, int? pageSizeHint) => firstPageFunc(pageSizeHint); - return new FuncAsyncPageable(first, nextPageFunc, pageSize); - } +// public static AsyncPageableCollection Create(Func>> firstPageFunc, Func>>? nextPageFunc, int? pageSize = default) where T : notnull +// { +// Task> first(string? _, int? pageSizeHint) => firstPageFunc(pageSizeHint); +// return new FuncAsyncPageable(first, nextPageFunc, pageSize); +// } - private class FuncAsyncPageable : AsyncPageableCollection where T : notnull - { - private readonly Func>> _firstPageFunc; - private readonly Func>>? _nextPageFunc; - private readonly int? _defaultPageSize; +// private class FuncAsyncPageable : AsyncPageableCollection where T : notnull +// { +// private readonly Func>> _firstPageFunc; +// private readonly Func>>? _nextPageFunc; +// private readonly int? _defaultPageSize; - public FuncAsyncPageable(Func>> firstPageFunc, Func>>? nextPageFunc, int? defaultPageSize = default) - { - _firstPageFunc = firstPageFunc; - _nextPageFunc = nextPageFunc; - _defaultPageSize = defaultPageSize; - } +// public FuncAsyncPageable(Func>> firstPageFunc, Func>>? nextPageFunc, int? defaultPageSize = default) +// { +// _firstPageFunc = firstPageFunc; +// _nextPageFunc = nextPageFunc; +// _defaultPageSize = defaultPageSize; +// } - public override async IAsyncEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default) - { - Func>>? pageFunc = string.IsNullOrEmpty(continuationToken) ? _firstPageFunc : _nextPageFunc; +// public override async IAsyncEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default) +// { +// Func>>? pageFunc = string.IsNullOrEmpty(continuationToken) ? _firstPageFunc : _nextPageFunc; - if (pageFunc == null) - { - yield break; - } +// if (pageFunc == null) +// { +// yield break; +// } - int? pageSize = pageSizeHint ?? _defaultPageSize; - do - { - ResultPage page = await pageFunc(continuationToken, pageSize).ConfigureAwait(false); - SetRawResponse(page.GetRawResponse()); - yield return page; - continuationToken = page.ContinuationToken; - pageFunc = _nextPageFunc; - } - while (!string.IsNullOrEmpty(continuationToken) && pageFunc != null); - } - } +// int? pageSize = pageSizeHint ?? _defaultPageSize; +// do +// { +// ResultPage page = await pageFunc(continuationToken, pageSize).ConfigureAwait(false); +// SetRawResponse(page.GetRawResponse()); +// yield return page; +// continuationToken = page.ContinuationToken; +// pageFunc = _nextPageFunc; +// } +// while (!string.IsNullOrEmpty(continuationToken) && pageFunc != null); +// } +// } - private class FuncPageable : PageableCollection where T : notnull - { - private readonly Func> _firstPageFunc; - private readonly Func>? _nextPageFunc; - private readonly int? _defaultPageSize; +// private class FuncPageable : PageableCollection where T : notnull +// { +// private readonly Func> _firstPageFunc; +// private readonly Func>? _nextPageFunc; +// private readonly int? _defaultPageSize; - public FuncPageable(Func> firstPageFunc, Func>? nextPageFunc, int? defaultPageSize = default) - { - _firstPageFunc = firstPageFunc; - _nextPageFunc = nextPageFunc; - _defaultPageSize = defaultPageSize; - } +// public FuncPageable(Func> firstPageFunc, Func>? nextPageFunc, int? defaultPageSize = default) +// { +// _firstPageFunc = firstPageFunc; +// _nextPageFunc = nextPageFunc; +// _defaultPageSize = defaultPageSize; +// } - public override IEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default) - { - Func>? pageFunc = string.IsNullOrEmpty(continuationToken) ? _firstPageFunc : _nextPageFunc; +// public override IEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default) +// { +// Func>? pageFunc = string.IsNullOrEmpty(continuationToken) ? _firstPageFunc : _nextPageFunc; - if (pageFunc == null) - { - yield break; - } +// if (pageFunc == null) +// { +// yield break; +// } - int? pageSize = pageSizeHint ?? _defaultPageSize; - do - { - ResultPage page = pageFunc(continuationToken, pageSize); - SetRawResponse(page.GetRawResponse()); - yield return page; - continuationToken = page.ContinuationToken; - pageFunc = _nextPageFunc; - } - while (!string.IsNullOrEmpty(continuationToken) && pageFunc != null); - } - } -} +// int? pageSize = pageSizeHint ?? _defaultPageSize; +// do +// { +// ResultPage page = pageFunc(continuationToken, pageSize); +// SetRawResponse(page.GetRawResponse()); +// yield return page; +// continuationToken = page.ContinuationToken; +// pageFunc = _nextPageFunc; +// } +// while (!string.IsNullOrEmpty(continuationToken) && pageFunc != null); +// } +// } +//} diff --git a/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs b/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs index 8e2fb8d3095e3..a7a2296cbfe16 100644 --- a/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs @@ -21,7 +21,7 @@ public ClientResultCollectionTests(bool isAsync) : base(isAsync) public async Task EnumeratesModelValues() { MockSseClient client = new(); - AsyncResultCollection models = client.GetModelsStreamingAsync(); + AsyncResultValueCollection models = client.GetModelsStreamingAsync(); int i = 0; await foreach (MockJsonModel model in models) @@ -39,7 +39,7 @@ public async Task EnumeratesModelValues() public async Task ModelCollectionDelaysSendingRequest() { MockSseClient client = new(); - AsyncResultCollection models = client.GetModelsStreamingAsync(); + AsyncResultValueCollection models = client.GetModelsStreamingAsync(); Assert.IsFalse(client.ProtocolMethodCalled); @@ -60,7 +60,7 @@ public async Task ModelCollectionDelaysSendingRequest() public void ModelCollectionThrowsIfCancelled() { MockSseClient client = new(); - AsyncResultCollection models = client.GetModelsStreamingAsync(); + AsyncResultValueCollection models = client.GetModelsStreamingAsync(); // Set it to `cancelled: true` to validate functionality. CancellationToken token = new(true); @@ -77,7 +77,7 @@ public void ModelCollectionThrowsIfCancelled() public async Task ModelCollectionDisposesStream() { MockSseClient client = new(); - AsyncResultCollection models = client.GetModelsStreamingAsync(); + AsyncResultValueCollection models = client.GetModelsStreamingAsync(); await foreach (MockJsonModel model in models) { @@ -91,7 +91,7 @@ public async Task ModelCollectionDisposesStream() public void ModelCollectionGetRawResponseThrowsBeforeEnumerated() { MockSseClient client = new(); - AsyncResultCollection models = client.GetModelsStreamingAsync(); + AsyncResultValueCollection models = client.GetModelsStreamingAsync(); Assert.Throws(() => { PipelineResponse response = models.GetRawResponse(); }); } @@ -99,7 +99,7 @@ public void ModelCollectionGetRawResponseThrowsBeforeEnumerated() public async Task StopsOnStringBasedTerminalEvent() { MockSseClient client = new(); - AsyncResultCollection models = client.GetModelsStreamingAsync("[DONE]"); + AsyncResultValueCollection models = client.GetModelsStreamingAsync("[DONE]"); bool empty = true; await foreach (MockJsonModel model in models) diff --git a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs index 2113d2acb0474..53fd7510c9d82 100644 --- a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs +++ b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs @@ -41,7 +41,7 @@ public class MockSseClient public bool ProtocolMethodCalled { get; private set; } // mock convenience method - public virtual AsyncResultCollection GetModelsStreamingAsync(string content = DefaultMockContent) + public virtual AsyncResultValueCollection GetModelsStreamingAsync(string content = DefaultMockContent) { return new AsyncMockJsonModelCollection(content, GetModelsStreamingAsync); } @@ -63,7 +63,7 @@ public virtual ClientResult GetModelsStreamingAsync(string content, RequestOptio // Internal client implementation of convenience-layer AsyncResultCollection. // This currently layers over an internal AsyncResultCollection // representing the event.data values, but does not strictly have to. - private class AsyncMockJsonModelCollection : AsyncResultCollection + private class AsyncMockJsonModelCollection : AsyncResultValueCollection { private readonly string _content; private readonly Func _protocolMethod; diff --git a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs index 00ebadbe6e60f..ac4702d9dc1ca 100644 --- a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs +++ b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs @@ -15,7 +15,7 @@ namespace ClientModel.Tests.Internal.Mocks; public static class MockSseClientExtensions { - public static AsyncResultCollection EnumerateDataEvents(this PipelineResponse response) + public static AsyncResultValueCollection EnumerateDataEvents(this PipelineResponse response) { if (response.ContentStream is null) { @@ -25,7 +25,7 @@ public static AsyncResultCollection EnumerateDataEvents(this Pipelin return new AsyncSseDataEventCollection(response, "[DONE]"); } - private class AsyncSseDataEventCollection : AsyncResultCollection + private class AsyncSseDataEventCollection : AsyncResultValueCollection { private readonly string _terminalData; From d6913bd9313eafd3293b80179863f6b54bea64fb Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 25 Jun 2024 15:40:02 -0700 Subject: [PATCH 02/49] nits --- .../src/Convenience/AsyncResultValueCollectionOfT.cs | 3 ++- .../src/Convenience/ResultValueCollectionOfT.cs | 3 ++- .../Mocks/{MockPagCollection.cs => MockPageCollection.cs} | 0 3 files changed, 4 insertions(+), 2 deletions(-) rename sdk/core/System.ClientModel/tests/TestFramework/Mocks/{MockPagCollection.cs => MockPageCollection.cs} (100%) diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs index 98a7b93d0350e..43914461e8762 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs @@ -8,7 +8,8 @@ namespace System.ClientModel; /// -/// 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. /// public abstract class AsyncResultValueCollection : ClientResult, IAsyncEnumerable { diff --git a/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs index 3738c6fa213ff..cd33af874d22f 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs @@ -8,7 +8,8 @@ namespace System.ClientModel; /// -/// 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. /// public abstract class ResultValueCollection : ClientResult, IEnumerable { diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPagCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs similarity index 100% rename from sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPagCollection.cs rename to sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs From 271540a8cc9b37874e890a39e621e7ced25e6f62 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 25 Jun 2024 16:56:54 -0700 Subject: [PATCH 03/49] rename ToValueCollection method to GetAllValues --- .../api/System.ClientModel.net6.0.cs | 4 +-- .../api/System.ClientModel.netstandard2.0.cs | 4 +-- .../src/Convenience/AsyncPageCollectionOfT.cs | 27 +------------------ .../src/Convenience/PageCollectionOfT.cs | 26 ++---------------- 4 files changed, 7 insertions(+), 54 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 0ccb32466945a..cd16a6ef9cdf0 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -11,9 +11,9 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi { protected AsyncPageCollection() { } public abstract System.BinaryData FirstPageToken { get; } + public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsync(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } - public System.Collections.Generic.IAsyncEnumerable ToValueCollectionAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable { @@ -68,10 +68,10 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn { protected PageCollection() { } public abstract System.BinaryData FirstPageToken { get; } + public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public abstract System.ClientModel.ClientPage GetPage(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - public System.Collections.Generic.IEnumerable ToValueCollection() { throw null; } } public abstract partial class ResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index d20d2b74552fe..3a8c21f15ab2f 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -11,9 +11,9 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi { protected AsyncPageCollection() { } public abstract System.BinaryData FirstPageToken { get; } + public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsync(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } - public System.Collections.Generic.IAsyncEnumerable ToValueCollectionAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable { @@ -68,10 +68,10 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn { protected PageCollection() { } public abstract System.BinaryData FirstPageToken { get; } + public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public abstract System.ClientModel.ClientPage GetPage(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } - public System.Collections.Generic.IEnumerable ToValueCollection() { throw null; } } public abstract partial class ResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 68a8c55bc7236..d2f050b5958b7 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -24,9 +24,7 @@ protected AsyncPageCollection() : base() public abstract Task> GetPageAsync(BinaryData pageToken, RequestOptions? options = default); - //public AsyncResultValueCollection ToValueCollectionAsync(CancellationToken cancellationToken = default) - // => new AsyncPagedValueCollection(this); - public async IAsyncEnumerable ToValueCollectionAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) + public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { await foreach (ClientPage page in this.ConfigureAwait(false).WithCancellation(cancellationToken)) { @@ -52,29 +50,6 @@ async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEn yield return page; } } - - //private class AsyncPagedValueCollection : AsyncResultValueCollection - //{ - // private readonly AsyncPageCollection _pages; - - // public AsyncPagedValueCollection(AsyncPageCollection pages) - // { - // _pages = pages; - // } - - // public async override IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - // { - // await foreach (ClientPage page in _pages.ConfigureAwait(false).WithCancellation(cancellationToken)) - // { - // foreach (T value in page.Values) - // { - // SetRawResponse(page.GetRawResponse()); - - // yield return value; - // } - // } - // } - //} } #pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 7d15e36e9c625..f70c8e3ac3e1f 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -23,7 +23,7 @@ protected PageCollection() : base() public abstract ClientPage GetPage(BinaryData pageToken, RequestOptions? options = default); - public IEnumerable ToValueCollection() + public IEnumerable GetAllValues() { foreach (ClientPage page in this) { @@ -47,28 +47,6 @@ IEnumerator> IEnumerable>.GetEnumerator() yield return page; } } - - //private class PagedValueCollection : ResultValueCollection - //{ - // private readonly PageCollection _pages; - - // public PagedValueCollection(PageCollection pages) - // { - // _pages = pages; - // } - - // public override IEnumerator GetEnumerator() - // { - // foreach (ClientPage page in _pages) - // { - // foreach (T value in page.Values) - // { - // SetRawResponse(page.GetRawResponse()); - - // yield return value; - // } - // } - // } - //} } + #pragma warning restore CS1591 From 50a82ac29c28d138cb99e9779870c25c7c1e7d5a Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 25 Jun 2024 17:23:06 -0700 Subject: [PATCH 04/49] nits --- sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs index 26061538a48eb..cd399264be377 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs @@ -4,11 +4,10 @@ using System.ClientModel.Primitives; using System.Collections.Generic; -// Lives in .Primitives because it's intended to be inherited from to create -// a service-specific instance. namespace System.ClientModel; #pragma warning disable CS1591 + public class ClientPage : ClientResult { private ClientPage(IReadOnlyList values, @@ -39,4 +38,5 @@ private ClientPage(IReadOnlyList values, public static ClientPage Create(IReadOnlyList values, BinaryData pageToken, BinaryData? nextPageToken, PipelineResponse response) => new(values, pageToken, nextPageToken, response); } + #pragma warning restore CS1591 From c16ebe072f6c43683095dfa8e63a7fa5deafc00e Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 09:59:08 -0700 Subject: [PATCH 05/49] Add back simplified PageToken; otherwise implementations must always deserialize before using --- .../api/System.ClientModel.net6.0.cs | 20 +++++++++++------ .../api/System.ClientModel.netstandard2.0.cs | 20 +++++++++++------ .../src/Convenience/AsyncPageCollectionOfT.cs | 4 ++-- .../src/Convenience/ClientPageOfT.cs | 10 ++++----- .../src/Convenience/PageCollectionOfT.cs | 4 ++-- .../src/Convenience/PageToken.cs | 22 +++++++++++++++++++ 6 files changed, 57 insertions(+), 23 deletions(-) create mode 100644 sdk/core/System.ClientModel/src/Convenience/PageToken.cs diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index cd16a6ef9cdf0..a0bfd9767026c 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -10,9 +10,9 @@ public void Update(string key) { } public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.BinaryData FirstPageToken { get; } + public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable @@ -35,10 +35,10 @@ protected BinaryContent() { } public partial class ClientPage : System.ClientModel.ClientResult { internal ClientPage() { } - public System.BinaryData? NextPageToken { get { throw null; } } - public System.BinaryData PageToken { get { throw null; } } + public System.ClientModel.Primitives.PageToken? NextPageToken { get { throw null; } } + public System.ClientModel.Primitives.PageToken PageToken { get { throw null; } } public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.BinaryData pageToken, System.BinaryData? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.PageToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } public partial class ClientResult { @@ -67,9 +67,9 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.BinaryData FirstPageToken { get; } + public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.ClientPage GetPage(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } @@ -172,6 +172,12 @@ public ModelReaderWriterOptions(string format) { } public static System.ClientModel.Primitives.ModelReaderWriterOptions Json { get { throw null; } } public static System.ClientModel.Primitives.ModelReaderWriterOptions Xml { get { throw null; } } } + public abstract partial class PageToken + { + protected PageToken() { } + protected abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } + public abstract System.BinaryData ToBytes(); + } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class PersistableModelProxyAttribute : System.Attribute { diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 3a8c21f15ab2f..e8fab5aae390e 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -10,9 +10,9 @@ public void Update(string key) { } public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.BinaryData FirstPageToken { get; } + public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable @@ -35,10 +35,10 @@ protected BinaryContent() { } public partial class ClientPage : System.ClientModel.ClientResult { internal ClientPage() { } - public System.BinaryData? NextPageToken { get { throw null; } } - public System.BinaryData PageToken { get { throw null; } } + public System.ClientModel.Primitives.PageToken? NextPageToken { get { throw null; } } + public System.ClientModel.Primitives.PageToken PageToken { get { throw null; } } public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.BinaryData pageToken, System.BinaryData? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.PageToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } public partial class ClientResult { @@ -67,9 +67,9 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.BinaryData FirstPageToken { get; } + public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.ClientPage GetPage(System.BinaryData pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } @@ -172,6 +172,12 @@ public ModelReaderWriterOptions(string format) { } public static System.ClientModel.Primitives.ModelReaderWriterOptions Json { get { throw null; } } public static System.ClientModel.Primitives.ModelReaderWriterOptions Xml { get { throw null; } } } + public abstract partial class PageToken + { + protected PageToken() { } + protected abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } + public abstract System.BinaryData ToBytes(); + } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class PersistableModelProxyAttribute : System.Attribute { diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index d2f050b5958b7..6bc992a95631a 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -20,9 +20,9 @@ protected AsyncPageCollection() : base() // I like this being abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - public abstract BinaryData FirstPageToken { get; } + public abstract PageToken FirstPageToken { get; } - public abstract Task> GetPageAsync(BinaryData pageToken, RequestOptions? options = default); + public abstract Task> GetPageAsync(PageToken pageToken, RequestOptions? options = default); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs index cd399264be377..9969f1f895e67 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs @@ -11,8 +11,8 @@ namespace System.ClientModel; public class ClientPage : ClientResult { private ClientPage(IReadOnlyList values, - BinaryData pageToken, - BinaryData? nextPageToken, + PageToken pageToken, + PageToken? nextPageToken, PipelineResponse response) : base(response) { Values = values; @@ -30,12 +30,12 @@ private ClientPage(IReadOnlyList values, // in it. // This is useful because I can cache this and retrive both the // full collection this page is in and/or the current page. - public BinaryData PageToken { get; } + public PageToken PageToken { get; } // If this is null, the current page is the last page in a collection. - public BinaryData? NextPageToken { get; } + public PageToken? NextPageToken { get; } - public static ClientPage Create(IReadOnlyList values, BinaryData pageToken, BinaryData? nextPageToken, PipelineResponse response) + public static ClientPage Create(IReadOnlyList values, PageToken pageToken, PageToken? nextPageToken, PipelineResponse response) => new(values, pageToken, nextPageToken, response); } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index f70c8e3ac3e1f..d355b3241005a 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -19,9 +19,9 @@ protected PageCollection() : base() { } - public abstract BinaryData FirstPageToken { get; } + public abstract PageToken FirstPageToken { get; } - public abstract ClientPage GetPage(BinaryData pageToken, RequestOptions? options = default); + public abstract ClientPage GetPage(PageToken pageToken, RequestOptions? options = default); public IEnumerable GetAllValues() { diff --git a/sdk/core/System.ClientModel/src/Convenience/PageToken.cs b/sdk/core/System.ClientModel/src/Convenience/PageToken.cs new file mode 100644 index 0000000000000..d1b0637050496 --- /dev/null +++ b/sdk/core/System.ClientModel/src/Convenience/PageToken.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.ClientModel.Primitives; + +#pragma warning disable CS1591 + +public abstract class PageToken +{ + protected PageToken() { } + + protected abstract PageToken FirstPageToken { get; } + + // Does it need to be public? + public abstract BinaryData ToBytes(); +} + +#pragma warning restore CS1591 From 48cadd9f42039d8a1e227eb011a060308beda892 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 10:24:33 -0700 Subject: [PATCH 06/49] rename to ClientToken for general use with LRO rehydration as well --- .../api/System.ClientModel.net6.0.cs | 25 +++++++++---------- .../api/System.ClientModel.netstandard2.0.cs | 25 +++++++++---------- .../src/Convenience/AsyncPageCollectionOfT.cs | 4 +-- .../src/Convenience/ClientPageOfT.cs | 10 ++++---- .../{PageToken.cs => ClientToken.cs} | 9 ++++--- .../src/Convenience/PageCollectionOfT.cs | 4 +-- 6 files changed, 38 insertions(+), 39 deletions(-) rename sdk/core/System.ClientModel/src/Convenience/{PageToken.cs => ClientToken.cs} (62%) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index a0bfd9767026c..1990e6b4d1fcd 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -10,9 +10,9 @@ public void Update(string key) { } public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } + public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable @@ -35,10 +35,10 @@ protected BinaryContent() { } public partial class ClientPage : System.ClientModel.ClientResult { internal ClientPage() { } - public System.ClientModel.Primitives.PageToken? NextPageToken { get { throw null; } } - public System.ClientModel.Primitives.PageToken PageToken { get { throw null; } } + public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } + public System.ClientModel.ClientToken PageToken { get { throw null; } } public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.PageToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } public partial class ClientResult { @@ -64,12 +64,17 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } + public abstract partial class ClientToken + { + protected ClientToken() { } + public abstract System.BinaryData ToBytes(); + } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } + public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } @@ -172,12 +177,6 @@ public ModelReaderWriterOptions(string format) { } public static System.ClientModel.Primitives.ModelReaderWriterOptions Json { get { throw null; } } public static System.ClientModel.Primitives.ModelReaderWriterOptions Xml { get { throw null; } } } - public abstract partial class PageToken - { - protected PageToken() { } - protected abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } - public abstract System.BinaryData ToBytes(); - } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class PersistableModelProxyAttribute : System.Attribute { diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index e8fab5aae390e..2ab33ba9b4c20 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -10,9 +10,9 @@ public void Update(string key) { } public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } + public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable @@ -35,10 +35,10 @@ protected BinaryContent() { } public partial class ClientPage : System.ClientModel.ClientResult { internal ClientPage() { } - public System.ClientModel.Primitives.PageToken? NextPageToken { get { throw null; } } - public System.ClientModel.Primitives.PageToken PageToken { get { throw null; } } + public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } + public System.ClientModel.ClientToken PageToken { get { throw null; } } public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.PageToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } public partial class ClientResult { @@ -64,12 +64,17 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } + public abstract partial class ClientToken + { + protected ClientToken() { } + public abstract System.BinaryData ToBytes(); + } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } + public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.Primitives.PageToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } @@ -172,12 +177,6 @@ public ModelReaderWriterOptions(string format) { } public static System.ClientModel.Primitives.ModelReaderWriterOptions Json { get { throw null; } } public static System.ClientModel.Primitives.ModelReaderWriterOptions Xml { get { throw null; } } } - public abstract partial class PageToken - { - protected PageToken() { } - protected abstract System.ClientModel.Primitives.PageToken FirstPageToken { get; } - public abstract System.BinaryData ToBytes(); - } [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public sealed partial class PersistableModelProxyAttribute : System.Attribute { diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 6bc992a95631a..d2c98d62980e2 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -20,9 +20,9 @@ protected AsyncPageCollection() : base() // I like this being abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - public abstract PageToken FirstPageToken { get; } + public abstract ClientToken FirstPageToken { get; } - public abstract Task> GetPageAsync(PageToken pageToken, RequestOptions? options = default); + public abstract Task> GetPageAsync(ClientToken pageToken, RequestOptions? options = default); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs index 9969f1f895e67..0599695272da3 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs @@ -11,8 +11,8 @@ namespace System.ClientModel; public class ClientPage : ClientResult { private ClientPage(IReadOnlyList values, - PageToken pageToken, - PageToken? nextPageToken, + ClientToken pageToken, + ClientToken? nextPageToken, PipelineResponse response) : base(response) { Values = values; @@ -30,12 +30,12 @@ private ClientPage(IReadOnlyList values, // in it. // This is useful because I can cache this and retrive both the // full collection this page is in and/or the current page. - public PageToken PageToken { get; } + public ClientToken PageToken { get; } // If this is null, the current page is the last page in a collection. - public PageToken? NextPageToken { get; } + public ClientToken? NextPageToken { get; } - public static ClientPage Create(IReadOnlyList values, PageToken pageToken, PageToken? nextPageToken, PipelineResponse response) + public static ClientPage Create(IReadOnlyList values, ClientToken pageToken, ClientToken? nextPageToken, PipelineResponse response) => new(values, pageToken, nextPageToken, response); } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageToken.cs b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs similarity index 62% rename from sdk/core/System.ClientModel/src/Convenience/PageToken.cs rename to sdk/core/System.ClientModel/src/Convenience/ClientToken.cs index d1b0637050496..8b140241a18f4 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs @@ -5,15 +5,16 @@ using System.Collections.Generic; using System.Text; -namespace System.ClientModel.Primitives; +namespace System.ClientModel; #pragma warning disable CS1591 -public abstract class PageToken +public abstract class ClientToken { - protected PageToken() { } + protected ClientToken() { } - protected abstract PageToken FirstPageToken { get; } + //// Do we need this property? + //protected abstract PageToken FirstPageToken { get; } // Does it need to be public? public abstract BinaryData ToBytes(); diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index d355b3241005a..691a1db43d612 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -19,9 +19,9 @@ protected PageCollection() : base() { } - public abstract PageToken FirstPageToken { get; } + public abstract ClientToken FirstPageToken { get; } - public abstract ClientPage GetPage(PageToken pageToken, RequestOptions? options = default); + public abstract ClientPage GetPage(ClientToken pageToken, RequestOptions? options = default); public IEnumerable GetAllValues() { From 614375f02e08c58e50ad9228c3db497d60907e06 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 13:05:15 -0700 Subject: [PATCH 07/49] nits --- .../src/Convenience/ClientToken.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs index 8b140241a18f4..dbe479a83e00c 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs @@ -8,16 +8,27 @@ namespace System.ClientModel; #pragma warning disable CS1591 - public abstract class ClientToken { protected ClientToken() { } - //// Do we need this property? - //protected abstract PageToken FirstPageToken { get; } - - // Does it need to be public? public abstract BinaryData ToBytes(); } +//public class ClientToken +//{ +// private readonly BinaryData? _bytes; + +// protected ClientToken() { } + +// public ClientToken(BinaryData bytes) +// { +// _bytes = bytes; +// } + +// public virtual BinaryData ToBytes() +// => _bytes ?? +// throw new InvalidOperationException("Unable to write token as bytes."); +//} + #pragma warning restore CS1591 From fbd085f1203f8e6f4e431e538914f154fcb613e6 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 13:19:13 -0700 Subject: [PATCH 08/49] make ClientToken non-abstract --- .../api/System.ClientModel.net6.0.cs | 6 ++-- .../api/System.ClientModel.netstandard2.0.cs | 6 ++-- .../src/Convenience/ClientToken.cs | 30 ++++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 1990e6b4d1fcd..d6d731b678b90 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -64,10 +64,12 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } - public abstract partial class ClientToken + public partial class ClientToken { protected ClientToken() { } - public abstract System.BinaryData ToBytes(); + public ClientToken(System.BinaryData bytes) { } + public static System.ClientModel.ClientToken FromBytes(System.BinaryData bytes) { throw null; } + public virtual System.BinaryData ToBytes() { throw null; } } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 2ab33ba9b4c20..aef7807938abb 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -64,10 +64,12 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } - public abstract partial class ClientToken + public partial class ClientToken { protected ClientToken() { } - public abstract System.BinaryData ToBytes(); + public ClientToken(System.BinaryData bytes) { } + public static System.ClientModel.ClientToken FromBytes(System.BinaryData bytes) { throw null; } + public virtual System.BinaryData ToBytes() { throw null; } } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs index dbe479a83e00c..e83e62880e407 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs @@ -8,27 +8,23 @@ namespace System.ClientModel; #pragma warning disable CS1591 -public abstract class ClientToken -{ - protected ClientToken() { } - public abstract BinaryData ToBytes(); -} +public class ClientToken +{ + private readonly BinaryData? _bytes; -//public class ClientToken -//{ -// private readonly BinaryData? _bytes; + protected ClientToken() { } -// protected ClientToken() { } + public ClientToken(BinaryData bytes) + { + _bytes = bytes; + } -// public ClientToken(BinaryData bytes) -// { -// _bytes = bytes; -// } + public virtual BinaryData ToBytes() + => _bytes ?? + throw new InvalidOperationException("Unable to write token as bytes."); -// public virtual BinaryData ToBytes() -// => _bytes ?? -// throw new InvalidOperationException("Unable to write token as bytes."); -//} + public static ClientToken FromBytes(BinaryData bytes) => new(bytes); +} #pragma warning restore CS1591 From 0c23dfc293f79fc035f1b2a57860af3da6e0e75d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 13:21:30 -0700 Subject: [PATCH 09/49] nits --- .../System.ClientModel/src/Convenience/ClientToken.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs index e83e62880e407..46226bef3497b 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs @@ -1,10 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; -using System.Collections.Generic; -using System.Text; - namespace System.ClientModel; #pragma warning disable CS1591 @@ -15,14 +11,13 @@ public class ClientToken protected ClientToken() { } - public ClientToken(BinaryData bytes) + protected ClientToken(BinaryData bytes) { _bytes = bytes; } - public virtual BinaryData ToBytes() - => _bytes ?? - throw new InvalidOperationException("Unable to write token as bytes."); + public virtual BinaryData ToBytes() => _bytes ?? + throw new InvalidOperationException("Unable to write token as bytes."); public static ClientToken FromBytes(BinaryData bytes) => new(bytes); } From 5f4ae592019a6654bcb45164133074290c6ee508 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 14:10:46 -0700 Subject: [PATCH 10/49] proposed renames --- .../api/System.ClientModel.net6.0.cs | 52 ++++----- .../api/System.ClientModel.netstandard2.0.cs | 52 ++++----- ...tionOfT.cs => AsyncCollectionResultOfT.cs} | 10 +- .../src/Convenience/AsyncPageCollectionOfT.cs | 10 +- .../src/Convenience/ClientResult.cs | 2 +- ...ollectionOfT.cs => CollectionResultOfT.cs} | 10 +- .../src/Convenience/PageCollectionOfT.cs | 12 +- .../{ClientPageOfT.cs => PageResultOfT.cs} | 6 +- .../tests/Convenience/PageCollectionTests.cs | 104 +++++++++--------- .../TestFramework/Mocks/MockPageCollection.cs | 82 +++++++------- .../TestFramework/Mocks/MockPageToken.cs | 16 +++ .../SSE/ClientResultCollectionTests.cs | 12 +- .../TestFramework/Mocks/MockSseClient.cs | 4 +- .../Mocks/MockSseClientExtensions.cs | 4 +- 14 files changed, 196 insertions(+), 180 deletions(-) rename sdk/core/System.ClientModel/src/Convenience/{AsyncResultValueCollectionOfT.cs => AsyncCollectionResultOfT.cs} (79%) rename sdk/core/System.ClientModel/src/Convenience/{ResultValueCollectionOfT.cs => CollectionResultOfT.cs} (80%) rename sdk/core/System.ClientModel/src/Convenience/{ClientPageOfT.cs => PageResultOfT.cs} (89%) create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index d6d731b678b90..4669213cad8bb 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -7,19 +7,19 @@ public ApiKeyCredential(string key) { } public static implicit operator System.ClientModel.ApiKeyCredential (string key) { throw null; } public void Update(string key) { } } - public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> + public abstract partial class AsyncCollectionResult : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable + { + protected internal AsyncCollectionResult() { } + protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineResponse response) { } + public abstract System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } + public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); - System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } - } - public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable - { - protected internal AsyncResultValueCollection() { } - protected internal AsyncResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } - public abstract System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable { @@ -32,14 +32,6 @@ protected BinaryContent() { } public abstract void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public abstract System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } - public partial class ClientPage : System.ClientModel.ClientResult - { - internal ClientPage() { } - public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } - public System.ClientModel.ClientToken PageToken { get { throw null; } } - public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } - } public partial class ClientResult { protected ClientResult() { } @@ -67,25 +59,33 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public partial class ClientToken { protected ClientToken() { } - public ClientToken(System.BinaryData bytes) { } + protected ClientToken(System.BinaryData bytes) { } public static System.ClientModel.ClientToken FromBytes(System.BinaryData bytes) { throw null; } public virtual System.BinaryData ToBytes() { throw null; } } - public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + public abstract partial class CollectionResult : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + { + protected internal CollectionResult() { } + protected internal CollectionResult(System.ClientModel.Primitives.PipelineResponse response) { } + public abstract System.Collections.Generic.IEnumerator GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); - System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } - public abstract partial class ResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + public partial class PageResult : System.ClientModel.ClientResult { - protected internal ResultValueCollection() { } - protected internal ResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } - public abstract System.Collections.Generic.IEnumerator GetEnumerator(); - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + internal PageResult() { } + public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } + public System.ClientModel.ClientToken PageToken { get { throw null; } } + public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } + public static System.ClientModel.PageResult Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } } namespace System.ClientModel.Primitives diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index aef7807938abb..937463fd274bb 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -7,19 +7,19 @@ public ApiKeyCredential(string key) { } public static implicit operator System.ClientModel.ApiKeyCredential (string key) { throw null; } public void Update(string key) { } } - public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> + public abstract partial class AsyncCollectionResult : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable + { + protected internal AsyncCollectionResult() { } + protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineResponse response) { } + public abstract System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } + public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); - System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } - } - public abstract partial class AsyncResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IAsyncEnumerable - { - protected internal AsyncResultValueCollection() { } - protected internal AsyncResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } - public abstract System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable { @@ -32,14 +32,6 @@ protected BinaryContent() { } public abstract void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public abstract System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); } - public partial class ClientPage : System.ClientModel.ClientResult - { - internal ClientPage() { } - public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } - public System.ClientModel.ClientToken PageToken { get { throw null; } } - public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.ClientPage Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } - } public partial class ClientResult { protected ClientResult() { } @@ -67,25 +59,33 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public partial class ClientToken { protected ClientToken() { } - public ClientToken(System.BinaryData bytes) { } + protected ClientToken(System.BinaryData bytes) { } public static System.ClientModel.ClientToken FromBytes(System.BinaryData bytes) { throw null; } public virtual System.BinaryData ToBytes() { throw null; } } - public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable + public abstract partial class CollectionResult : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + { + protected internal CollectionResult() { } + protected internal CollectionResult(System.ClientModel.Primitives.PipelineResponse response) { } + public abstract System.Collections.Generic.IEnumerator GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + } + public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.ClientPage GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); - System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } - public abstract partial class ResultValueCollection : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable + public partial class PageResult : System.ClientModel.ClientResult { - protected internal ResultValueCollection() { } - protected internal ResultValueCollection(System.ClientModel.Primitives.PipelineResponse response) { } - public abstract System.Collections.Generic.IEnumerator GetEnumerator(); - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } + internal PageResult() { } + public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } + public System.ClientModel.ClientToken PageToken { get { throw null; } } + public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } + public static System.ClientModel.PageResult Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } } namespace System.ClientModel.Primitives diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncCollectionResultOfT.cs similarity index 79% rename from sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs rename to sdk/core/System.ClientModel/src/Convenience/AsyncCollectionResultOfT.cs index 43914461e8762..cada18b25ba4c 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncResultValueCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncCollectionResultOfT.cs @@ -11,10 +11,10 @@ namespace System.ClientModel; /// Represents a collection of values returned from a cloud service operation. /// The collection values may be returned by one or more service responses. /// -public abstract class AsyncResultValueCollection : ClientResult, IAsyncEnumerable +public abstract class AsyncCollectionResult : ClientResult, IAsyncEnumerable { /// - /// Create a new instance of . + /// Create a new instance of . /// /// If no is provided when the /// instance is created, it is expected that @@ -25,17 +25,17 @@ public abstract class AsyncResultValueCollection : ClientResult, IAsyncEnumer /// 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. - protected internal AsyncResultValueCollection() : base() + protected internal AsyncCollectionResult() : base() { } /// - /// Create a new instance of . + /// Create a new instance of . /// /// The holding the /// items in the collection, or the first set of the items in the collection. /// - protected internal AsyncResultValueCollection(PipelineResponse response) : base(response) + protected internal AsyncCollectionResult(PipelineResponse response) : base(response) { } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index d2c98d62980e2..ccec652143229 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -11,7 +11,7 @@ namespace System.ClientModel; #pragma warning disable CS1591 -public abstract class AsyncPageCollection : IAsyncEnumerable> +public abstract class AsyncPageCollection : IAsyncEnumerable> { protected AsyncPageCollection() : base() { @@ -22,11 +22,11 @@ protected AsyncPageCollection() : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } - public abstract Task> GetPageAsync(ClientToken pageToken, RequestOptions? options = default); + public abstract Task> GetPageAsync(ClientToken pageToken, RequestOptions? options = default); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { - await foreach (ClientPage page in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + await foreach (PageResult page in this.ConfigureAwait(false).WithCancellation(cancellationToken)) { foreach (T value in page.Values) { @@ -35,13 +35,13 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc } } - async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) { RequestOptions? options = cancellationToken == default ? default : new RequestOptions() { CancellationToken = cancellationToken }; - ClientPage page = await GetPageAsync(FirstPageToken, options).ConfigureAwait(false); + PageResult page = await GetPageAsync(FirstPageToken, options).ConfigureAwait(false); yield return page; while (page.NextPageToken != null) diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs b/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs index 551988e9c3422..77e7b3cbf94d8 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientResult.cs @@ -45,7 +45,7 @@ protected ClientResult(PipelineResponse response) /// No /// value is currently available for this /// instance. This can happen when the instance - /// is a collection type like + /// is a collection type like /// that has not yet been enumerated. public PipelineResponse GetRawResponse() { diff --git a/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/CollectionResultOfT.cs similarity index 80% rename from sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs rename to sdk/core/System.ClientModel/src/Convenience/CollectionResultOfT.cs index cd33af874d22f..bf51575ef9d20 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ResultValueCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/CollectionResultOfT.cs @@ -11,10 +11,10 @@ namespace System.ClientModel; /// Represents a collection of values returned from a cloud service operation. /// The collection values may be returned by one or more service responses. /// -public abstract class ResultValueCollection : ClientResult, IEnumerable +public abstract class CollectionResult : ClientResult, IEnumerable { /// - /// Create a new instance of . + /// Create a new instance of . /// /// If no is provided when the /// instance is created, it is expected that @@ -25,17 +25,17 @@ public abstract class ResultValueCollection : ClientResult, IEnumerable /// 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. - protected internal ResultValueCollection() : base() + protected internal CollectionResult() : base() { } /// - /// Create a new instance of . + /// Create a new instance of . /// /// The holding the /// items in the collection, or the first set of the items in the collection. /// - protected internal ResultValueCollection(PipelineResponse response) : base(response) + protected internal CollectionResult(PipelineResponse response) : base(response) { } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 691a1db43d612..f33bfb9887797 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -11,7 +11,7 @@ namespace System.ClientModel; // This type is a client that defines a collection of elements and can // make service requests to retrieve specific pages -public abstract class PageCollection : IEnumerable> +public abstract class PageCollection : IEnumerable> { // Note - assumes we don't make a request initially, so don't call // response constructor @@ -21,11 +21,11 @@ protected PageCollection() : base() public abstract ClientToken FirstPageToken { get; } - public abstract ClientPage GetPage(ClientToken pageToken, RequestOptions? options = default); + public abstract PageResult GetPage(ClientToken pageToken, RequestOptions? options = default); public IEnumerable GetAllValues() { - foreach (ClientPage page in this) + foreach (PageResult page in this) { foreach (T value in page.Values) { @@ -34,11 +34,11 @@ public IEnumerable GetAllValues() } } - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); - IEnumerator> IEnumerable>.GetEnumerator() + IEnumerator> IEnumerable>.GetEnumerator() { - ClientPage page = GetPage(FirstPageToken); + PageResult page = GetPage(FirstPageToken); yield return page; while (page.NextPageToken != null) diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs similarity index 89% rename from sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs rename to sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs index 0599695272da3..214ec48d31237 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientPageOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs @@ -8,9 +8,9 @@ namespace System.ClientModel; #pragma warning disable CS1591 -public class ClientPage : ClientResult +public class PageResult : ClientResult { - private ClientPage(IReadOnlyList values, + private PageResult(IReadOnlyList values, ClientToken pageToken, ClientToken? nextPageToken, PipelineResponse response) : base(response) @@ -35,7 +35,7 @@ private ClientPage(IReadOnlyList values, // If this is null, the current page is the last page in a collection. public ClientToken? NextPageToken { get; } - public static ClientPage Create(IReadOnlyList values, ClientToken pageToken, ClientToken? nextPageToken, PipelineResponse response) + public static PageResult Create(IReadOnlyList values, ClientToken pageToken, ClientToken? nextPageToken, PipelineResponse response) => new(values, pageToken, nextPageToken, response); } diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 00067683c21b5..ab10dc65d9979 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -10,71 +10,71 @@ namespace System.ClientModel.Tests.Results; public class PageCollectionTests { - [Test] - public void CanEnumeratePages() - { - List values = new() { 0, 1, 2, 3 }; - int pageSize = 2; + //[Test] + //public void CanEnumeratePages() + //{ + // List values = new() { 0, 1, 2, 3 }; + // int pageSize = 2; - List mockResults = new() { - new MockClientResult(new MockPipelineResponse(0)), - new MockClientResult(new MockPipelineResponse(1)) - }; + // List mockResults = new() { + // new MockClientResult(new MockPipelineResponse(0)), + // new MockClientResult(new MockPipelineResponse(1)) + // }; - PageCollection pages = new MockPageCollection(values, mockResults, pageSize); + // PageCollection pages = new MockPageCollection(values, mockResults, pageSize); - int i = 0; - foreach (ClientPage page in pages) - { - Assert.AreEqual(i++, page.Values[0]); - Assert.AreEqual(i++, page.Values[1]); - } + // int i = 0; + // foreach (PageResult page in pages) + // { + // Assert.AreEqual(i++, page.Values[0]); + // Assert.AreEqual(i++, page.Values[1]); + // } - Assert.AreEqual(4, i); - } + // Assert.AreEqual(4, i); + //} - [Test] - public void CanEnumerateClientResults() - { - List mockResults = new() { - new MockClientResult(new MockPipelineResponse(0)), - new MockClientResult(new MockPipelineResponse(1)) - }; + //[Test] + //public void CanEnumerateClientResults() + //{ + // List mockResults = new() { + // new MockClientResult(new MockPipelineResponse(0)), + // new MockClientResult(new MockPipelineResponse(1)) + // }; - IEnumerable results = new ProtocolMockPageCollection(mockResults); + // IEnumerable results = new ProtocolMockPageCollection(mockResults); - int i = 0; - foreach (ClientResult result in results) - { - Assert.AreEqual(i++, result.GetRawResponse().Status); - } + // int i = 0; + // foreach (ClientResult result in results) + // { + // Assert.AreEqual(i++, result.GetRawResponse().Status); + // } - Assert.AreEqual(2, i); - } + // Assert.AreEqual(2, i); + //} - [Test] - public void CanEvolveFromProtocol() - { - List values = new() { 0, 1, 2, 3 }; - int pageSize = 2; + //[Test] + //public void CanEvolveFromProtocol() + //{ + // List values = new() { 0, 1, 2, 3 }; + // int pageSize = 2; - List mockResults = new() { - new MockClientResult(new MockPipelineResponse(0)), - new MockClientResult(new MockPipelineResponse(1)) - }; + // List mockResults = new() { + // new MockClientResult(new MockPipelineResponse(0)), + // new MockClientResult(new MockPipelineResponse(1)) + // }; - // Showing that we can use the same code as protocol-only - // with a convenience return type. - IEnumerable results = new MockPageCollection(values, mockResults, pageSize); + // // Showing that we can use the same code as protocol-only + // // with a convenience return type. + // IEnumerable results = new MockPageCollection(values, mockResults, pageSize); - int i = 0; - foreach (ClientResult result in results) - { - Assert.AreEqual(i++, result.GetRawResponse().Status); - } + // int i = 0; + // foreach (ClientResult result in results) + // { + // Assert.AreEqual(i++, result.GetRawResponse().Status); + // } - Assert.AreEqual(2, i); - } + // Assert.AreEqual(2, i); + //} //private static readonly string[] MockPageContents = { """ // [ diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs index b48be05997171..5ca9ad0ee86a6 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs @@ -9,57 +9,57 @@ namespace ClientModel.Tests.Mocks; -public class MockPageCollection : PageCollection -{ - private readonly List _values; - private readonly int _pageSize; - private readonly List _results; +//public class MockPageCollection : PageCollection +//{ +// private readonly List _values; +// private readonly int _pageSize; +// private readonly List _results; - private int _current; - private int _currentPage; +// private int _current; +// private int _currentPage; - public MockPageCollection(List values, List results, int pageSize) - { - Debug.Assert(results.Count % pageSize == 0); - Debug.Assert(values.Count / pageSize == results.Count); +// public MockPageCollection(List values, List results, int pageSize) +// { +// Debug.Assert(results.Count % pageSize == 0); +// Debug.Assert(values.Count / pageSize == results.Count); - _values = values; - _results = results; - _pageSize = pageSize; - _current = 0; - } +// _values = values; +// _results = results; +// _pageSize = pageSize; +// _current = 0; +// } - public override BinaryData FirstPageToken - => BinaryData.FromString(string.Empty); +// public override ClientToken FirstPageToken +// => BinaryData.FromString(string.Empty); - public override ClientPage GetPage(BinaryData pageToken, RequestOptions? options = null) - { - int currPageSize = Math.Min(_pageSize, _values.Count - _current); +// public override PageResult GetPage(ClientToken pageToken, RequestOptions? options = null) +// { +// int currPageSize = Math.Min(_pageSize, _values.Count - _current); - List values = _values.GetRange(_current, currPageSize); - _current += _pageSize; +// List values = _values.GetRange(_current, currPageSize); +// _current += _pageSize; - BinaryData? nextPageToken = (_current >= _values.Count) ? - null : - new MockPageToken(_current).ToBytes(); +// ClientToken? nextPageToken = (_current >= _values.Count) ? +// null : +// new MockPageToken(_current).ToBytes(); - PipelineResponse response = _results[_currentPage++].GetRawResponse(); +// PipelineResponse response = _results[_currentPage++].GetRawResponse(); - return ClientPage.Create(values, pageToken, nextPageToken, response); - } +// return PageResult.Create(values, pageToken, nextPageToken, response); +// } - private class MockPageToken - { - public MockPageToken(int index) - { - Index = index; - } +// private class MockPageToken +// { +// public MockPageToken(int index) +// { +// Index = index; +// } - public int Index { get; } +// public int Index { get; } - public BinaryData ToBytes() => BinaryData.FromString($"{Index}"); +// public BinaryData ToBytes() => BinaryData.FromString($"{Index}"); - public static MockPageToken FromBytes(BinaryData data) - => new(data.ToMemory().Length == 0 ? 0 : int.Parse(data.ToString())); - } -} +// public static MockPageToken FromBytes(BinaryData data) +// => new(data.ToMemory().Length == 0 ? 0 : int.Parse(data.ToString())); +// } +//} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs new file mode 100644 index 0000000000000..14a0e96c36218 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ClientModel.Tests.Mocks; + +public class MockPageToken : ClientToken +{ + // TODO +} diff --git a/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs b/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs index a7a2296cbfe16..d9f896e7769e8 100644 --- a/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/internal/Convenience/SSE/ClientResultCollectionTests.cs @@ -21,7 +21,7 @@ public ClientResultCollectionTests(bool isAsync) : base(isAsync) public async Task EnumeratesModelValues() { MockSseClient client = new(); - AsyncResultValueCollection models = client.GetModelsStreamingAsync(); + AsyncCollectionResult models = client.GetModelsStreamingAsync(); int i = 0; await foreach (MockJsonModel model in models) @@ -39,7 +39,7 @@ public async Task EnumeratesModelValues() public async Task ModelCollectionDelaysSendingRequest() { MockSseClient client = new(); - AsyncResultValueCollection models = client.GetModelsStreamingAsync(); + AsyncCollectionResult models = client.GetModelsStreamingAsync(); Assert.IsFalse(client.ProtocolMethodCalled); @@ -60,7 +60,7 @@ public async Task ModelCollectionDelaysSendingRequest() public void ModelCollectionThrowsIfCancelled() { MockSseClient client = new(); - AsyncResultValueCollection models = client.GetModelsStreamingAsync(); + AsyncCollectionResult models = client.GetModelsStreamingAsync(); // Set it to `cancelled: true` to validate functionality. CancellationToken token = new(true); @@ -77,7 +77,7 @@ public void ModelCollectionThrowsIfCancelled() public async Task ModelCollectionDisposesStream() { MockSseClient client = new(); - AsyncResultValueCollection models = client.GetModelsStreamingAsync(); + AsyncCollectionResult models = client.GetModelsStreamingAsync(); await foreach (MockJsonModel model in models) { @@ -91,7 +91,7 @@ public async Task ModelCollectionDisposesStream() public void ModelCollectionGetRawResponseThrowsBeforeEnumerated() { MockSseClient client = new(); - AsyncResultValueCollection models = client.GetModelsStreamingAsync(); + AsyncCollectionResult models = client.GetModelsStreamingAsync(); Assert.Throws(() => { PipelineResponse response = models.GetRawResponse(); }); } @@ -99,7 +99,7 @@ public void ModelCollectionGetRawResponseThrowsBeforeEnumerated() public async Task StopsOnStringBasedTerminalEvent() { MockSseClient client = new(); - AsyncResultValueCollection models = client.GetModelsStreamingAsync("[DONE]"); + AsyncCollectionResult models = client.GetModelsStreamingAsync("[DONE]"); bool empty = true; await foreach (MockJsonModel model in models) diff --git a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs index 53fd7510c9d82..34d156fc486b8 100644 --- a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs +++ b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClient.cs @@ -41,7 +41,7 @@ public class MockSseClient public bool ProtocolMethodCalled { get; private set; } // mock convenience method - public virtual AsyncResultValueCollection GetModelsStreamingAsync(string content = DefaultMockContent) + public virtual AsyncCollectionResult GetModelsStreamingAsync(string content = DefaultMockContent) { return new AsyncMockJsonModelCollection(content, GetModelsStreamingAsync); } @@ -63,7 +63,7 @@ public virtual ClientResult GetModelsStreamingAsync(string content, RequestOptio // Internal client implementation of convenience-layer AsyncResultCollection. // This currently layers over an internal AsyncResultCollection // representing the event.data values, but does not strictly have to. - private class AsyncMockJsonModelCollection : AsyncResultValueCollection + private class AsyncMockJsonModelCollection : AsyncCollectionResult { private readonly string _content; private readonly Func _protocolMethod; diff --git a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs index ac4702d9dc1ca..ac493b7abd644 100644 --- a/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs +++ b/sdk/core/System.ClientModel/tests/internal/TestFramework/Mocks/MockSseClientExtensions.cs @@ -15,7 +15,7 @@ namespace ClientModel.Tests.Internal.Mocks; public static class MockSseClientExtensions { - public static AsyncResultValueCollection EnumerateDataEvents(this PipelineResponse response) + public static AsyncCollectionResult EnumerateDataEvents(this PipelineResponse response) { if (response.ContentStream is null) { @@ -25,7 +25,7 @@ public static AsyncResultValueCollection EnumerateDataEvents(this Pi return new AsyncSseDataEventCollection(response, "[DONE]"); } - private class AsyncSseDataEventCollection : AsyncResultValueCollection + private class AsyncSseDataEventCollection : AsyncCollectionResult { private readonly string _terminalData; From ed35bf95c3285df508461a911f3393470af9d287 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 14:54:42 -0700 Subject: [PATCH 11/49] backup idea; I'm wondering if RequestOptions can always be handled by the closure --- .../src/Convenience/AsyncPageCollectionOfT.cs | 14 ++++++++++++-- .../src/Convenience/PageCollectionOfT.cs | 15 +++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index ccec652143229..96043ce6bc873 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -13,15 +13,21 @@ namespace System.ClientModel; public abstract class AsyncPageCollection : IAsyncEnumerable> { - protected AsyncPageCollection() : base() + // Note page collections delay making a first request until either + // GetPage is called or the collection is enumerated, so the constructor + // calls the base class constructor that does not take a response. + protected AsyncPageCollection(RequestOptions? options /* = default*/) : base() { + RequestOptions = options; } - // I like this being abstract rather than providing the field in the base + // Note that this is abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } + protected RequestOptions? RequestOptions { get; } + public abstract Task> GetPageAsync(ClientToken pageToken, RequestOptions? options = default); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) @@ -30,6 +36,8 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc { foreach (T value in page.Values) { + cancellationToken.ThrowIfCancellationRequested(); + yield return value; } } @@ -37,6 +45,8 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) { + // TODO: join cancellation tokens + RequestOptions? options = cancellationToken == default ? default : new RequestOptions() { CancellationToken = cancellationToken }; diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index f33bfb9887797..4306569a84bc6 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -13,14 +13,21 @@ namespace System.ClientModel; // make service requests to retrieve specific pages public abstract class PageCollection : IEnumerable> { - // Note - assumes we don't make a request initially, so don't call - // response constructor - protected PageCollection() : base() + // Note page collections delay making a first request until either + // GetPage is called or the collection is enumerated, so the constructor + // calls the base class constructor that does not take a response. + protected PageCollection(RequestOptions? options/* = default*/) : base() { + RequestOptions = options; } + // Note that this is abstract rather than providing the field in the base + // type because it means the implementation can hold the field as a subtype + // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } + protected RequestOptions? RequestOptions { get; } + public abstract PageResult GetPage(ClientToken pageToken, RequestOptions? options = default); public IEnumerable GetAllValues() @@ -38,7 +45,7 @@ public IEnumerable GetAllValues() IEnumerator> IEnumerable>.GetEnumerator() { - PageResult page = GetPage(FirstPageToken); + PageResult page = GetPage(FirstPageToken, RequestOptions); yield return page; while (page.NextPageToken != null) From 7f18c2a5820203df5fa23f4e890f62db1987be73 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 15:05:08 -0700 Subject: [PATCH 12/49] remove RequestOptions from signatures --- .../api/System.ClientModel.net6.0.cs | 4 ++-- .../api/System.ClientModel.netstandard2.0.cs | 4 ++-- .../src/Convenience/AsyncPageCollectionOfT.cs | 21 +++++++------------ .../src/Convenience/PageCollectionOfT.cs | 11 ++++------ 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 4669213cad8bb..805362d5e2a3b 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -18,7 +18,7 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -75,7 +75,7 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 937463fd274bb..1c0087b7874a7 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -18,7 +18,7 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -75,7 +75,7 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken, System.ClientModel.Primitives.RequestOptions? options = null); + public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 96043ce6bc873..4a7ac0ac2c750 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.ClientModel.Primitives; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; @@ -16,9 +15,8 @@ public abstract class AsyncPageCollection : IAsyncEnumerable> // Note page collections delay making a first request until either // GetPage is called or the collection is enumerated, so the constructor // calls the base class constructor that does not take a response. - protected AsyncPageCollection(RequestOptions? options /* = default*/) : base() + protected AsyncPageCollection() : base() { - RequestOptions = options; } // Note that this is abstract rather than providing the field in the base @@ -26,9 +24,8 @@ protected AsyncPageCollection(RequestOptions? options /* = default*/) : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } - protected RequestOptions? RequestOptions { get; } - - public abstract Task> GetPageAsync(ClientToken pageToken, RequestOptions? options = default); + // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. + public abstract Task> GetPageAsync(ClientToken pageToken); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -45,18 +42,14 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) { - // TODO: join cancellation tokens - - RequestOptions? options = cancellationToken == default ? - default : - new RequestOptions() { CancellationToken = cancellationToken }; - - PageResult page = await GetPageAsync(FirstPageToken, options).ConfigureAwait(false); + PageResult page = await GetPageAsync(FirstPageToken).ConfigureAwait(false); yield return page; while (page.NextPageToken != null) { - page = await GetPageAsync(page.NextPageToken, options).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + + page = await GetPageAsync(page.NextPageToken).ConfigureAwait(false); yield return page; } } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 4306569a84bc6..c5c6a4077879a 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.ClientModel.Primitives; using System.Collections; using System.Collections.Generic; @@ -16,9 +15,8 @@ public abstract class PageCollection : IEnumerable> // Note page collections delay making a first request until either // GetPage is called or the collection is enumerated, so the constructor // calls the base class constructor that does not take a response. - protected PageCollection(RequestOptions? options/* = default*/) : base() + protected PageCollection() : base() { - RequestOptions = options; } // Note that this is abstract rather than providing the field in the base @@ -26,9 +24,8 @@ protected PageCollection(RequestOptions? options/* = default*/) : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } - protected RequestOptions? RequestOptions { get; } - - public abstract PageResult GetPage(ClientToken pageToken, RequestOptions? options = default); + // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. + public abstract PageResult GetPage(ClientToken pageToken); public IEnumerable GetAllValues() { @@ -45,7 +42,7 @@ public IEnumerable GetAllValues() IEnumerator> IEnumerable>.GetEnumerator() { - PageResult page = GetPage(FirstPageToken, RequestOptions); + PageResult page = GetPage(FirstPageToken); yield return page; while (page.NextPageToken != null) From 104c8f6cd2809475d3d05f4f8caf6cce50f56ec7 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 15:09:49 -0700 Subject: [PATCH 13/49] nits --- sdk/core/System.ClientModel/src/Convenience/ClientToken.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs index 46226bef3497b..eabb54728488e 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs @@ -16,10 +16,10 @@ protected ClientToken(BinaryData bytes) _bytes = bytes; } + public static ClientToken FromBytes(BinaryData bytes) => new(bytes); + public virtual BinaryData ToBytes() => _bytes ?? throw new InvalidOperationException("Unable to write token as bytes."); - - public static ClientToken FromBytes(BinaryData bytes) => new(bytes); } #pragma warning restore CS1591 From cbd529f6f75913fa3b3121b2e8a6c0167f84a752 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 26 Jun 2024 17:20:22 -0700 Subject: [PATCH 14/49] GetPageCore - enable an overload that doesn't take a page token --- .../api/System.ClientModel.net6.0.cs | 8 ++++++-- .../api/System.ClientModel.netstandard2.0.cs | 8 ++++++-- .../src/Convenience/AsyncPageCollectionOfT.cs | 12 +++++++++++- .../src/Convenience/PageCollectionOfT.cs | 12 +++++++++++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 805362d5e2a3b..a418321c1c0bf 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -18,7 +18,9 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken); + public System.Threading.Tasks.Task> GetPageAsync() { throw null; } + public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken) { throw null; } + public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -75,7 +77,9 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken); + public System.ClientModel.PageResult GetPage() { throw null; } + public System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken) { throw null; } + public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 1c0087b7874a7..e1e509668235d 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -18,7 +18,9 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken); + public System.Threading.Tasks.Task> GetPageAsync() { throw null; } + public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken) { throw null; } + public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -75,7 +77,9 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public abstract System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken); + public System.ClientModel.PageResult GetPage() { throw null; } + public System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken) { throw null; } + public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 4a7ac0ac2c750..0407b7c0b7148 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.ClientModel.Internal; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; @@ -24,8 +25,17 @@ protected AsyncPageCollection() : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } + public async Task> GetPageAsync() + => await GetPageAsyncCore(FirstPageToken).ConfigureAwait(false); + + public async Task> GetPageAsync(ClientToken pageToken) + { + Argument.AssertNotNull(pageToken, nameof(pageToken)); + return await GetPageAsyncCore(FirstPageToken).ConfigureAwait(false); + } + // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract Task> GetPageAsync(ClientToken pageToken); + public abstract Task> GetPageAsyncCore(ClientToken pageToken); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index c5c6a4077879a..c42c3ef5e5c08 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.ClientModel.Internal; using System.Collections; using System.Collections.Generic; @@ -24,8 +25,17 @@ protected PageCollection() : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } + public PageResult GetPage() + => GetPageCore(FirstPageToken); + + public PageResult GetPage(ClientToken pageToken) + { + Argument.AssertNotNull(pageToken, nameof(pageToken)); + return GetPageCore(pageToken); + } + // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract PageResult GetPage(ClientToken pageToken); + public abstract PageResult GetPageCore(ClientToken pageToken); public IEnumerable GetAllValues() { From 31b9990f9908326b50e5028c2762d1e36eb9bd77 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Thu, 27 Jun 2024 08:12:59 -0700 Subject: [PATCH 15/49] Change GetPage with no parameters to GetFirstPage --- sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs | 4 ++-- .../api/System.ClientModel.netstandard2.0.cs | 4 ++-- .../src/Convenience/AsyncPageCollectionOfT.cs | 2 +- .../System.ClientModel/src/Convenience/PageCollectionOfT.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index a418321c1c0bf..263ff2a373ff3 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -18,7 +18,7 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetPageAsync() { throw null; } + public System.Threading.Tasks.Task> GetFirstPageAsync() { throw null; } public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } @@ -77,7 +77,7 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public System.ClientModel.PageResult GetPage() { throw null; } + public System.ClientModel.PageResult GetFirstPage() { throw null; } public System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken) { throw null; } public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index e1e509668235d..3797f8bdf98ac 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -18,7 +18,7 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetPageAsync() { throw null; } + public System.Threading.Tasks.Task> GetFirstPageAsync() { throw null; } public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } @@ -77,7 +77,7 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public abstract System.ClientModel.ClientToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public System.ClientModel.PageResult GetPage() { throw null; } + public System.ClientModel.PageResult GetFirstPage() { throw null; } public System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken) { throw null; } public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ClientToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 0407b7c0b7148..cc6c6fbf0d4bd 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -25,7 +25,7 @@ protected AsyncPageCollection() : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } - public async Task> GetPageAsync() + public async Task> GetFirstPageAsync() => await GetPageAsyncCore(FirstPageToken).ConfigureAwait(false); public async Task> GetPageAsync(ClientToken pageToken) diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index c42c3ef5e5c08..8268ca17c9aa3 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -25,7 +25,7 @@ protected PageCollection() : base() // instance in the implementation and not have to cast it. public abstract ClientToken FirstPageToken { get; } - public PageResult GetPage() + public PageResult GetFirstPage() => GetPageCore(FirstPageToken); public PageResult GetPage(ClientToken pageToken) From 6a56d39f46ff450555a6626ef09d94e61d0fc7d9 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Thu, 27 Jun 2024 09:24:44 -0700 Subject: [PATCH 16/49] rename ClientToken to ContinuationToken and remove GetFirstPage method --- .../api/System.ClientModel.net6.0.cs | 34 +++++++++---------- .../api/System.ClientModel.netstandard2.0.cs | 34 +++++++++---------- .../src/Convenience/AsyncPageCollectionOfT.cs | 9 ++--- .../{ClientToken.cs => ContinuationToken.cs} | 8 ++--- .../src/Convenience/PageCollectionOfT.cs | 9 ++--- .../src/Convenience/PageResultOfT.cs | 10 +++--- .../TestFramework/Mocks/MockPageToken.cs | 2 +- 7 files changed, 48 insertions(+), 58 deletions(-) rename sdk/core/System.ClientModel/src/Convenience/{ClientToken.cs => ContinuationToken.cs} (67%) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 263ff2a373ff3..b7ffaaf5e3045 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -16,11 +16,10 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.ClientModel.ClientToken FirstPageToken { get; } + public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetFirstPageAsync() { throw null; } - public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ClientToken pageToken); + public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } + public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -58,13 +57,6 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } - public partial class ClientToken - { - protected ClientToken() { } - protected ClientToken(System.BinaryData bytes) { } - public static System.ClientModel.ClientToken FromBytes(System.BinaryData bytes) { throw null; } - public virtual System.BinaryData ToBytes() { throw null; } - } public abstract partial class CollectionResult : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { protected internal CollectionResult() { } @@ -72,24 +64,30 @@ protected internal CollectionResult(System.ClientModel.Primitives.PipelineRespon public abstract System.Collections.Generic.IEnumerator GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } + public partial class ContinuationToken + { + 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 : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.ClientModel.ClientToken FirstPageToken { get; } + public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public System.ClientModel.PageResult GetFirstPage() { throw null; } - public System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken) { throw null; } - public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ClientToken pageToken); + public System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } + public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class PageResult : System.ClientModel.ClientResult { internal PageResult() { } - public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } - public System.ClientModel.ClientToken PageToken { get { throw null; } } + public System.ClientModel.ContinuationToken? NextPageToken { get { throw null; } } + public System.ClientModel.ContinuationToken PageToken { get { throw null; } } public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.PageResult Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + public static System.ClientModel.PageResult Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ContinuationToken pageToken, System.ClientModel.ContinuationToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } } namespace System.ClientModel.Primitives diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 3797f8bdf98ac..1e9d4a52fcfaf 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -16,11 +16,10 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.ClientModel.ClientToken FirstPageToken { get; } + public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetFirstPageAsync() { throw null; } - public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ClientToken pageToken) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ClientToken pageToken); + public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } + public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -58,13 +57,6 @@ protected internal ClientResult(T value, System.ClientModel.Primitives.PipelineR public virtual T Value { get { throw null; } } public static implicit operator T (System.ClientModel.ClientResult result) { throw null; } } - public partial class ClientToken - { - protected ClientToken() { } - protected ClientToken(System.BinaryData bytes) { } - public static System.ClientModel.ClientToken FromBytes(System.BinaryData bytes) { throw null; } - public virtual System.BinaryData ToBytes() { throw null; } - } public abstract partial class CollectionResult : System.ClientModel.ClientResult, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { protected internal CollectionResult() { } @@ -72,24 +64,30 @@ protected internal CollectionResult(System.ClientModel.Primitives.PipelineRespon public abstract System.Collections.Generic.IEnumerator GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } + public partial class ContinuationToken + { + 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 : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.ClientModel.ClientToken FirstPageToken { get; } + public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public System.ClientModel.PageResult GetFirstPage() { throw null; } - public System.ClientModel.PageResult GetPage(System.ClientModel.ClientToken pageToken) { throw null; } - public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ClientToken pageToken); + public System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } + public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class PageResult : System.ClientModel.ClientResult { internal PageResult() { } - public System.ClientModel.ClientToken? NextPageToken { get { throw null; } } - public System.ClientModel.ClientToken PageToken { get { throw null; } } + public System.ClientModel.ContinuationToken? NextPageToken { get { throw null; } } + public System.ClientModel.ContinuationToken PageToken { get { throw null; } } public System.Collections.Generic.IReadOnlyList Values { get { throw null; } } - public static System.ClientModel.PageResult Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ClientToken pageToken, System.ClientModel.ClientToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } + public static System.ClientModel.PageResult Create(System.Collections.Generic.IReadOnlyList values, System.ClientModel.ContinuationToken pageToken, System.ClientModel.ContinuationToken? nextPageToken, System.ClientModel.Primitives.PipelineResponse response) { throw null; } } } namespace System.ClientModel.Primitives diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index cc6c6fbf0d4bd..be25866075e9a 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -23,19 +23,16 @@ protected AsyncPageCollection() : base() // Note that this is abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - public abstract ClientToken FirstPageToken { get; } + public abstract ContinuationToken FirstPageToken { get; } - public async Task> GetFirstPageAsync() - => await GetPageAsyncCore(FirstPageToken).ConfigureAwait(false); - - public async Task> GetPageAsync(ClientToken pageToken) + public async Task> GetPageAsync(ContinuationToken pageToken) { Argument.AssertNotNull(pageToken, nameof(pageToken)); return await GetPageAsyncCore(FirstPageToken).ConfigureAwait(false); } // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract Task> GetPageAsyncCore(ClientToken pageToken); + public abstract Task> GetPageAsyncCore(ContinuationToken pageToken); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { diff --git a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs similarity index 67% rename from sdk/core/System.ClientModel/src/Convenience/ClientToken.cs rename to sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs index eabb54728488e..8258c0a14d269 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ClientToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs @@ -5,18 +5,18 @@ namespace System.ClientModel; #pragma warning disable CS1591 -public class ClientToken +public class ContinuationToken { private readonly BinaryData? _bytes; - protected ClientToken() { } + protected ContinuationToken() { } - protected ClientToken(BinaryData bytes) + protected ContinuationToken(BinaryData bytes) { _bytes = bytes; } - public static ClientToken FromBytes(BinaryData bytes) => new(bytes); + public static ContinuationToken FromBytes(BinaryData bytes) => new(bytes); public virtual BinaryData ToBytes() => _bytes ?? throw new InvalidOperationException("Unable to write token as bytes."); diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 8268ca17c9aa3..56d578430a652 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -23,19 +23,16 @@ protected PageCollection() : base() // Note that this is abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - public abstract ClientToken FirstPageToken { get; } + public abstract ContinuationToken FirstPageToken { get; } - public PageResult GetFirstPage() - => GetPageCore(FirstPageToken); - - public PageResult GetPage(ClientToken pageToken) + public PageResult GetPage(ContinuationToken pageToken) { Argument.AssertNotNull(pageToken, nameof(pageToken)); return GetPageCore(pageToken); } // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract PageResult GetPageCore(ClientToken pageToken); + public abstract PageResult GetPageCore(ContinuationToken pageToken); public IEnumerable GetAllValues() { diff --git a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs index 214ec48d31237..58cf6686e381a 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs @@ -11,8 +11,8 @@ namespace System.ClientModel; public class PageResult : ClientResult { private PageResult(IReadOnlyList values, - ClientToken pageToken, - ClientToken? nextPageToken, + ContinuationToken pageToken, + ContinuationToken? nextPageToken, PipelineResponse response) : base(response) { Values = values; @@ -30,12 +30,12 @@ private PageResult(IReadOnlyList values, // in it. // This is useful because I can cache this and retrive both the // full collection this page is in and/or the current page. - public ClientToken PageToken { get; } + public ContinuationToken PageToken { get; } // If this is null, the current page is the last page in a collection. - public ClientToken? NextPageToken { get; } + public ContinuationToken? NextPageToken { get; } - public static PageResult Create(IReadOnlyList values, ClientToken pageToken, ClientToken? nextPageToken, PipelineResponse response) + public static PageResult Create(IReadOnlyList values, ContinuationToken pageToken, ContinuationToken? nextPageToken, PipelineResponse response) => new(values, pageToken, nextPageToken, response); } diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs index 14a0e96c36218..42f779c7adb01 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs @@ -10,7 +10,7 @@ namespace ClientModel.Tests.Mocks; -public class MockPageToken : ClientToken +public class MockPageToken : ContinuationToken { // TODO } From bb41536da3e592fdf1b43368e9f764bb99edba13 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 10:31:14 -0700 Subject: [PATCH 17/49] some updates based on feedback from Krzysztof --- .../api/System.ClientModel.net6.0.cs | 12 ++++---- .../api/System.ClientModel.netstandard2.0.cs | 12 ++++---- .../src/Convenience/AsyncPageCollectionOfT.cs | 26 +++++++++++------ .../src/Convenience/PageCollectionOfT.cs | 28 +++++++++++++------ 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index b7ffaaf5e3045..963d9c683c281 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -16,9 +16,10 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } + public System.Threading.Tasks.Task> GetCurrentPage() { throw null; } + protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -74,10 +75,11 @@ protected ContinuationToken(System.BinaryData bytes) { } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } - public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); + public System.ClientModel.PageResult GetCurrentPage() { throw null; } + protected System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } + protected abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 1e9d4a52fcfaf..648d4318c126c 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -16,9 +16,10 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } + public System.Threading.Tasks.Task> GetCurrentPage() { throw null; } + protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } @@ -74,10 +75,11 @@ protected ContinuationToken(System.BinaryData bytes) { } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - public abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } - public System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } - public abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); + public System.ClientModel.PageResult GetCurrentPage() { throw null; } + protected System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } + protected abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index be25866075e9a..3ffecad45deb1 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -13,6 +13,8 @@ namespace System.ClientModel; public abstract class AsyncPageCollection : IAsyncEnumerable> { + private ContinuationToken? _currentPageToken; + // Note page collections delay making a first request until either // GetPage is called or the collection is enumerated, so the constructor // calls the base class constructor that does not take a response. @@ -23,16 +25,12 @@ protected AsyncPageCollection() : base() // Note that this is abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - public abstract ContinuationToken FirstPageToken { get; } - public async Task> GetPageAsync(ContinuationToken pageToken) - { - Argument.AssertNotNull(pageToken, nameof(pageToken)); - return await GetPageAsyncCore(FirstPageToken).ConfigureAwait(false); - } + // TODO: do we need this to be public? + protected abstract ContinuationToken FirstPageToken { get; /*protected set;*/ } - // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract Task> GetPageAsyncCore(ContinuationToken pageToken); + public async Task> GetCurrentPage() + => await GetPageAsync(_currentPageToken ?? FirstPageToken).ConfigureAwait(false); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -47,6 +45,16 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc } } + protected async Task> GetPageAsync(ContinuationToken pageToken) + { + Argument.AssertNotNull(pageToken, nameof(pageToken)); + + return await GetPageAsyncCore(pageToken).ConfigureAwait(false); + } + + // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. + public abstract Task> GetPageAsyncCore(ContinuationToken pageToken); + async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) { PageResult page = await GetPageAsync(FirstPageToken).ConfigureAwait(false); @@ -57,6 +65,8 @@ async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEn cancellationToken.ThrowIfCancellationRequested(); page = await GetPageAsync(page.NextPageToken).ConfigureAwait(false); + _currentPageToken = page.PageToken; + yield return page; } } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 56d578430a652..5d186249647bd 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -4,6 +4,7 @@ using System.ClientModel.Internal; using System.Collections; using System.Collections.Generic; +using System.Threading; namespace System.ClientModel; @@ -13,6 +14,8 @@ namespace System.ClientModel; // make service requests to retrieve specific pages public abstract class PageCollection : IEnumerable> { + private ContinuationToken? _currentPageToken; + // Note page collections delay making a first request until either // GetPage is called or the collection is enumerated, so the constructor // calls the base class constructor that does not take a response. @@ -23,16 +26,12 @@ protected PageCollection() : base() // Note that this is abstract rather than providing the field in the base // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - public abstract ContinuationToken FirstPageToken { get; } - public PageResult GetPage(ContinuationToken pageToken) - { - Argument.AssertNotNull(pageToken, nameof(pageToken)); - return GetPageCore(pageToken); - } + // TODO: do we need this to be public? + protected abstract ContinuationToken FirstPageToken { get; /*protected set;*/ } - // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract PageResult GetPageCore(ContinuationToken pageToken); + public PageResult GetCurrentPage() => + GetPage(_currentPageToken ?? FirstPageToken); public IEnumerable GetAllValues() { @@ -45,6 +44,17 @@ public IEnumerable GetAllValues() } } + // TODO: do we need this, and do we need it to be called Core? + protected PageResult GetPage(ContinuationToken pageToken) + { + Argument.AssertNotNull(pageToken, nameof(pageToken)); + + return GetPageCore(pageToken); + } + + // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. + protected abstract PageResult GetPageCore(ContinuationToken pageToken); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); IEnumerator> IEnumerable>.GetEnumerator() @@ -55,6 +65,8 @@ IEnumerator> IEnumerable>.GetEnumerator() while (page.NextPageToken != null) { page = GetPage(page.NextPageToken); + _currentPageToken = page.PageToken; + yield return page; } } From 5b846cda7fd1d281c4542e18f45a12a3a3056466 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 10:44:22 -0700 Subject: [PATCH 18/49] add Async suffix --- sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs | 2 +- .../System.ClientModel/api/System.ClientModel.netstandard2.0.cs | 2 +- .../src/Convenience/AsyncPageCollectionOfT.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 963d9c683c281..aaf3d62f303e3 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -18,7 +18,7 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetCurrentPage() { throw null; } + public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 648d4318c126c..20fb862a7d1b9 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -18,7 +18,7 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi protected AsyncPageCollection() { } protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task> GetCurrentPage() { throw null; } + public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 3ffecad45deb1..1268e14f6a3d9 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -29,7 +29,7 @@ protected AsyncPageCollection() : base() // TODO: do we need this to be public? protected abstract ContinuationToken FirstPageToken { get; /*protected set;*/ } - public async Task> GetCurrentPage() + public async Task> GetCurrentPageAsync() => await GetPageAsync(_currentPageToken ?? FirstPageToken).ConfigureAwait(false); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) From 875be9f83b18c7388e43a312779b84ee441c4108 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 12:04:50 -0700 Subject: [PATCH 19/49] move to CurrentPageToken instead of FirstPageToken --- .../api/System.ClientModel.net6.0.cs | 4 ++-- .../api/System.ClientModel.netstandard2.0.cs | 4 ++-- .../src/Convenience/AsyncPageCollectionOfT.cs | 12 +++++------- .../src/Convenience/PageCollectionOfT.cs | 14 +++++--------- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index aaf3d62f303e3..f6e8d4afb31e4 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -16,7 +16,7 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } @@ -75,7 +75,7 @@ protected ContinuationToken(System.BinaryData bytes) { } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } protected System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 20fb862a7d1b9..6d944a603074a 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -16,7 +16,7 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } @@ -75,7 +75,7 @@ protected ContinuationToken(System.BinaryData bytes) { } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - protected abstract System.ClientModel.ContinuationToken FirstPageToken { get; } + protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } protected System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 1268e14f6a3d9..8b55a9e4ad9c3 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -13,8 +13,6 @@ namespace System.ClientModel; public abstract class AsyncPageCollection : IAsyncEnumerable> { - private ContinuationToken? _currentPageToken; - // Note page collections delay making a first request until either // GetPage is called or the collection is enumerated, so the constructor // calls the base class constructor that does not take a response. @@ -26,11 +24,11 @@ protected AsyncPageCollection() : base() // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - // TODO: do we need this to be public? - protected abstract ContinuationToken FirstPageToken { get; /*protected set;*/ } + // If we ever make this property public, we should keep the setter protected. + protected abstract ContinuationToken CurrentPageToken { get; set; } public async Task> GetCurrentPageAsync() - => await GetPageAsync(_currentPageToken ?? FirstPageToken).ConfigureAwait(false); + => await GetPageAsync(CurrentPageToken).ConfigureAwait(false); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -57,7 +55,7 @@ protected async Task> GetPageAsync(ContinuationToken pageToken) async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) { - PageResult page = await GetPageAsync(FirstPageToken).ConfigureAwait(false); + PageResult page = await GetPageAsync(CurrentPageToken).ConfigureAwait(false); yield return page; while (page.NextPageToken != null) @@ -65,7 +63,7 @@ async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEn cancellationToken.ThrowIfCancellationRequested(); page = await GetPageAsync(page.NextPageToken).ConfigureAwait(false); - _currentPageToken = page.PageToken; + CurrentPageToken = page.PageToken; yield return page; } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 5d186249647bd..21f7db418fa7b 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -4,7 +4,6 @@ using System.ClientModel.Internal; using System.Collections; using System.Collections.Generic; -using System.Threading; namespace System.ClientModel; @@ -14,8 +13,6 @@ namespace System.ClientModel; // make service requests to retrieve specific pages public abstract class PageCollection : IEnumerable> { - private ContinuationToken? _currentPageToken; - // Note page collections delay making a first request until either // GetPage is called or the collection is enumerated, so the constructor // calls the base class constructor that does not take a response. @@ -27,11 +24,10 @@ protected PageCollection() : base() // type because it means the implementation can hold the field as a subtype // instance in the implementation and not have to cast it. - // TODO: do we need this to be public? - protected abstract ContinuationToken FirstPageToken { get; /*protected set;*/ } + // If we ever make this property public, we should keep the setter protected. + protected abstract ContinuationToken CurrentPageToken { get; set; } - public PageResult GetCurrentPage() => - GetPage(_currentPageToken ?? FirstPageToken); + public PageResult GetCurrentPage() => GetPage(CurrentPageToken); public IEnumerable GetAllValues() { @@ -59,13 +55,13 @@ protected PageResult GetPage(ContinuationToken pageToken) IEnumerator> IEnumerable>.GetEnumerator() { - PageResult page = GetPage(FirstPageToken); + PageResult page = GetPage(CurrentPageToken); yield return page; while (page.NextPageToken != null) { page = GetPage(page.NextPageToken); - _currentPageToken = page.PageToken; + CurrentPageToken = page.PageToken; yield return page; } From 844c62af38f059b496554a6e3f740449b71f4213 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 14:10:44 -0700 Subject: [PATCH 20/49] simplified page collection --- .../api/System.ClientModel.net6.0.cs | 10 +-- .../api/System.ClientModel.netstandard2.0.cs | 10 +-- .../src/Convenience/AsyncPageCollectionOfT.cs | 64 +++++++++++-------- .../src/Convenience/PageCollectionOfT.cs | 63 ++++++++++-------- 4 files changed, 81 insertions(+), 66 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index f6e8d4afb31e4..2d39e8e3469f6 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -16,12 +16,9 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } - protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); - System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable { @@ -75,12 +72,9 @@ protected ContinuationToken(System.BinaryData bytes) { } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } - protected System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } - protected abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); - System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + public abstract System.Collections.Generic.IEnumerator> GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class PageResult : System.ClientModel.ClientResult diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 6d944a603074a..424518b43ffbd 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -16,12 +16,9 @@ protected internal AsyncCollectionResult(System.ClientModel.Primitives.PipelineR public abstract partial class AsyncPageCollection : System.Collections.Generic.IAsyncEnumerable> { protected AsyncPageCollection() { } - protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } - protected System.Threading.Tasks.Task> GetPageAsync(System.ClientModel.ContinuationToken pageToken) { throw null; } - public abstract System.Threading.Tasks.Task> GetPageAsyncCore(System.ClientModel.ContinuationToken pageToken); - System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable { @@ -75,12 +72,9 @@ protected ContinuationToken(System.BinaryData bytes) { } public abstract partial class PageCollection : System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { protected PageCollection() { } - protected abstract System.ClientModel.ContinuationToken CurrentPageToken { get; set; } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } - protected System.ClientModel.PageResult GetPage(System.ClientModel.ContinuationToken pageToken) { throw null; } - protected abstract System.ClientModel.PageResult GetPageCore(System.ClientModel.ContinuationToken pageToken); - System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } + public abstract System.Collections.Generic.IEnumerator> GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class PageResult : System.ClientModel.ClientResult diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 8b55a9e4ad9c3..0890b1b08f11b 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -20,15 +20,26 @@ protected AsyncPageCollection() : base() { } - // Note that this is abstract rather than providing the field in the base - // type because it means the implementation can hold the field as a subtype - // instance in the implementation and not have to cast it. + //// Note that this is abstract rather than providing the field in the base + //// type because it means the implementation can hold the field as a subtype + //// instance in the implementation and not have to cast it. - // If we ever make this property public, we should keep the setter protected. - protected abstract ContinuationToken CurrentPageToken { get; set; } + //// If we ever make this property public, we should keep the setter protected. + //protected abstract ContinuationToken CurrentPageToken { get; set; } public async Task> GetCurrentPageAsync() - => await GetPageAsync(CurrentPageToken).ConfigureAwait(false); + { + IAsyncEnumerator> enumerator = GetAsyncEnumerator(); + PageResult current = enumerator.Current; + + if (current == null) + { + await enumerator.MoveNextAsync().ConfigureAwait(false); + current = enumerator.Current; + } + + return current; + } public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { @@ -43,31 +54,34 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc } } - protected async Task> GetPageAsync(ContinuationToken pageToken) - { - Argument.AssertNotNull(pageToken, nameof(pageToken)); + public abstract IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default); - return await GetPageAsyncCore(pageToken).ConfigureAwait(false); - } + //protected async Task> GetPageAsync(ContinuationToken pageToken) + //{ + // // This is important to validate against passing a null NextPageToken. + // Argument.AssertNotNull(pageToken, nameof(pageToken)); - // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - public abstract Task> GetPageAsyncCore(ContinuationToken pageToken); + // return await GetPageAsyncCore(pageToken).ConfigureAwait(false); + //} - async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) - { - PageResult page = await GetPageAsync(CurrentPageToken).ConfigureAwait(false); - yield return page; + //// Doesn't take RequestOptions because RequestOptions cannot be rehydrated. + //public abstract Task> GetPageAsyncCore(ContinuationToken pageToken); - while (page.NextPageToken != null) - { - cancellationToken.ThrowIfCancellationRequested(); + //async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + //{ + // PageResult page = await GetPageAsync(CurrentPageToken).ConfigureAwait(false); + // yield return page; - page = await GetPageAsync(page.NextPageToken).ConfigureAwait(false); - CurrentPageToken = page.PageToken; + // while (page.NextPageToken != null) + // { + // cancellationToken.ThrowIfCancellationRequested(); - yield return page; - } - } + // page = await GetPageAsync(page.NextPageToken).ConfigureAwait(false); + // CurrentPageToken = page.PageToken; + + // yield return page; + // } + //} } #pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 21f7db418fa7b..8287a20273ab4 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -20,14 +20,26 @@ protected PageCollection() : base() { } - // Note that this is abstract rather than providing the field in the base - // type because it means the implementation can hold the field as a subtype - // instance in the implementation and not have to cast it. + //// Note that this is abstract rather than providing the field in the base + //// type because it means the implementation can hold the field as a subtype + //// instance in the implementation and not have to cast it. - // If we ever make this property public, we should keep the setter protected. - protected abstract ContinuationToken CurrentPageToken { get; set; } + //// If we ever make this property public, we should keep the setter protected. + //protected abstract ContinuationToken CurrentPageToken { get; set; } - public PageResult GetCurrentPage() => GetPage(CurrentPageToken); + public PageResult GetCurrentPage() + { + IEnumerator> enumerator = GetEnumerator(); + PageResult current = enumerator.Current; + + if (current == null) + { + enumerator.MoveNext(); + current = enumerator.Current; + } + + return current; + } public IEnumerable GetAllValues() { @@ -40,32 +52,33 @@ public IEnumerable GetAllValues() } } - // TODO: do we need this, and do we need it to be called Core? - protected PageResult GetPage(ContinuationToken pageToken) - { - Argument.AssertNotNull(pageToken, nameof(pageToken)); + //// TODO: do we need this, and do we need it to be called Core? + //protected PageResult GetPage(ContinuationToken pageToken) + //{ + // Argument.AssertNotNull(pageToken, nameof(pageToken)); - return GetPageCore(pageToken); - } + // return GetPageCore(pageToken); + //} - // Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - protected abstract PageResult GetPageCore(ContinuationToken pageToken); + //// Doesn't take RequestOptions because RequestOptions cannot be rehydrated. + //protected abstract PageResult GetPageCore(ContinuationToken pageToken); + public abstract IEnumerator> GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); - IEnumerator> IEnumerable>.GetEnumerator() - { - PageResult page = GetPage(CurrentPageToken); - yield return page; + //IEnumerator> IEnumerable>.GetEnumerator() + //{ + // PageResult page = GetCurrentPage(); + // yield return page; - while (page.NextPageToken != null) - { - page = GetPage(page.NextPageToken); - CurrentPageToken = page.PageToken; + // while (page.NextPageToken != null) + // { + // page = GetPage(page.NextPageToken); + // CurrentPageToken = page.PageToken; - yield return page; - } - } + // yield return page; + // } + //} } #pragma warning restore CS1591 From c3bac5a55f73c7bdeff14372387f914e427b9daa Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 14:58:30 -0700 Subject: [PATCH 21/49] tidy --- .../src/Convenience/AsyncPageCollectionOfT.cs | 35 +------------------ .../src/Convenience/PageCollectionOfT.cs | 32 ----------------- 2 files changed, 1 insertion(+), 66 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 0890b1b08f11b..42406825f62ac 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -20,18 +20,12 @@ protected AsyncPageCollection() : base() { } - //// Note that this is abstract rather than providing the field in the base - //// type because it means the implementation can hold the field as a subtype - //// instance in the implementation and not have to cast it. - - //// If we ever make this property public, we should keep the setter protected. - //protected abstract ContinuationToken CurrentPageToken { get; set; } - public async Task> GetCurrentPageAsync() { IAsyncEnumerator> enumerator = GetAsyncEnumerator(); PageResult current = enumerator.Current; + // Relies on generated enumerator contract if (current == null) { await enumerator.MoveNextAsync().ConfigureAwait(false); @@ -55,33 +49,6 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc } public abstract IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default); - - //protected async Task> GetPageAsync(ContinuationToken pageToken) - //{ - // // This is important to validate against passing a null NextPageToken. - // Argument.AssertNotNull(pageToken, nameof(pageToken)); - - // return await GetPageAsyncCore(pageToken).ConfigureAwait(false); - //} - - //// Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - //public abstract Task> GetPageAsyncCore(ContinuationToken pageToken); - - //async IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) - //{ - // PageResult page = await GetPageAsync(CurrentPageToken).ConfigureAwait(false); - // yield return page; - - // while (page.NextPageToken != null) - // { - // cancellationToken.ThrowIfCancellationRequested(); - - // page = await GetPageAsync(page.NextPageToken).ConfigureAwait(false); - // CurrentPageToken = page.PageToken; - - // yield return page; - // } - //} } #pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 8287a20273ab4..7e2323bd276da 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.ClientModel.Internal; using System.Collections; using System.Collections.Generic; @@ -20,13 +19,6 @@ protected PageCollection() : base() { } - //// Note that this is abstract rather than providing the field in the base - //// type because it means the implementation can hold the field as a subtype - //// instance in the implementation and not have to cast it. - - //// If we ever make this property public, we should keep the setter protected. - //protected abstract ContinuationToken CurrentPageToken { get; set; } - public PageResult GetCurrentPage() { IEnumerator> enumerator = GetEnumerator(); @@ -52,33 +44,9 @@ public IEnumerable GetAllValues() } } - //// TODO: do we need this, and do we need it to be called Core? - //protected PageResult GetPage(ContinuationToken pageToken) - //{ - // Argument.AssertNotNull(pageToken, nameof(pageToken)); - - // return GetPageCore(pageToken); - //} - - //// Doesn't take RequestOptions because RequestOptions cannot be rehydrated. - //protected abstract PageResult GetPageCore(ContinuationToken pageToken); public abstract IEnumerator> GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); - - //IEnumerator> IEnumerable>.GetEnumerator() - //{ - // PageResult page = GetCurrentPage(); - // yield return page; - - // while (page.NextPageToken != null) - // { - // page = GetPage(page.NextPageToken); - // CurrentPageToken = page.PageToken; - - // yield return page; - // } - //} } #pragma warning restore CS1591 From 876846387cce51a447109b043081b06aac652ffc Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 14:59:04 -0700 Subject: [PATCH 22/49] tidy --- sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 7e2323bd276da..c0cdda1a37857 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -24,6 +24,7 @@ public PageResult GetCurrentPage() IEnumerator> enumerator = GetEnumerator(); PageResult current = enumerator.Current; + // Relies on generated enumerator contract if (current == null) { enumerator.MoveNext(); From 3d5ac742269a0927a79cda179eb78f7f2e440dda Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 1 Jul 2024 16:23:00 -0700 Subject: [PATCH 23/49] implement interface explicitly --- .../System.ClientModel/api/System.ClientModel.net6.0.cs | 6 ++++-- .../api/System.ClientModel.netstandard2.0.cs | 6 ++++-- .../src/Convenience/AsyncPageCollectionOfT.cs | 7 +++++-- .../src/Convenience/PageCollectionOfT.cs | 7 +++++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index 2d39e8e3469f6..e6367932fb834 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -17,8 +17,9 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi { protected AsyncPageCollection() { } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + protected abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumeratorCore(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } + System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable { @@ -74,7 +75,8 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } - public abstract System.Collections.Generic.IEnumerator> GetEnumerator(); + protected abstract System.Collections.Generic.IEnumerator> GetEnumeratorCore(); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class PageResult : System.ClientModel.ClientResult diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 424518b43ffbd..11a7ae50c0c7a 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -17,8 +17,9 @@ public abstract partial class AsyncPageCollection : System.Collections.Generi { protected AsyncPageCollection() { } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + protected abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumeratorCore(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } + System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable { @@ -74,7 +75,8 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } - public abstract System.Collections.Generic.IEnumerator> GetEnumerator(); + protected abstract System.Collections.Generic.IEnumerator> GetEnumeratorCore(); + System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } } public partial class PageResult : System.ClientModel.ClientResult diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 42406825f62ac..2119ae549c367 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -22,7 +22,7 @@ protected AsyncPageCollection() : base() public async Task> GetCurrentPageAsync() { - IAsyncEnumerator> enumerator = GetAsyncEnumerator(); + IAsyncEnumerator> enumerator = GetAsyncEnumeratorCore(); PageResult current = enumerator.Current; // Relies on generated enumerator contract @@ -48,7 +48,10 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc } } - public abstract IAsyncEnumerator> GetAsyncEnumerator(CancellationToken cancellationToken = default); + protected abstract IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default); + + IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) + => GetAsyncEnumeratorCore(cancellationToken); } #pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index c0cdda1a37857..921acd389322b 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -21,7 +21,7 @@ protected PageCollection() : base() public PageResult GetCurrentPage() { - IEnumerator> enumerator = GetEnumerator(); + IEnumerator> enumerator = GetEnumeratorCore(); PageResult current = enumerator.Current; // Relies on generated enumerator contract @@ -45,7 +45,10 @@ public IEnumerable GetAllValues() } } - public abstract IEnumerator> GetEnumerator(); + protected abstract IEnumerator> GetEnumeratorCore(); + + IEnumerator> IEnumerable>.GetEnumerator() + => GetEnumeratorCore(); IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); } From cd5aaa5f66ac5b04a2e9bf4935b4da9be50dc4ff Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 2 Jul 2024 15:32:44 -0700 Subject: [PATCH 24/49] reimplement GetAllValues to account for possibility GetCurrentPage was called first --- .../src/Convenience/AsyncPageCollectionOfT.cs | 22 ++++++++++--------- .../src/Convenience/PageCollectionOfT.cs | 19 +++++++++------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 2119ae549c367..9444b5e2e81fc 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.ClientModel.Internal; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; @@ -23,29 +22,32 @@ protected AsyncPageCollection() : base() public async Task> GetCurrentPageAsync() { IAsyncEnumerator> enumerator = GetAsyncEnumeratorCore(); - PageResult current = enumerator.Current; - // Relies on generated enumerator contract - if (current == null) + if (enumerator.Current == null) { await enumerator.MoveNextAsync().ConfigureAwait(false); - current = enumerator.Current; } - return current; + return enumerator.Current!; } public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { - await foreach (PageResult page in this.ConfigureAwait(false).WithCancellation(cancellationToken)) + IAsyncEnumerator> enumerator = GetAsyncEnumeratorCore(cancellationToken); + + do { - foreach (T value in page.Values) + if (enumerator.Current is not null) { - cancellationToken.ThrowIfCancellationRequested(); + foreach (T value in enumerator.Current.Values) + { + cancellationToken.ThrowIfCancellationRequested(); - yield return value; + yield return value; + } } } + while (await enumerator.MoveNextAsync().ConfigureAwait(false)); } protected abstract IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default); diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 921acd389322b..236d997ffd392 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -22,27 +22,30 @@ protected PageCollection() : base() public PageResult GetCurrentPage() { IEnumerator> enumerator = GetEnumeratorCore(); - PageResult current = enumerator.Current; - // Relies on generated enumerator contract - if (current == null) + if (enumerator.Current == null) { enumerator.MoveNext(); - current = enumerator.Current; } - return current; + return enumerator.Current!; } public IEnumerable GetAllValues() { - foreach (PageResult page in this) + IEnumerator> enumerator = GetEnumeratorCore(); + + do { - foreach (T value in page.Values) + if (enumerator.Current is not null) { - yield return value; + foreach (T value in enumerator.Current.Values) + { + yield return value; + } } } + while (enumerator.MoveNext()); } protected abstract IEnumerator> GetEnumeratorCore(); From 91126e36c7f6873d3e224dc8da0c887abe80eeea Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 2 Jul 2024 15:53:38 -0700 Subject: [PATCH 25/49] add internal emitted files that generated code will use --- .../Emitted/PageCollectionHelpers.cs | 67 +++++++++++++ .../TestFramework/Emitted/PageEnumerator.cs | 40 ++++++++ .../Emitted/PageResultEnumerator.cs | 76 +++++++++++++++ .../TestFramework/PageableResultHelpers.cs | 94 ------------------- 4 files changed, 183 insertions(+), 94 deletions(-) create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs delete mode 100644 sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs b/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs new file mode 100644 index 0000000000000..20b5675d70977 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.Collections.Generic; +using System.Threading; + +namespace ClientModel.Tests.Emitted; + +internal class PageCollectionHelpers +{ + public static PageCollection Create(PageEnumerator enumerator) + => new EnumeratorPageCollection(enumerator); + + public static AsyncPageCollection CreateAsync(PageEnumerator enumerator) + => new AsyncEnumeratorPageCollection(enumerator); + + public static IEnumerable Create(PageResultEnumerator enumerator) + { + do + { + if (enumerator.Current is not null) + { + yield return enumerator.Current; + } + } + while (enumerator.MoveNext()); + } + + public static async IAsyncEnumerable CreateAsync(PageResultEnumerator enumerator) + { + do + { + if (enumerator.Current is not null) + { + yield return enumerator.Current; + } + } + while (await enumerator.MoveNextAsync().ConfigureAwait(false)); + } + + private class EnumeratorPageCollection : PageCollection + { + private readonly PageEnumerator _enumerator; + + public EnumeratorPageCollection(PageEnumerator enumerator) + { + _enumerator = enumerator; + } + + protected override IEnumerator> GetEnumeratorCore() + => _enumerator; + } + + private class AsyncEnumeratorPageCollection : AsyncPageCollection + { + private readonly PageEnumerator _enumerator; + + public AsyncEnumeratorPageCollection(PageEnumerator enumerator) + { + _enumerator = enumerator; + } + + protected override IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default) + => _enumerator; + } +} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs b/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs new file mode 100644 index 0000000000000..3559b0d94855f --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.Collections.Generic; + +namespace ClientModel.Tests.Emitted; + +internal abstract class PageEnumerator : PageResultEnumerator, + IAsyncEnumerator>, + IEnumerator> +{ + public abstract PageResult GetPageFromResult(ClientResult result); + + PageResult IEnumerator>.Current + { + get + { + if (Current is null) + { + return default!; + } + + return GetPageFromResult(Current); + } + } + + PageResult IAsyncEnumerator>.Current + { + get + { + if (Current is null) + { + return default!; + } + + return GetPageFromResult(Current); + } + } +} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs new file mode 100644 index 0000000000000..d963168ad7921 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ClientModel.Tests.Emitted; + +internal abstract class PageResultEnumerator : IAsyncEnumerator, IEnumerator +{ + private ClientResult? _current; + private bool _hasNext = true; + + public ClientResult Current => _current!; + + public abstract Task GetFirstAsync(); + + public abstract ClientResult GetFirst(); + + public abstract Task GetNextAsync(ClientResult result); + + public abstract ClientResult GetNext(ClientResult result); + + public abstract bool HasNext(ClientResult result); + + object IEnumerator.Current => ((IEnumerator)this).Current; + + public bool MoveNext() + { + if (!_hasNext) + { + return false; + } + + if (_current == null) + { + _current = GetFirst(); + } + else + { + _current = GetNext(_current); + } + + _hasNext = HasNext(_current); + return true; + } + + void IEnumerator.Reset() => _current = null; + + void IDisposable.Dispose() { } + + public async ValueTask MoveNextAsync() + { + if (!_hasNext) + { + return false; + } + + if (_current == null) + { + _current = await GetFirstAsync().ConfigureAwait(false); + } + else + { + _current = await GetNextAsync(_current).ConfigureAwait(false); + } + + _hasNext = HasNext(_current); + return true; + } + + ValueTask IAsyncDisposable.DisposeAsync() => default; +} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs b/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs deleted file mode 100644 index 0dc4b82be00de..0000000000000 --- a/sdk/core/System.ClientModel/tests/TestFramework/PageableResultHelpers.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.ClientModel; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace ClientModel.Tests.Internal; - -//internal class PageableResultHelpers -//{ -// public static PageableCollection Create(Func> firstPageFunc, Func>? nextPageFunc, int? pageSize = default) where T : notnull -// { -// ResultPage first(string? _, int? pageSizeHint) => firstPageFunc(pageSizeHint); -// return new FuncPageable(first, nextPageFunc, pageSize); -// } - -// public static AsyncPageableCollection Create(Func>> firstPageFunc, Func>>? nextPageFunc, int? pageSize = default) where T : notnull -// { -// Task> first(string? _, int? pageSizeHint) => firstPageFunc(pageSizeHint); -// return new FuncAsyncPageable(first, nextPageFunc, pageSize); -// } - -// private class FuncAsyncPageable : AsyncPageableCollection where T : notnull -// { -// private readonly Func>> _firstPageFunc; -// private readonly Func>>? _nextPageFunc; -// private readonly int? _defaultPageSize; - -// public FuncAsyncPageable(Func>> firstPageFunc, Func>>? nextPageFunc, int? defaultPageSize = default) -// { -// _firstPageFunc = firstPageFunc; -// _nextPageFunc = nextPageFunc; -// _defaultPageSize = defaultPageSize; -// } - -// public override async IAsyncEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default) -// { -// Func>>? pageFunc = string.IsNullOrEmpty(continuationToken) ? _firstPageFunc : _nextPageFunc; - -// if (pageFunc == null) -// { -// yield break; -// } - -// int? pageSize = pageSizeHint ?? _defaultPageSize; -// do -// { -// ResultPage page = await pageFunc(continuationToken, pageSize).ConfigureAwait(false); -// SetRawResponse(page.GetRawResponse()); -// yield return page; -// continuationToken = page.ContinuationToken; -// pageFunc = _nextPageFunc; -// } -// while (!string.IsNullOrEmpty(continuationToken) && pageFunc != null); -// } -// } - -// private class FuncPageable : PageableCollection where T : notnull -// { -// private readonly Func> _firstPageFunc; -// private readonly Func>? _nextPageFunc; -// private readonly int? _defaultPageSize; - -// public FuncPageable(Func> firstPageFunc, Func>? nextPageFunc, int? defaultPageSize = default) -// { -// _firstPageFunc = firstPageFunc; -// _nextPageFunc = nextPageFunc; -// _defaultPageSize = defaultPageSize; -// } - -// public override IEnumerable> AsPages(string? continuationToken = default, int? pageSizeHint = default) -// { -// Func>? pageFunc = string.IsNullOrEmpty(continuationToken) ? _firstPageFunc : _nextPageFunc; - -// if (pageFunc == null) -// { -// yield break; -// } - -// int? pageSize = pageSizeHint ?? _defaultPageSize; -// do -// { -// ResultPage page = pageFunc(continuationToken, pageSize); -// SetRawResponse(page.GetRawResponse()); -// yield return page; -// continuationToken = page.ContinuationToken; -// pageFunc = _nextPageFunc; -// } -// while (!string.IsNullOrEmpty(continuationToken) && pageFunc != null); -// } -// } -//} From fa8b2f47c96853ed489e2c660af9b3996b09fb19 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 2 Jul 2024 16:19:16 -0700 Subject: [PATCH 26/49] Add stubs for mock paging client --- .../tests/Convenience/PageCollectionTests.cs | 16 +++ .../tests/System.ClientModel.Tests.csproj | 4 + .../TestFramework/Mocks/MockPageCollection.cs | 65 --------- .../TestFramework/Mocks/MockPageableClient.cs | 133 ------------------ .../Emitted/PageCollectionHelpers.cs | 2 +- .../PagingClient}/Emitted/PageEnumerator.cs | 2 +- .../Emitted/PageResultEnumerator.cs | 2 +- .../TestClients/PagingClient/PagingClient.cs | 31 ++++ .../TestClients/PagingClient/ValueItem.cs | 15 ++ .../PagingClient/ValuesPageEnumerator.cs | 53 +++++++ .../ValuesPageResultEnumerator.cs | 50 +++++++ .../PagingClient/ValuesPageToken.cs} | 5 +- 12 files changed, 174 insertions(+), 204 deletions(-) delete mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs delete mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs rename sdk/core/System.ClientModel/tests/{TestFramework => client/TestClients/PagingClient}/Emitted/PageCollectionHelpers.cs (97%) rename sdk/core/System.ClientModel/tests/{TestFramework => client/TestClients/PagingClient}/Emitted/PageEnumerator.cs (95%) rename sdk/core/System.ClientModel/tests/{TestFramework => client/TestClients/PagingClient}/Emitted/PageResultEnumerator.cs (97%) create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs rename sdk/core/System.ClientModel/tests/{TestFramework/Mocks/MockPageToken.cs => client/TestClients/PagingClient/ValuesPageToken.cs} (72%) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index ab10dc65d9979..733c3b64b09fd 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -3,13 +3,29 @@ using System.Collections; using System.Collections.Generic; + +using ClientModel.Tests.PagingClient; + using ClientModel.Tests.Mocks; using NUnit.Framework; +using System.ClientModel.Primitives; namespace System.ClientModel.Tests.Results; public class PageCollectionTests { + [Test] + public void CanGetValues() + { + ClientPipelineOptions options = new ClientPipelineOptions() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + ClientPipeline mockPipeline = ClientPipeline.Create(options); + + PagingClient client = new PagingClient(mockPipeline); + } + //[Test] //public void CanEnumeratePages() //{ diff --git a/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj b/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj index 838309afa64f4..636918987ad6f 100644 --- a/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj +++ b/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj @@ -31,4 +31,8 @@ + + + + diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs deleted file mode 100644 index 5ca9ad0ee86a6..0000000000000 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.ClientModel; -using System.ClientModel.Primitives; -using System.Collections.Generic; -using System.Diagnostics; - -namespace ClientModel.Tests.Mocks; - -//public class MockPageCollection : PageCollection -//{ -// private readonly List _values; -// private readonly int _pageSize; -// private readonly List _results; - -// private int _current; -// private int _currentPage; - -// public MockPageCollection(List values, List results, int pageSize) -// { -// Debug.Assert(results.Count % pageSize == 0); -// Debug.Assert(values.Count / pageSize == results.Count); - -// _values = values; -// _results = results; -// _pageSize = pageSize; -// _current = 0; -// } - -// public override ClientToken FirstPageToken -// => BinaryData.FromString(string.Empty); - -// public override PageResult GetPage(ClientToken pageToken, RequestOptions? options = null) -// { -// int currPageSize = Math.Min(_pageSize, _values.Count - _current); - -// List values = _values.GetRange(_current, currPageSize); -// _current += _pageSize; - -// ClientToken? nextPageToken = (_current >= _values.Count) ? -// null : -// new MockPageToken(_current).ToBytes(); - -// PipelineResponse response = _results[_currentPage++].GetRawResponse(); - -// return PageResult.Create(values, pageToken, nextPageToken, response); -// } - -// private class MockPageToken -// { -// public MockPageToken(int index) -// { -// Index = index; -// } - -// public int Index { get; } - -// public BinaryData ToBytes() => BinaryData.FromString($"{Index}"); - -// public static MockPageToken FromBytes(BinaryData data) -// => new(data.ToMemory().Length == 0 ? 0 : int.Parse(data.ToString())); -// } -//} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs deleted file mode 100644 index 9bb7a6008aedb..0000000000000 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageableClient.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.ClientModel; -using System.ClientModel.Primitives; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Azure.Core.TestFramework; -using ClientModel.Tests.Internal; - -namespace ClientModel.Tests.Mocks; - -//public class MockPageableClient -//{ -// public bool ProtocolMethodCalled { get; private set; } -// public int? RequestedPageSize { get; private set; } - -// // mock convenience method - async -// public virtual AsyncPageableCollection GetModelsAsync(string[] pageContents) -// { -// PipelineResponse? lastResponse = default; - -// // The contract for this pageable implementation is that the last seen -// // value id (where the id is StringValue) provides the continuation token -// // for the page. - -// int pageNumber = 0; -// JsonModelList values = new(); - -// async Task> firstPageFuncAsync(int? pageSize) -// { -// ClientResult result = await GetModelsAsync(pageContents[pageNumber++], options: null).ConfigureAwait(false); -// lastResponse = result.GetRawResponse(); -// values = ModelReaderWriter.Read>(lastResponse.Content)!; -// string? continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; -// return ResultPage.Create(values, continuationToken, lastResponse); -// } - -// async Task> nextPageFuncAsync(string? continuationToken, int? pageSize) -// { -// RequestedPageSize = pageSize; - -// bool atRequestedPage = values.Count > 0 && values.Last().StringValue == continuationToken; -// while (!atRequestedPage && pageNumber < pageContents.Length) -// { -// BinaryData content = BinaryData.FromString(pageContents[pageNumber++]); -// JsonModelList pageValues = ModelReaderWriter.Read>(content)!; -// atRequestedPage = pageValues[pageValues.Count - 1].StringValue == continuationToken; -// } - -// Debug.Assert(atRequestedPage is true); - -// ClientResult result = await GetModelsAsync(pageContents[pageNumber++], options: null).ConfigureAwait(false); -// lastResponse = result.GetRawResponse(); -// values = ModelReaderWriter.Read>(lastResponse.Content)!; -// continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; -// return ResultPage.Create(values, continuationToken, lastResponse); -// } - -// return PageableResultHelpers.Create(firstPageFuncAsync, nextPageFuncAsync); -// } - -// // mock convenience method - sync -// public virtual PageableCollection GetModels(string[] pageContents) -// { -// PipelineResponse? lastResponse = default; - -// // The contract for this pageable implementation is that the last seen -// // value id (where the id is StringValue) provides the continuation token -// // for the page. - -// int pageNumber = 0; -// JsonModelList values = new(); - -// ResultPage firstPageFunc(int? pageSize) -// { -// ClientResult result = GetModels(pageContents[pageNumber++], options: null); -// lastResponse = result.GetRawResponse(); -// values = ModelReaderWriter.Read>(lastResponse.Content)!; -// string? continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; -// return ResultPage.Create(values, continuationToken, lastResponse); -// } - -// ResultPage nextPageFunc(string? continuationToken, int? pageSize) -// { -// RequestedPageSize = pageSize; - -// bool atRequestedPage = values.Count > 0 && values.Last().StringValue == continuationToken; -// while (!atRequestedPage && pageNumber < pageContents.Length) -// { -// BinaryData content = BinaryData.FromString(pageContents[pageNumber++]); -// JsonModelList pageValues = ModelReaderWriter.Read>(content)!; -// atRequestedPage = pageValues[pageValues.Count - 1].StringValue == continuationToken; -// } - -// Debug.Assert(atRequestedPage is true); - -// ClientResult result = GetModels(pageContents[pageNumber++], options: null); -// lastResponse = result.GetRawResponse(); -// values = ModelReaderWriter.Read>(lastResponse.Content)!; -// continuationToken = pageNumber < pageContents.Length ? values[values.Count - 1].StringValue : null; -// return ResultPage.Create(values, continuationToken, lastResponse); -// } - -// return PageableResultHelpers.Create(firstPageFunc, nextPageFunc); -// } - -// // mock protocol method - async -// public virtual async Task GetModelsAsync(string pageContent, RequestOptions? options = default) -// { -// await Task.Delay(0); - -// MockPipelineResponse response = new(200); -// response.SetContent(pageContent); - -// ProtocolMethodCalled = true; - -// return ClientResult.FromResponse(response); -// } - -// // mock protocol method - sync -// public virtual ClientResult GetModels(string pageContent, RequestOptions? options = default) -// { -// MockPipelineResponse response = new(200); -// response.SetContent(pageContent); - -// ProtocolMethodCalled = true; - -// return ClientResult.FromResponse(response); -// } -//} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs similarity index 97% rename from sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs rename to sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs index 20b5675d70977..f79e9e2212f1e 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageCollectionHelpers.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading; -namespace ClientModel.Tests.Emitted; +namespace ClientModel.Tests.PagingClient; internal class PageCollectionHelpers { diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs similarity index 95% rename from sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs rename to sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs index 3559b0d94855f..6fb930a576d09 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs @@ -4,7 +4,7 @@ using System.ClientModel; using System.Collections.Generic; -namespace ClientModel.Tests.Emitted; +namespace ClientModel.Tests.PagingClient; internal abstract class PageEnumerator : PageResultEnumerator, IAsyncEnumerator>, diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs similarity index 97% rename from sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs rename to sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs index d963168ad7921..c9b964e4bf2f4 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Emitted/PageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace ClientModel.Tests.Emitted; +namespace ClientModel.Tests.PagingClient; internal abstract class PageResultEnumerator : IAsyncEnumerator, IEnumerator { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs new file mode 100644 index 0000000000000..ad3c2147c1689 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Collections.Generic; + +namespace ClientModel.Tests.PagingClient; + +// A mock client implementation that illustrates paging patterns +public class PagingClient +{ + private readonly ClientPipeline _pipeline; + + public PagingClient(ClientPipeline pipeline) + { + _pipeline = pipeline; + } + + public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) + { + PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline); + return PageCollectionHelpers.CreateAsync(enumerator); + } + + public virtual IEnumerable GetAssistants(RequestOptions options) + { + PageResultEnumerator enumerator = new GetValues(_pipeline); + return PageCollectionHelpers.Create(enumerator); + } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs new file mode 100644 index 0000000000000..435bab5c3038b --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace ClientModel.Tests.PagingClient; + +// A mock model that illustrate values that can be returned in a page collection +public class ValueItem +{ + public ValueItem(string id) + { + Id = id; + } + + public string Id { get; } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs new file mode 100644 index 0000000000000..f494d497bcd6f --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Threading.Tasks; + +namespace ClientModel.Tests.PagingClient; + +internal class ValuesPageEnumerator : PageEnumerator +{ + private readonly ClientPipeline _pipeline; + private readonly RequestOptions _options; + + public ValuesPageEnumerator( + ClientPipeline pipeline, + RequestOptions options) + { + _pipeline = pipeline; + _options = options; + } + + public override PageResult GetPageFromResult(ClientResult result) + { + throw new NotImplementedException(); + } + + public override ClientResult GetFirst() + { + throw new NotImplementedException(); + } + + public override Task GetFirstAsync() + { + throw new NotImplementedException(); + } + + public override ClientResult GetNext(ClientResult result) + { + throw new NotImplementedException(); + } + + public override Task GetNextAsync(ClientResult result) + { + throw new NotImplementedException(); + } + + public override bool HasNext(ClientResult result) + { + throw new NotImplementedException(); + } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs new file mode 100644 index 0000000000000..c8eb411d34993 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Threading.Tasks; + +namespace ClientModel.Tests.PagingClient; + +// Mocks a page result enumerator a client would have for paged endpoints when +// those endpoints only have protocol methods on the client. +internal class ValuesPageResultEnumerator : PageResultEnumerator +{ + private readonly ClientPipeline _pipeline; + private readonly RequestOptions _options; + + public ValuesPageResultEnumerator( + ClientPipeline pipeline, + RequestOptions options) + { + _pipeline = pipeline; + _options = options; + } + + public override ClientResult GetFirst() + { + throw new NotImplementedException(); + } + + public override Task GetFirstAsync() + { + throw new NotImplementedException(); + } + + public override ClientResult GetNext(ClientResult result) + { + throw new NotImplementedException(); + } + + public override Task GetNextAsync(ClientResult result) + { + throw new NotImplementedException(); + } + + public override bool HasNext(ClientResult result) + { + throw new NotImplementedException(); + } +} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs similarity index 72% rename from sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs rename to sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs index 42f779c7adb01..590653a918001 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageToken.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs @@ -8,9 +8,8 @@ using System.Text; using System.Threading.Tasks; -namespace ClientModel.Tests.Mocks; +namespace ClientModel.Tests.PagingClient; -public class MockPageToken : ContinuationToken +public class ValuesPageToken : ContinuationToken { - // TODO } From d9020f22d8a5abbabeb78f6b0b4d1e4d8fc512eb Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 2 Jul 2024 16:33:33 -0700 Subject: [PATCH 27/49] flush out client a bit --- .../tests/Convenience/PageCollectionTests.cs | 2 +- .../PagingClient/Emitted/Argument.cs | 19 ++++++++ .../Emitted/CancellationTokenExtensions.cs | 18 ++++++++ .../TestClients/PagingClient/PagingClient.cs | 46 +++++++++++++++++-- .../PagingClient/PagingProtocolClient.cs | 32 +++++++++++++ .../PagingClient/ValuesPageEnumerator.cs | 8 ++-- .../ValuesPageResultEnumerator.cs | 4 +- .../PagingClient/ValuesPageToken.cs | 21 ++++++++- 8 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 733c3b64b09fd..9f5337381f8c8 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -23,7 +23,7 @@ public void CanGetValues() }; ClientPipeline mockPipeline = ClientPipeline.Create(options); - PagingClient client = new PagingClient(mockPipeline); + PagingProtocolClient client = new PagingProtocolClient(mockPipeline); } //[Test] diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs new file mode 100644 index 0000000000000..e7e41297a8cc2 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ClientModel.Tests.PagingClient; + +internal static class Argument +{ + public static void AssertNotNull(T? value, string name) + { + if (value is null) + { + throw new ArgumentNullException(name); + } + } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs new file mode 100644 index 0000000000000..12f0db0188e6c --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel.Primitives; +using System.Threading; + +namespace ClientModel.Tests.PagingClient; + +internal static class CancellationTokenExtensions +{ + public static RequestOptions ToRequestOptions(this CancellationToken cancellationToken) + { + return new RequestOptions() + { + CancellationToken = cancellationToken + }; + } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs index ad3c2147c1689..d06bc4ab04ae6 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs @@ -4,10 +4,12 @@ using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; +using System.Threading; namespace ClientModel.Tests.PagingClient; -// A mock client implementation that illustrates paging patterns +// A mock client implementation that illustrates paging patterns for client +// endpoints that have both convenience and protocol methods. public class PagingClient { private readonly ClientPipeline _pipeline; @@ -17,15 +19,51 @@ public PagingClient(ClientPipeline pipeline) _pipeline = pipeline; } + public virtual AsyncPageCollection GetValuesAsync(CancellationToken cancellationToken = default) + { + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + return PageCollectionHelpers.CreateAsync(enumerator); + } + + public virtual AsyncPageCollection GetValuesAsync( + ContinuationToken firstPageToken, + CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(firstPageToken, nameof(firstPageToken)); + + ValuesPageToken pageToken = ValuesPageToken.FromToken(firstPageToken); + + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + return PageCollectionHelpers.CreateAsync(enumerator); + } + + public virtual IEnumerable GetValues(CancellationToken cancellationToken = default) + { + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + return PageCollectionHelpers.Create(enumerator); + } + + public virtual PageCollection GetValues( + ContinuationToken firstPageToken, + CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(firstPageToken, nameof(firstPageToken)); + + ValuesPageToken pageToken = ValuesPageToken.FromToken(firstPageToken); + + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + return PageCollectionHelpers.Create(enumerator); + } + public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline); + PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, options); return PageCollectionHelpers.CreateAsync(enumerator); } - public virtual IEnumerable GetAssistants(RequestOptions options) + public virtual IEnumerable GetValues(RequestOptions options) { - PageResultEnumerator enumerator = new GetValues(_pipeline); + PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, options); return PageCollectionHelpers.Create(enumerator); } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs new file mode 100644 index 0000000000000..a865c4953a2d9 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Collections.Generic; + +namespace ClientModel.Tests.PagingClient; + +// A mock client implementation that illustrates paging patterns for client +// endpoints that only have protocol methods. +public class PagingProtocolClient +{ + private readonly ClientPipeline _pipeline; + + public PagingProtocolClient(ClientPipeline pipeline) + { + _pipeline = pipeline; + } + + public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) + { + PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, options); + return PageCollectionHelpers.CreateAsync(enumerator); + } + + public virtual IEnumerable GetValues(RequestOptions options) + { + PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, options); + return PageCollectionHelpers.Create(enumerator); + } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs index f494d497bcd6f..9fa361c331929 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -8,20 +8,18 @@ namespace ClientModel.Tests.PagingClient; -internal class ValuesPageEnumerator : PageEnumerator +internal class ValuesPageEnumerator : PageEnumerator { private readonly ClientPipeline _pipeline; private readonly RequestOptions _options; - public ValuesPageEnumerator( - ClientPipeline pipeline, - RequestOptions options) + public ValuesPageEnumerator(ClientPipeline pipeline, RequestOptions options) { _pipeline = pipeline; _options = options; } - public override PageResult GetPageFromResult(ClientResult result) + public override PageResult GetPageFromResult(ClientResult result) { throw new NotImplementedException(); } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index c8eb411d34993..f0388f39151f3 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -15,9 +15,7 @@ internal class ValuesPageResultEnumerator : PageResultEnumerator private readonly ClientPipeline _pipeline; private readonly RequestOptions _options; - public ValuesPageResultEnumerator( - ClientPipeline pipeline, - RequestOptions options) + public ValuesPageResultEnumerator(ClientPipeline pipeline, RequestOptions options) { _pipeline = pipeline; _options = options; diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs index 590653a918001..00398bb3b1e04 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs @@ -10,6 +10,25 @@ namespace ClientModel.Tests.PagingClient; -public class ValuesPageToken : ContinuationToken +internal class ValuesPageToken : ContinuationToken { + public override BinaryData ToBytes() + { + throw new NotImplementedException(); + } + + public ValuesPageToken? GetNextPageToken() + { + throw new NotImplementedException(); + } + + public static ValuesPageToken FromToken(ContinuationToken pageToken) + { + throw new NotImplementedException(); + } + + public static ValuesPageToken FromOptions() + { + throw new NotImplementedException(); + } } From ba5ea50776694467ce8a16e5d5cb5ee17be4d30e Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 2 Jul 2024 16:45:27 -0700 Subject: [PATCH 28/49] updates --- .../tests/Convenience/PageCollectionTests.cs | 11 ++++------ .../TestClients/PagingClient/PagingClient.cs | 21 ++++++++++--------- .../PagingClient/PagingClientOptions.cs | 12 +++++++++++ .../PagingClient/PagingProtocolClient.cs | 11 ++++++---- .../TestClients/PagingClient/ValueItem.cs | 6 ++++-- .../PagingClient/ValuesPageEnumerator.cs | 8 ++++++- .../ValuesPageResultEnumerator.cs | 8 ++++++- 7 files changed, 52 insertions(+), 25 deletions(-) create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 9f5337381f8c8..6bfcf600b2059 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -1,14 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.ClientModel.Primitives; using System.Collections; using System.Collections.Generic; - -using ClientModel.Tests.PagingClient; - using ClientModel.Tests.Mocks; +using ClientModel.Tests.PagingClient; using NUnit.Framework; -using System.ClientModel.Primitives; namespace System.ClientModel.Tests.Results; @@ -17,13 +15,12 @@ public class PageCollectionTests [Test] public void CanGetValues() { - ClientPipelineOptions options = new ClientPipelineOptions() + PagingClientOptions options = new() { Transport = new MockPipelineTransport("Mock", i => 200) }; - ClientPipeline mockPipeline = ClientPipeline.Create(options); - PagingProtocolClient client = new PagingProtocolClient(mockPipeline); + PagingClient client = new PagingClient(options); } //[Test] diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs index d06bc4ab04ae6..e23d1e59f5280 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; @@ -13,15 +14,17 @@ namespace ClientModel.Tests.PagingClient; public class PagingClient { private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; - public PagingClient(ClientPipeline pipeline) + public PagingClient(PagingClientOptions options) { - _pipeline = pipeline; + _pipeline = ClientPipeline.Create(options); + _endpoint = new Uri("https://www.paging.com"); } public virtual AsyncPageCollection GetValuesAsync(CancellationToken cancellationToken = default) { - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); return PageCollectionHelpers.CreateAsync(enumerator); } @@ -32,14 +35,13 @@ public virtual AsyncPageCollection GetValuesAsync( Argument.AssertNotNull(firstPageToken, nameof(firstPageToken)); ValuesPageToken pageToken = ValuesPageToken.FromToken(firstPageToken); - - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); return PageCollectionHelpers.CreateAsync(enumerator); } public virtual IEnumerable GetValues(CancellationToken cancellationToken = default) { - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); return PageCollectionHelpers.Create(enumerator); } @@ -50,20 +52,19 @@ public virtual PageCollection GetValues( Argument.AssertNotNull(firstPageToken, nameof(firstPageToken)); ValuesPageToken pageToken = ValuesPageToken.FromToken(firstPageToken); - - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, cancellationToken.ToRequestOptions()); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); return PageCollectionHelpers.Create(enumerator); } public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, options); + PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, options); return PageCollectionHelpers.CreateAsync(enumerator); } public virtual IEnumerable GetValues(RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, options); + PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, options); return PageCollectionHelpers.Create(enumerator); } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs new file mode 100644 index 0000000000000..55bdf7634629b --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.ClientModel.Primitives; +using System.Collections.Generic; + +namespace ClientModel.Tests.PagingClient; + +public class PagingClientOptions : ClientPipelineOptions +{ +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs index a865c4953a2d9..44d717999acf5 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; @@ -12,21 +13,23 @@ namespace ClientModel.Tests.PagingClient; public class PagingProtocolClient { private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; - public PagingProtocolClient(ClientPipeline pipeline) + public PagingProtocolClient(PagingClientOptions options) { - _pipeline = pipeline; + _pipeline = ClientPipeline.Create(options); + _endpoint = new Uri("https://www.paging.com"); } public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, options); + PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, _endpoint, options); return PageCollectionHelpers.CreateAsync(enumerator); } public virtual IEnumerable GetValues(RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, options); + PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, _endpoint, options); return PageCollectionHelpers.Create(enumerator); } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs index 435bab5c3038b..46b37cd85a69d 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs @@ -6,10 +6,12 @@ namespace ClientModel.Tests.PagingClient; // A mock model that illustrate values that can be returned in a page collection public class ValueItem { - public ValueItem(string id) + public ValueItem(int id, string value) { Id = id; + Value = value; } - public string Id { get; } + public int Id { get; } + public string Value { get; } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs index 9fa361c331929..475a282a84b82 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -11,11 +11,17 @@ namespace ClientModel.Tests.PagingClient; internal class ValuesPageEnumerator : PageEnumerator { private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; private readonly RequestOptions _options; - public ValuesPageEnumerator(ClientPipeline pipeline, RequestOptions options) + public ValuesPageEnumerator( + ClientPipeline pipeline, + Uri endpoint, + RequestOptions options) { _pipeline = pipeline; + _endpoint = endpoint; + _options = options; } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index f0388f39151f3..1a68ca9abcb8a 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -13,11 +13,17 @@ namespace ClientModel.Tests.PagingClient; internal class ValuesPageResultEnumerator : PageResultEnumerator { private readonly ClientPipeline _pipeline; + private readonly Uri _endpoint; private readonly RequestOptions _options; - public ValuesPageResultEnumerator(ClientPipeline pipeline, RequestOptions options) + public ValuesPageResultEnumerator( + ClientPipeline pipeline, + Uri endpoint, + RequestOptions options) { _pipeline = pipeline; + _endpoint = endpoint; + _options = options; } From 9bee2fe3649888b74efe14be1c8f825352a9ebb7 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Tue, 2 Jul 2024 17:28:05 -0700 Subject: [PATCH 29/49] Backup of mock client WIP --- .../PagingClient/MockData/MockData.cs | 24 +++++++++ .../MockData/MockValueItemPageResponse.cs | 50 +++++++++++++++++++ .../MockData/MockValueItemResponse.cs | 41 +++++++++++++++ .../PagingClient/PagingProtocolClient.cs | 28 +++++++++-- .../TestClients/PagingClient/ValueItem.cs | 5 ++ .../ValuesPageResultEnumerator.cs | 36 ++++++++++++- 6 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs new file mode 100644 index 0000000000000..08206a9f6a126 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel; +using System.Collections.Generic; +using System.Text; + +namespace ClientModel.Tests.PagingClient; + +internal class MockData +{ + private const int Count = 16; + + public static IEnumerable GetValues() + { + for (int i = 0; i < Count; i++) + { + yield return new ValueItem(i, $"{i}"); + } + } + + public static ClientResult GetPageResult(IEnumerable values) + => ClientResult.FromResponse(new MockValueItemPageResponse(values)); +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs new file mode 100644 index 0000000000000..980ee4ab9e27f --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ClientModel.Tests.PagingClient; + +internal class MockValueItemPageResponse : PipelineResponse +{ + public MockValueItemPageResponse(IEnumerable values) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("{["); + foreach (ValueItem value in values) + { + sb.AppendLine(value.ToJson()); + } + sb.AppendLine("]}"); + + Content = BinaryData.FromString(sb.ToString()); + } + + public override int Status => 200; + + public override string ReasonPhrase => "OK"; + + public override Stream? ContentStream + { + get => null; + set => throw new NotImplementedException(); + } + + public override BinaryData Content { get; } + + protected override PipelineResponseHeaders HeadersCore => throw new NotImplementedException(); + + public override BinaryData BufferContent(CancellationToken cancellationToken = default) + => Content; + + public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) + => new(Content); + + public override void Dispose() { } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs new file mode 100644 index 0000000000000..296d81540725d --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel.Primitives; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClientModel.Tests.PagingClient; + +internal class MockValueItemResponse : PipelineResponse +{ + public MockValueItemResponse(int id, string value) + { + string json = $"{{ \"id\" = {id}, \"value\"={value} }}"; + Content = BinaryData.FromString(json); + } + + public override int Status => 200; + + public override string ReasonPhrase => "OK"; + + public override Stream? ContentStream + { + get => null; + set => throw new NotImplementedException(); + } + + public override BinaryData Content { get; } + + protected override PipelineResponseHeaders HeadersCore => throw new NotImplementedException(); + + public override BinaryData BufferContent(CancellationToken cancellationToken = default) + => Content; + + public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) + => new(Content); + + public override void Dispose() { } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs index 44d717999acf5..1941815652200 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs @@ -21,15 +21,35 @@ public PagingProtocolClient(PagingClientOptions options) _endpoint = new Uri("https://www.paging.com"); } - public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) + public virtual IAsyncEnumerable GetValuesAsync( + string? order, + int? pageSize, + int? offset, + RequestOptions? options = default) { - PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, _endpoint, options); + PageResultEnumerator enumerator = new ValuesPageResultEnumerator( + _pipeline, + _endpoint, + order, + pageSize, + offset, + options); return PageCollectionHelpers.CreateAsync(enumerator); } - public virtual IEnumerable GetValues(RequestOptions options) + public virtual IEnumerable GetValues( + string? order, + int? pageSize, + int? offset, + RequestOptions? options = default) { - PageResultEnumerator enumerator = new ValuesPageResultEnumerator(_pipeline, _endpoint, options); + PageResultEnumerator enumerator = new ValuesPageResultEnumerator( + _pipeline, + _endpoint, + order, + pageSize, + offset, + options); return PageCollectionHelpers.Create(enumerator); } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs index 46b37cd85a69d..7b9c65ea93c37 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; + namespace ClientModel.Tests.PagingClient; // A mock model that illustrate values that can be returned in a page collection @@ -14,4 +16,7 @@ public ValueItem(int id, string value) public int Id { get; } public string Value { get; } + + public string ToJson() + => $"{{ \"id\" = {Id}, \"value\"= {Value} }}"; } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index 1a68ca9abcb8a..67911522f1d1d 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -4,6 +4,8 @@ using System; using System.ClientModel; using System.ClientModel.Primitives; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace ClientModel.Tests.PagingClient; @@ -14,16 +16,28 @@ internal class ValuesPageResultEnumerator : PageResultEnumerator { private readonly ClientPipeline _pipeline; private readonly Uri _endpoint; - private readonly RequestOptions _options; + + private readonly string? _order; + private readonly int? _pageSize; + private readonly int? _offset; + + private readonly RequestOptions? _options; public ValuesPageResultEnumerator( ClientPipeline pipeline, Uri endpoint, - RequestOptions options) + string? order, + int? pageSize, + int? offset, + RequestOptions? options) { _pipeline = pipeline; _endpoint = endpoint; + _order = order; + _pageSize = pageSize; + _offset = offset; + _options = options; } @@ -51,4 +65,22 @@ public override bool HasNext(ClientResult result) { throw new NotImplementedException(); } + + internal virtual ClientResult GetValuesPage( + string? order, + int? pageSize, + int? offset, + RequestOptions? options = default) + { + order ??= "asc"; + pageSize ??= 8; + offset ??= 0; + + IEnumerable ordered = order == "asc" ? + MockData.GetValues() : + MockData.GetValues().Reverse(); + IEnumerable skipped = ordered.Skip(offset.Value); + IEnumerable page = skipped.Take(pageSize.Value); + return MockData.GetPageResult(page); + } } From 3ca6e240610c565b9beaa9acf7aee0d96223f2b5 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 09:29:25 -0700 Subject: [PATCH 30/49] rework/rename and add tests prior to implementation --- .../tests/Convenience/PageCollectionTests.cs | 300 +++++++++++++++++- .../tests/System.ClientModel.Tests.csproj | 4 - .../{MockData.cs => MockPagingData.cs} | 8 +- .../TestClients/PagingClient/PagingClient.cs | 24 +- .../ValuesPageResultEnumerator.cs | 6 +- 5 files changed, 326 insertions(+), 16 deletions(-) rename sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/{MockData.cs => MockPagingData.cs} (74%) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 6bfcf600b2059..4f6181d004554 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -4,23 +4,319 @@ using System.ClientModel.Primitives; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using ClientModel.Tests; using ClientModel.Tests.Mocks; using ClientModel.Tests.PagingClient; using NUnit.Framework; namespace System.ClientModel.Tests.Results; -public class PageCollectionTests +public class PageCollectionTests : SyncAsyncTestBase { + public PageCollectionTests(bool isAsync) : base(isAsync) + { + } + + [Test] + public void CanGetAllValues() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public async Task CanGetAllValueAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + AsyncPageCollection pages = client.GetValuesAsync(); + IAsyncEnumerable values = pages.GetAllValuesAsync(); + + int count = 0; + await foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanGetCurrentPage() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + PageResult page = pages.GetCurrentPage(); + + Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0].Id); + } + + // TODO: Async + + [Test] + public void CanGetCurrentPageThenAllItems() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + + PageResult page = pages.GetCurrentPage(); + + Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0].Id); + + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanGetCurrentPageWhileEnumeratingItems() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + + PageResult page = pages.GetCurrentPage(); + + // Validate that the current item is in range of the page values + Assert.GreaterOrEqual(value.Id, page.Values[0].Id); + Assert.LessOrEqual(value.Id, page.Values[page.Values.Count - 1].Id); + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanRehydratePageCollection() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + List allValues = pages.GetAllValues().ToList(); + List allRehydratedValues = rehydratedPages.GetAllValues().ToList(); + + for (int i = 0; i < allValues.Count; i++) + { + Assert.AreEqual(allValues[i].Id, allRehydratedValues[i].Id); + } + } + + [Test] + public void CanCastToConvenienceFromProtocol() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + + // Call the protocol method on the convenience client. + IEnumerable pageResults = client.GetValues( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + // Cast to convience type from protocol return value. + PageCollection pages = (PageCollection)pageResults; + + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanReorderItemsAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + string order = "desc"; + Assert.AreNotEqual(MockPagingData.DefaultOrder, order); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(order: order); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + // We got the last one first from both pages + Assert.AreEqual(MockPagingData.Count - 1, page.Values[0].Id); + Assert.AreEqual(MockPagingData.Count - 1, rehydratedPage.Values[0].Id); + } + [Test] - public void CanGetValues() + public void CanChangePageSizeAndRehydrate() { PagingClientOptions options = new() { Transport = new MockPipelineTransport("Mock", i => 200) }; + int pageSize = 4; + Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(pageSize: pageSize); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + // Both pages have same non-default page size + Assert.AreEqual(pageSize, page.Values.Count); + Assert.AreEqual(pageSize, rehydratedPage.Values.Count); + } + + [Test] + public void CanSkipItemsAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + int offset = 4; + Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(offset: offset); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + // Both pages have the same non-default offset value + Assert.AreEqual(offset - 1, page.Values[0].Id); + Assert.AreEqual(offset - 1, rehydratedPage.Values[0].Id); + } + + [Test] + public void CanChangeAllCollectionParametersAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + string order = "desc"; + Assert.AreNotEqual(MockPagingData.DefaultOrder, order); + + int pageSize = 4; + Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); + + int offset = 4; + Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(pageSize: pageSize); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + // Both page collections and first page are the same on each dimension + + // Same number of pages in the two collections + Assert.AreEqual(3, pages.Count()); + Assert.AreEqual(3, rehydratedPages.Count()); + + // Last one first and same items skipped + Assert.AreEqual(11, page.Values[0].Id); + Assert.AreEqual(11, rehydratedPage.Values[0].Id); + + // Equal page size + Assert.AreEqual(pageSize, page.Values.Count); + Assert.AreEqual(pageSize, rehydratedPage.Values.Count); } //[Test] diff --git a/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj b/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj index 636918987ad6f..838309afa64f4 100644 --- a/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj +++ b/sdk/core/System.ClientModel/tests/System.ClientModel.Tests.csproj @@ -31,8 +31,4 @@ - - - - diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs similarity index 74% rename from sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs rename to sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs index 08206a9f6a126..fbd7a2e81bab4 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockData.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs @@ -3,13 +3,15 @@ using System.ClientModel; using System.Collections.Generic; -using System.Text; namespace ClientModel.Tests.PagingClient; -internal class MockData +public class MockPagingData { - private const int Count = 16; + public const int Count = 16; + public const int DefaultPageSize = 8; + public const int DefaultOffset = 0; + public const string DefaultOrder = "asc"; public static IEnumerable GetValues() { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs index e23d1e59f5280..af9d93ae7aefc 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs @@ -22,7 +22,11 @@ public PagingClient(PagingClientOptions options) _endpoint = new Uri("https://www.paging.com"); } - public virtual AsyncPageCollection GetValuesAsync(CancellationToken cancellationToken = default) + public virtual AsyncPageCollection GetValuesAsync( + string? order = default, + int? pageSize = default, + int? offset = default, + CancellationToken cancellationToken = default) { ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); return PageCollectionHelpers.CreateAsync(enumerator); @@ -39,7 +43,11 @@ public virtual AsyncPageCollection GetValuesAsync( return PageCollectionHelpers.CreateAsync(enumerator); } - public virtual IEnumerable GetValues(CancellationToken cancellationToken = default) + public virtual PageCollection GetValues( + string? order = default, + int? pageSize = default, + int? offset = default, + CancellationToken cancellationToken = default) { ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); return PageCollectionHelpers.Create(enumerator); @@ -56,13 +64,21 @@ public virtual PageCollection GetValues( return PageCollectionHelpers.Create(enumerator); } - public virtual IAsyncEnumerable GetValuesAsync(RequestOptions options) + public virtual IAsyncEnumerable GetValuesAsync( + string? order, + int? pageSize, + int? offset, + RequestOptions options) { PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, options); return PageCollectionHelpers.CreateAsync(enumerator); } - public virtual IEnumerable GetValues(RequestOptions options) + public virtual IEnumerable GetValues( + string? order, + int? pageSize, + int? offset, + RequestOptions options) { PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, options); return PageCollectionHelpers.Create(enumerator); diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index 67911522f1d1d..a1c47c9514c45 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -77,10 +77,10 @@ internal virtual ClientResult GetValuesPage( offset ??= 0; IEnumerable ordered = order == "asc" ? - MockData.GetValues() : - MockData.GetValues().Reverse(); + MockPagingData.GetValues() : + MockPagingData.GetValues().Reverse(); IEnumerable skipped = ordered.Skip(offset.Value); IEnumerable page = skipped.Take(pageSize.Value); - return MockData.GetPageResult(page); + return MockPagingData.GetPageResult(page); } } From d9f743adcc3591d62b7023baa84c07155a2bbe41 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 10:35:55 -0700 Subject: [PATCH 31/49] make first test pass --- .../tests/Convenience/PageCollectionTests.cs | 10 +- .../PagingClient/MockData/MockPagingData.cs | 22 +++- .../MockData/MockValueItemPageResponse.cs | 12 +- .../MockData/MockValueItemResponse.cs | 41 ------ .../TestClients/PagingClient/PagingClient.cs | 52 ++++++-- .../TestClients/PagingClient/ValueItem.cs | 12 +- .../TestClients/PagingClient/ValueItemPage.cs | 34 +++++ .../PagingClient/ValuesPageEnumerator.cs | 66 +++++++++- .../ValuesPageResultEnumerator.cs | 29 +++-- .../PagingClient/ValuesPageToken.cs | 119 ++++++++++++++++-- 10 files changed, 301 insertions(+), 96 deletions(-) delete mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs create mode 100644 sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 4f6181d004554..ea81e1b9621aa 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -4,22 +4,16 @@ using System.ClientModel.Primitives; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; -using ClientModel.Tests; using ClientModel.Tests.Mocks; using ClientModel.Tests.PagingClient; using NUnit.Framework; -namespace System.ClientModel.Tests.Results; +namespace System.ClientModel.Tests.Paging; -public class PageCollectionTests : SyncAsyncTestBase +public class PageCollectionTests { - public PageCollectionTests(bool isAsync) : base(isAsync) - { - } - [Test] public void CanGetAllValues() { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs index fbd7a2e81bab4..39f798958a069 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs @@ -3,15 +3,17 @@ using System.ClientModel; using System.Collections.Generic; +using System.Linq; namespace ClientModel.Tests.PagingClient; public class MockPagingData { public const int Count = 16; + + public const string DefaultOrder = "asc"; public const int DefaultPageSize = 8; public const int DefaultOffset = 0; - public const string DefaultOrder = "asc"; public static IEnumerable GetValues() { @@ -21,6 +23,24 @@ public static IEnumerable GetValues() } } + public static IEnumerable GetValues( + string? order, + int? pageSize, + int? offset) + { + order ??= DefaultOrder; + pageSize ??= DefaultPageSize; + offset ??= DefaultOffset; + + IEnumerable ordered = order == "asc" ? + GetValues() : + GetValues().Reverse(); + IEnumerable skipped = ordered.Skip(offset.Value); + IEnumerable page = skipped.Take(pageSize.Value); + + return page; + } + public static ClientResult GetPageResult(IEnumerable values) => ClientResult.FromResponse(new MockValueItemPageResponse(values)); } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs index 980ee4ab9e27f..8144dd24de512 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs @@ -5,6 +5,7 @@ using System.ClientModel.Primitives; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -16,12 +17,19 @@ internal class MockValueItemPageResponse : PipelineResponse public MockValueItemPageResponse(IEnumerable values) { StringBuilder sb = new StringBuilder(); - sb.AppendLine("{["); + sb.AppendLine("["); + + int count = 0; foreach (ValueItem value in values) { sb.AppendLine(value.ToJson()); + + if (++count != values.Count()) + { + sb.AppendLine(","); + } } - sb.AppendLine("]}"); + sb.AppendLine("]"); Content = BinaryData.FromString(sb.ToString()); } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs deleted file mode 100644 index 296d81540725d..0000000000000 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemResponse.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.ClientModel.Primitives; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace ClientModel.Tests.PagingClient; - -internal class MockValueItemResponse : PipelineResponse -{ - public MockValueItemResponse(int id, string value) - { - string json = $"{{ \"id\" = {id}, \"value\"={value} }}"; - Content = BinaryData.FromString(json); - } - - public override int Status => 200; - - public override string ReasonPhrase => "OK"; - - public override Stream? ContentStream - { - get => null; - set => throw new NotImplementedException(); - } - - public override BinaryData Content { get; } - - protected override PipelineResponseHeaders HeadersCore => throw new NotImplementedException(); - - public override BinaryData BufferContent(CancellationToken cancellationToken = default) - => Content; - - public override ValueTask BufferContentAsync(CancellationToken cancellationToken = default) - => new(Content); - - public override void Dispose() { } -} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs index af9d93ae7aefc..2f3dc91a232f7 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs @@ -28,7 +28,13 @@ public virtual AsyncPageCollection GetValuesAsync( int? offset = default, CancellationToken cancellationToken = default) { - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator( + _pipeline, + _endpoint, + order: order, + pageSize: pageSize, + offset: offset, + cancellationToken.ToRequestOptions()); return PageCollectionHelpers.CreateAsync(enumerator); } @@ -38,8 +44,14 @@ public virtual AsyncPageCollection GetValuesAsync( { Argument.AssertNotNull(firstPageToken, nameof(firstPageToken)); - ValuesPageToken pageToken = ValuesPageToken.FromToken(firstPageToken); - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); + ValuesPageToken token = ValuesPageToken.FromToken(firstPageToken); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator( + _pipeline, + _endpoint, + token.Order, + token.PageSize, + token.Offset, + cancellationToken.ToRequestOptions()); return PageCollectionHelpers.CreateAsync(enumerator); } @@ -49,7 +61,13 @@ public virtual PageCollection GetValues( int? offset = default, CancellationToken cancellationToken = default) { - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator( + _pipeline, + _endpoint, + order: order, + pageSize: pageSize, + offset: offset, + cancellationToken.ToRequestOptions()); return PageCollectionHelpers.Create(enumerator); } @@ -59,8 +77,14 @@ public virtual PageCollection GetValues( { Argument.AssertNotNull(firstPageToken, nameof(firstPageToken)); - ValuesPageToken pageToken = ValuesPageToken.FromToken(firstPageToken); - ValuesPageEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, cancellationToken.ToRequestOptions()); + ValuesPageToken token = ValuesPageToken.FromToken(firstPageToken); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator( + _pipeline, + _endpoint, + token.Order, + token.PageSize, + token.Offset, + cancellationToken.ToRequestOptions()); return PageCollectionHelpers.Create(enumerator); } @@ -70,7 +94,13 @@ public virtual IAsyncEnumerable GetValuesAsync( int? offset, RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, options); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator( + _pipeline, + _endpoint, + order: order, + pageSize: pageSize, + offset: offset, + options); return PageCollectionHelpers.CreateAsync(enumerator); } @@ -80,7 +110,13 @@ public virtual IEnumerable GetValues( int? offset, RequestOptions options) { - PageResultEnumerator enumerator = new ValuesPageEnumerator(_pipeline, _endpoint, options); + ValuesPageEnumerator enumerator = new ValuesPageEnumerator( + _pipeline, + _endpoint, + order: order, + pageSize: pageSize, + offset: offset, + options); return PageCollectionHelpers.Create(enumerator); } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs index 7b9c65ea93c37..f4293d507ef2e 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System; +using System.Text.Json; namespace ClientModel.Tests.PagingClient; @@ -17,6 +17,12 @@ public ValueItem(int id, string value) public int Id { get; } public string Value { get; } - public string ToJson() - => $"{{ \"id\" = {Id}, \"value\"= {Value} }}"; + public string ToJson() => $"{{ \"id\" : {Id}, \"value\" : \"{Value}\" }}"; + + public static ValueItem FromJson(JsonElement element) + { + int id = element.GetProperty("id").GetInt32(); + string value = element.GetProperty("value").GetString()!; + return new ValueItem(id, value); + } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs new file mode 100644 index 0000000000000..a265e9676730f --- /dev/null +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace ClientModel.Tests.PagingClient; + +// In a real client, this type would be generated but would be made internal. +// It corresponds to the REST API definition of the response that comes back +// with a list of items in a page. +internal class ValueItemPage +{ + protected ValueItemPage(List values) + { + Values = values; + } + + public IReadOnlyList Values { get; set; } + + public static ValueItemPage FromJson(BinaryData json) + { + List items = new(); + + using JsonDocument doc = JsonDocument.Parse(json); + foreach (JsonElement element in doc.RootElement.EnumerateArray()) + { + items.Add(ValueItem.FromJson(element)); + } + + return new ValueItemPage(items); + } +} diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs index 475a282a84b82..07fe6910b002c 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -4,6 +4,7 @@ using System; using System.ClientModel; using System.ClientModel.Primitives; +using System.Collections.Generic; using System.Threading.Tasks; namespace ClientModel.Tests.PagingClient; @@ -12,27 +13,54 @@ internal class ValuesPageEnumerator : PageEnumerator { private readonly ClientPipeline _pipeline; private readonly Uri _endpoint; - private readonly RequestOptions _options; + + private readonly string? _order; + private readonly int? _pageSize; + + // This one is special - it keeps track of which page we're on. + private int? _offset; + + // We need two offsets to be able to create both page tokens. + private int _nextOffset; + + private readonly RequestOptions? _options; public ValuesPageEnumerator( ClientPipeline pipeline, Uri endpoint, - RequestOptions options) + string? order, + int? pageSize, + int? offset, + RequestOptions? options) { _pipeline = pipeline; _endpoint = endpoint; + _order = order; + _pageSize = pageSize; + _offset = offset; + _options = options; } public override PageResult GetPageFromResult(ClientResult result) { - throw new NotImplementedException(); + PipelineResponse response = result.GetRawResponse(); + ValueItemPage pageModel = ValueItemPage.FromJson(response.Content); + + ValuesPageToken pageToken = ValuesPageToken.FromOptions(_order, _pageSize, _offset); + ValuesPageToken? nextPageToken = pageToken.GetNextPageToken(_nextOffset, MockPagingData.Count); + + return PageResult.Create(pageModel.Values, pageToken, nextPageToken, response); } public override ClientResult GetFirst() { - throw new NotImplementedException(); + ClientResult result = GetValuesPage(_order, _pageSize, _offset); + + _nextOffset = GetNextOffset(_offset, _pageSize); + + return result; } public override Task GetFirstAsync() @@ -42,7 +70,13 @@ public override Task GetFirstAsync() public override ClientResult GetNext(ClientResult result) { - throw new NotImplementedException(); + _offset = _nextOffset; + + ClientResult pageResult = GetValuesPage(_order, _pageSize, _offset); + + _nextOffset = GetNextOffset(_offset, _pageSize); + + return pageResult; } public override Task GetNextAsync(ClientResult result) @@ -52,6 +86,26 @@ public override Task GetNextAsync(ClientResult result) public override bool HasNext(ClientResult result) { - throw new NotImplementedException(); + return _nextOffset < MockPagingData.Count; + } + + // In a real client implementation, thes would be the generated protocol + // method used to obtain a page of items. + internal virtual ClientResult GetValuesPage( + string? order, + int? pageSize, + int? offset, + RequestOptions? options = default) + { + IEnumerable values = MockPagingData.GetValues(order, pageSize, offset); + return MockPagingData.GetPageResult(values); + } + + // This helper method is specific to this mock enumerator implementation + private static int GetNextOffset(int? offset, int? pageSize) + { + offset ??= MockPagingData.DefaultOffset; + pageSize ??= MockPagingData.DefaultPageSize; + return offset.Value + pageSize.Value; } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index a1c47c9514c45..ed73c2c3820ec 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -5,7 +5,6 @@ using System.ClientModel; using System.ClientModel.Primitives; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace ClientModel.Tests.PagingClient; @@ -19,7 +18,9 @@ internal class ValuesPageResultEnumerator : PageResultEnumerator private readonly string? _order; private readonly int? _pageSize; - private readonly int? _offset; + + // This one is special - it keep track of which page we're on. + private int? _offset; private readonly RequestOptions? _options; @@ -43,7 +44,9 @@ public ValuesPageResultEnumerator( public override ClientResult GetFirst() { - throw new NotImplementedException(); + ClientResult result = GetValuesPage(_order, _pageSize, _offset); + _offset += _pageSize; + return result; } public override Task GetFirstAsync() @@ -53,7 +56,9 @@ public override Task GetFirstAsync() public override ClientResult GetNext(ClientResult result) { - throw new NotImplementedException(); + ClientResult pageResult = GetValuesPage(_order, _pageSize, _offset); + _offset += _pageSize; + return pageResult; } public override Task GetNextAsync(ClientResult result) @@ -63,24 +68,18 @@ public override Task GetNextAsync(ClientResult result) public override bool HasNext(ClientResult result) { - throw new NotImplementedException(); + return _offset < MockPagingData.Count; } + // In a real client implementation, thes would be the generated protocol + // method used to obtain a page of items. internal virtual ClientResult GetValuesPage( string? order, int? pageSize, int? offset, RequestOptions? options = default) { - order ??= "asc"; - pageSize ??= 8; - offset ??= 0; - - IEnumerable ordered = order == "asc" ? - MockPagingData.GetValues() : - MockPagingData.GetValues().Reverse(); - IEnumerable skipped = ordered.Skip(offset.Value); - IEnumerable page = skipped.Take(pageSize.Value); - return MockPagingData.GetPageResult(page); + IEnumerable values = MockPagingData.GetValues(order, pageSize, offset); + return MockPagingData.GetPageResult(values); } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs index 00398bb3b1e04..7ffdee3db912d 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs @@ -3,32 +3,127 @@ using System; using System.ClientModel; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Diagnostics; +using System.IO; +using System.Text.Json; namespace ClientModel.Tests.PagingClient; internal class ValuesPageToken : ContinuationToken { - public override BinaryData ToBytes() + protected ValuesPageToken(string? order, int? pageSize, int? offset) { - throw new NotImplementedException(); + Order = order; + PageSize = pageSize; + Offset = offset; } - public ValuesPageToken? GetNextPageToken() + public string? Order { get; } + public int? PageSize { get; } + public int? Offset { get; } + + public override BinaryData ToBytes() { - throw new NotImplementedException(); + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + + writer.WriteStartObject(); + + if (Order is not null) + { + writer.WriteString("order", Order); + } + + if (PageSize.HasValue) + { + writer.WriteNumber("pageSize", PageSize.Value); + } + + if (Offset.HasValue) + { + writer.WriteNumber("offset", Offset.Value); + } + + writer.WriteEndObject(); + + writer.Flush(); + stream.Position = 0; + + return BinaryData.FromStream(stream); } - public static ValuesPageToken FromToken(ContinuationToken pageToken) + public ValuesPageToken? GetNextPageToken(int offset, int count) { - throw new NotImplementedException(); + if (offset >= count) + { + return null; + } + + return new ValuesPageToken(Order, PageSize, offset); } - public static ValuesPageToken FromOptions() + public static ValuesPageToken FromToken(ContinuationToken pageToken) { - throw new NotImplementedException(); + if (pageToken is ValuesPageToken token) + { + return token; + } + + BinaryData data = pageToken.ToBytes(); + + if (data.ToMemory().Length == 0) + { + throw new ArgumentException("Failed to create ValuesPageToken from provided pageToken.", nameof(pageToken)); + } + + Utf8JsonReader reader = new(data); + + string? order = null; + int? pageSize = null; + int? offset = null; + + reader.Read(); + + Debug.Assert(reader.TokenType == JsonTokenType.StartObject); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + { + break; + } + + Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); + + string propertyName = reader.GetString()!; + + switch (propertyName) + { + case "order": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.String); + order = reader.GetString(); + break; + + case "pageSize": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.Number); + pageSize = reader.GetInt32(); + break; + + case "offset": + reader.Read(); + Debug.Assert(reader.TokenType == JsonTokenType.Number); + offset = reader.GetInt32(); + break; + default: + throw new JsonException($"Unrecognized property '{propertyName}'."); + } + } + + return new(order, pageSize, offset); } + + public static ValuesPageToken FromOptions(string? order, int? pageSize, int? offset) + => new(order, pageSize, offset); } From bcb7d69c135cecb6a649dbbe972a6d781d20096d Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 11:00:44 -0700 Subject: [PATCH 32/49] sync tests all pass --- .../tests/Convenience/PageCollectionTests.cs | 42 ++++++++++++++----- .../PagingClient/MockData/MockPagingData.cs | 3 ++ .../TestClients/PagingClient/ValueItem.cs | 2 + 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index ea81e1b9621aa..2d81e00a14eba 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -14,6 +14,9 @@ namespace System.ClientModel.Tests.Paging; public class PageCollectionTests { + // TODO: Async + // TODO: A few more tests - from commented-out tests + [Test] public void CanGetAllValues() { @@ -74,10 +77,8 @@ public void CanGetCurrentPage() Assert.AreEqual(0, page.Values[0].Id); } - // TODO: Async - [Test] - public void CanGetCurrentPageThenAllItems() + public void CanGetCurrentPageThenGetAllItems() { PagingClientOptions options = new() { @@ -133,6 +134,31 @@ public void CanGetCurrentPageWhileEnumeratingItems() Assert.AreEqual(MockPagingData.Count, count); } + //[Test] + //public void CanGetCurrentPageWhileEnumeratingPages() + //{ + // PagingClientOptions options = new() + // { + // Transport = new MockPipelineTransport("Mock", i => 200) + // }; + + // PagingClient client = new PagingClient(options); + // PageCollection pages = client.GetValues(); + + // int pageCount = 0; + // foreach (PageResult page in pages) + // { + // pageCount++; + + // PageResult currentPage = pages.GetCurrentPage(); + + // Assert.AreEqual(page.Values.Count, currentPage.Values.Count); + // Assert.AreEqual(page.Values[0].Id, currentPage.Values[0].Id); + // } + + // Assert.AreEqual(2, pageCount); + //} + [Test] public void CanRehydratePageCollection() { @@ -268,8 +294,8 @@ public void CanSkipItemsAndRehydrate() Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); // Both pages have the same non-default offset value - Assert.AreEqual(offset - 1, page.Values[0].Id); - Assert.AreEqual(offset - 1, rehydratedPage.Values[0].Id); + Assert.AreEqual(offset, page.Values[0].Id); + Assert.AreEqual(offset, rehydratedPage.Values[0].Id); } [Test] @@ -290,7 +316,7 @@ public void CanChangeAllCollectionParametersAndRehydrate() Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(pageSize: pageSize); + PageCollection pages = client.GetValues(order, pageSize, offset); PageResult page = pages.GetCurrentPage(); ContinuationToken pageToken = page.PageToken; @@ -300,10 +326,6 @@ public void CanChangeAllCollectionParametersAndRehydrate() // Both page collections and first page are the same on each dimension - // Same number of pages in the two collections - Assert.AreEqual(3, pages.Count()); - Assert.AreEqual(3, rehydratedPages.Count()); - // Last one first and same items skipped Assert.AreEqual(11, page.Values[0].Id); Assert.AreEqual(11, rehydratedPage.Values[0].Id); diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs index 39f798958a069..75ec86196401c 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs @@ -15,6 +15,7 @@ public class MockPagingData public const int DefaultPageSize = 8; public const int DefaultOffset = 0; + // Source of all the data public static IEnumerable GetValues() { for (int i = 0; i < Count; i++) @@ -23,6 +24,7 @@ public static IEnumerable GetValues() } } + // Filters on top of data source public static IEnumerable GetValues( string? order, int? pageSize, @@ -41,6 +43,7 @@ public static IEnumerable GetValues( return page; } + // Turn data into a page result for protocol layer public static ClientResult GetPageResult(IEnumerable values) => ClientResult.FromResponse(new MockValueItemPageResponse(values)); } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs index f4293d507ef2e..7bb47089956a9 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs @@ -25,4 +25,6 @@ public static ValueItem FromJson(JsonElement element) string value = element.GetProperty("value").GetString()!; return new ValueItem(id, value); } + + public override string ToString() => ToJson(); } From c7ea4a512692c02e486eca767f093c12dc1bd51f Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 11:41:45 -0700 Subject: [PATCH 33/49] backup sync async test base paging tests idea --- .../tests/Convenience/PageCollectionTests.cs | 60 +++---------------- .../Mocks/MockSyncAsyncExtensions.cs | 46 ++++++++++++++ .../PagingClient/Emitted/Argument.cs | 2 +- .../Emitted/CancellationTokenExtensions.cs | 2 +- .../Emitted/PageCollectionHelpers.cs | 2 +- .../PagingClient/Emitted/PageEnumerator.cs | 2 +- .../Emitted/PageResultEnumerator.cs | 2 +- .../PagingClient/MockData/MockPagingData.cs | 2 +- .../MockData/MockValueItemPageResponse.cs | 2 +- .../TestClients/PagingClient/PagingClient.cs | 2 +- .../PagingClient/PagingClientOptions.cs | 2 +- .../PagingClient/PagingProtocolClient.cs | 2 +- .../TestClients/PagingClient/ValueItem.cs | 2 +- .../TestClients/PagingClient/ValueItemPage.cs | 2 +- .../PagingClient/ValuesPageEnumerator.cs | 2 +- .../ValuesPageResultEnumerator.cs | 2 +- .../PagingClient/ValuesPageToken.cs | 2 +- 17 files changed, 69 insertions(+), 67 deletions(-) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 2d81e00a14eba..44ea7e62b9733 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -6,41 +6,24 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using ClientModel.Tests; using ClientModel.Tests.Mocks; -using ClientModel.Tests.PagingClient; +using ClientModel.Tests.Paging; using NUnit.Framework; namespace System.ClientModel.Tests.Paging; -public class PageCollectionTests +public class PageCollectionTests : SyncAsyncTestBase { // TODO: Async // TODO: A few more tests - from commented-out tests - [Test] - public void CanGetAllValues() + public PageCollectionTests(bool isAsync) : base(isAsync) { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - IEnumerable values = pages.GetAllValues(); - - int count = 0; - foreach (ValueItem value in values) - { - Assert.AreEqual(count, value.Id); - count++; - } - - Assert.AreEqual(MockPagingData.Count, count); } [Test] - public async Task CanGetAllValueAsync() + public async Task CanGetAllValues() { PagingClientOptions options = new() { @@ -48,8 +31,7 @@ public async Task CanGetAllValueAsync() }; PagingClient client = new PagingClient(options); - AsyncPageCollection pages = client.GetValuesAsync(); - IAsyncEnumerable values = pages.GetAllValuesAsync(); + IAsyncEnumerable values = client.GetAllValuesSyncOrAsync(IsAsync); int count = 0; await foreach (ValueItem value in values) @@ -62,7 +44,7 @@ public async Task CanGetAllValueAsync() } [Test] - public void CanGetCurrentPage() + public async Task CanGetCurrentPage() { PagingClientOptions options = new() { @@ -70,8 +52,7 @@ public void CanGetCurrentPage() }; PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - PageResult page = pages.GetCurrentPage(); + PageResult page = await client.GetCurrentPageSyncOrAsync(IsAsync); Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); Assert.AreEqual(0, page.Values[0].Id); @@ -134,31 +115,6 @@ public void CanGetCurrentPageWhileEnumeratingItems() Assert.AreEqual(MockPagingData.Count, count); } - //[Test] - //public void CanGetCurrentPageWhileEnumeratingPages() - //{ - // PagingClientOptions options = new() - // { - // Transport = new MockPipelineTransport("Mock", i => 200) - // }; - - // PagingClient client = new PagingClient(options); - // PageCollection pages = client.GetValues(); - - // int pageCount = 0; - // foreach (PageResult page in pages) - // { - // pageCount++; - - // PageResult currentPage = pages.GetCurrentPage(); - - // Assert.AreEqual(page.Values.Count, currentPage.Values.Count); - // Assert.AreEqual(page.Values[0].Id, currentPage.Values[0].Id); - // } - - // Assert.AreEqual(2, pageCount); - //} - [Test] public void CanRehydratePageCollection() { diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs index da2405a3257f5..10d7650b43e5a 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs @@ -8,6 +8,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using ClientModel.Tests.Paging; namespace ClientModel.Tests.Mocks; @@ -96,4 +97,49 @@ public static async Task BufferContentSyncOrAsync(this PipelineRespo return response.BufferContent(cancellationToken); } } + + public static IAsyncEnumerable GetAllValuesSyncOrAsync(this PagingClient client, + bool isAsync, + string? order = default, + int? pageSize = default, + int? offset = default) + { + if (isAsync) + { + AsyncPageCollection pages = client.GetValuesAsync(order, pageSize, offset); + return pages.GetAllValuesAsync(); + } + else + { + PageCollection pages = client.GetValues(order, pageSize, offset); + return pages.GetAllValues().ToAsyncEnumerable(); + } + } + + public static async Task> GetCurrentPageSyncOrAsync(this PagingClient client, + bool isAsync, + string? order = default, + int? pageSize = default, + int? offset = default) + { + if (isAsync) + { + AsyncPageCollection pages = client.GetValuesAsync(order, pageSize, offset); + return await pages.GetCurrentPageAsync().ConfigureAwait(false); + } + else + { + PageCollection pages = client.GetValues(order, pageSize, offset); + return pages.GetCurrentPage(); + } + } + + public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable enumerable) + { + foreach (T item in enumerable) + { + await Task.Delay(0); + yield return item; + } + } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs index e7e41297a8cc2..79804fd51ac16 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/Argument.cs @@ -5,7 +5,7 @@ using System.Collections; using System.Collections.Generic; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal static class Argument { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs index 12f0db0188e6c..78058b617a068 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/CancellationTokenExtensions.cs @@ -4,7 +4,7 @@ using System.ClientModel.Primitives; using System.Threading; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal static class CancellationTokenExtensions { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs index f79e9e2212f1e..c195644d6092f 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal class PageCollectionHelpers { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs index 6fb930a576d09..0227931a980c7 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs @@ -4,7 +4,7 @@ using System.ClientModel; using System.Collections.Generic; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal abstract class PageEnumerator : PageResultEnumerator, IAsyncEnumerator>, diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs index c9b964e4bf2f4..64d2550f97db8 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageResultEnumerator.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal abstract class PageResultEnumerator : IAsyncEnumerator, IEnumerator { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs index 75ec86196401c..9a004c51eec09 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockPagingData.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; public class MockPagingData { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs index 8144dd24de512..f21cb0720d269 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/MockData/MockValueItemPageResponse.cs @@ -10,7 +10,7 @@ using System.Threading; using System.Threading.Tasks; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal class MockValueItemPageResponse : PipelineResponse { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs index 2f3dc91a232f7..c44c08c57ac3a 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClient.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Threading; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; // A mock client implementation that illustrates paging patterns for client // endpoints that have both convenience and protocol methods. diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs index 55bdf7634629b..0813e7730122f 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingClientOptions.cs @@ -5,7 +5,7 @@ using System.ClientModel.Primitives; using System.Collections.Generic; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; public class PagingClientOptions : ClientPipelineOptions { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs index 1941815652200..05c4779bb792b 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/PagingProtocolClient.cs @@ -6,7 +6,7 @@ using System.ClientModel.Primitives; using System.Collections.Generic; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; // A mock client implementation that illustrates paging patterns for client // endpoints that only have protocol methods. diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs index 7bb47089956a9..5265fc52d974e 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItem.cs @@ -3,7 +3,7 @@ using System.Text.Json; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; // A mock model that illustrate values that can be returned in a page collection public class ValueItem diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs index a265e9676730f..8dbde03dacaac 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValueItemPage.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Text.Json; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; // In a real client, this type would be generated but would be made internal. // It corresponds to the REST API definition of the response that comes back diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs index 07fe6910b002c..b2afcfe033256 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal class ValuesPageEnumerator : PageEnumerator { diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index ed73c2c3820ec..fe011f4dbd4ac 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; // Mocks a page result enumerator a client would have for paged endpoints when // those endpoints only have protocol methods on the client. diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs index 7ffdee3db912d..3a205541eadc2 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageToken.cs @@ -7,7 +7,7 @@ using System.IO; using System.Text.Json; -namespace ClientModel.Tests.PagingClient; +namespace ClientModel.Tests.Paging; internal class ValuesPageToken : ContinuationToken { From a36fd22cf051e261088b9a751efe5d1fd95445e4 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 12:08:53 -0700 Subject: [PATCH 34/49] separate unit and scenario tests --- .../src/Convenience/ContinuationToken.cs | 4 + .../src/Convenience/PageResultOfT.cs | 4 + .../PageCollectionScenarioTests.cs | 640 ++++++++++++++++++ .../tests/Convenience/PageCollectionTests.cs | 69 +- .../TestFramework/Mocks/MockPageCollection.cs | 36 + .../Mocks/MockSyncAsyncExtensions.cs | 45 -- 6 files changed, 712 insertions(+), 86 deletions(-) create mode 100644 sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs diff --git a/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs index 8258c0a14d269..9e0ed0f54d3a2 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.ClientModel.Internal; + namespace System.ClientModel; #pragma warning disable CS1591 @@ -13,6 +15,8 @@ protected ContinuationToken() { } protected ContinuationToken(BinaryData bytes) { + Argument.AssertNotNull(bytes, nameof(bytes)); + _bytes = bytes; } diff --git a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs index 58cf6686e381a..44d9b876d5caf 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.ClientModel.Internal; using System.ClientModel.Primitives; using System.Collections.Generic; @@ -15,6 +16,9 @@ private PageResult(IReadOnlyList values, ContinuationToken? nextPageToken, PipelineResponse response) : base(response) { + Argument.AssertNotNull(values, nameof(values)); + Argument.AssertNotNull(pageToken, nameof(pageToken)); + Values = values; PageToken = pageToken; NextPageToken = nextPageToken; diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs new file mode 100644 index 0000000000000..e00ad639546bc --- /dev/null +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs @@ -0,0 +1,640 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.ClientModel.Primitives; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ClientModel.Tests; +using ClientModel.Tests.Mocks; +using ClientModel.Tests.Paging; +using NUnit.Framework; + +namespace System.ClientModel.Tests.Paging; + +// Scenario tests for sync and async page collections +public class PageScenarioCollectionTests +{ + // TODO: Async + // TODO: A few more tests - from commented-out tests + + [Test] + public void CanGetAllValues() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanGetCurrentPage() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + PageResult page = pages.GetCurrentPage(); + + Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0].Id); + } + + [Test] + public void CanGetCurrentPageThenGetAllItems() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + + PageResult page = pages.GetCurrentPage(); + + Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0].Id); + + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanGetCurrentPageWhileEnumeratingItems() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + + PageResult page = pages.GetCurrentPage(); + + // Validate that the current item is in range of the page values + Assert.GreaterOrEqual(value.Id, page.Values[0].Id); + Assert.LessOrEqual(value.Id, page.Values[page.Values.Count - 1].Id); + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanRehydratePageCollection() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + List allValues = pages.GetAllValues().ToList(); + List allRehydratedValues = rehydratedPages.GetAllValues().ToList(); + + for (int i = 0; i < allValues.Count; i++) + { + Assert.AreEqual(allValues[i].Id, allRehydratedValues[i].Id); + } + } + + [Test] + public void CanCastToConvenienceFromProtocol() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + + // Call the protocol method on the convenience client. + IEnumerable pageResults = client.GetValues( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + // Cast to convience type from protocol return value. + PageCollection pages = (PageCollection)pageResults; + + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanReorderItemsAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + string order = "desc"; + Assert.AreNotEqual(MockPagingData.DefaultOrder, order); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(order: order); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + // We got the last one first from both pages + Assert.AreEqual(MockPagingData.Count - 1, page.Values[0].Id); + Assert.AreEqual(MockPagingData.Count - 1, rehydratedPage.Values[0].Id); + } + + [Test] + public void CanChangePageSizeAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + int pageSize = 4; + Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(pageSize: pageSize); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + // Both pages have same non-default page size + Assert.AreEqual(pageSize, page.Values.Count); + Assert.AreEqual(pageSize, rehydratedPage.Values.Count); + } + + [Test] + public void CanSkipItemsAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + int offset = 4; + Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(offset: offset); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + // Both pages have the same non-default offset value + Assert.AreEqual(offset, page.Values[0].Id); + Assert.AreEqual(offset, rehydratedPage.Values[0].Id); + } + + [Test] + public void CanChangeAllCollectionParametersAndRehydrate() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + string order = "desc"; + Assert.AreNotEqual(MockPagingData.DefaultOrder, order); + + int pageSize = 4; + Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); + + int offset = 4; + Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); + + PagingClient client = new PagingClient(options); + PageCollection pages = client.GetValues(order, pageSize, offset); + PageResult page = pages.GetCurrentPage(); + + ContinuationToken pageToken = page.PageToken; + + PageCollection rehydratedPages = client.GetValues(pageToken); + PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); + + // Both page collections and first page are the same on each dimension + + // Last one first and same items skipped + Assert.AreEqual(11, page.Values[0].Id); + Assert.AreEqual(11, rehydratedPage.Values[0].Id); + + // Equal page size + Assert.AreEqual(pageSize, page.Values.Count); + Assert.AreEqual(pageSize, rehydratedPage.Values.Count); + } + + //[Test] + //public void CanEnumeratePages() + //{ + // List values = new() { 0, 1, 2, 3 }; + // int pageSize = 2; + + // List mockResults = new() { + // new MockClientResult(new MockPipelineResponse(0)), + // new MockClientResult(new MockPipelineResponse(1)) + // }; + + // PageCollection pages = new MockPageCollection(values, mockResults, pageSize); + + // int i = 0; + // foreach (PageResult page in pages) + // { + // Assert.AreEqual(i++, page.Values[0]); + // Assert.AreEqual(i++, page.Values[1]); + // } + + // Assert.AreEqual(4, i); + //} + + //[Test] + //public void CanEnumerateClientResults() + //{ + // List mockResults = new() { + // new MockClientResult(new MockPipelineResponse(0)), + // new MockClientResult(new MockPipelineResponse(1)) + // }; + + // IEnumerable results = new ProtocolMockPageCollection(mockResults); + + // int i = 0; + // foreach (ClientResult result in results) + // { + // Assert.AreEqual(i++, result.GetRawResponse().Status); + // } + + // Assert.AreEqual(2, i); + //} + + //[Test] + //public void CanEvolveFromProtocol() + //{ + // List values = new() { 0, 1, 2, 3 }; + // int pageSize = 2; + + // List mockResults = new() { + // new MockClientResult(new MockPipelineResponse(0)), + // new MockClientResult(new MockPipelineResponse(1)) + // }; + + // // Showing that we can use the same code as protocol-only + // // with a convenience return type. + // IEnumerable results = new MockPageCollection(values, mockResults, pageSize); + + // int i = 0; + // foreach (ClientResult result in results) + // { + // Assert.AreEqual(i++, result.GetRawResponse().Status); + // } + + // Assert.AreEqual(2, i); + //} + + //private static readonly string[] MockPageContents = { """ + // [ + // { "intValue" : 0, "stringValue" : "0" }, + // { "intValue" : 1, "stringValue" : "1" }, + // { "intValue" : 2, "stringValue" : "2" } + // ] + // """,""" + // [ + // { "intValue" : 3, "stringValue" : "3" }, + // { "intValue" : 4, "stringValue" : "4" }, + // { "intValue" : 5, "stringValue" : "5" } + // ] + // """,""" + // [ + // { "intValue" : 6, "stringValue" : "6" }, + // { "intValue" : 7, "stringValue" : "7" }, + // { "intValue" : 8, "stringValue" : "8" } + // ] + // """, + // }; + + //private static readonly int PageCount = MockPageContents.Length; + //private static readonly int ItemCount = 9; + + //[Test] + //public void CanEnumerateValues() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int i = 0; + // foreach (MockJsonModel model in models) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // Assert.AreEqual(ItemCount, i); + //} + + //[Test] + //public void CanEnumeratePages() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + //[Test] + //public void CanStartPageEnumerationMidwayThrough() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int pageCount = 0; + // int i = 6; + + // // Request just the last page by starting at the last seen value + // // on the prior page -- i.e. item 5. + // foreach (ResultPage page in models.AsPages(continuationToken: "5")) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, i); + // Assert.AreEqual(1, pageCount); + //} + + //[Test] + //public void CanSetPageSizeHint() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + // var pages = models.AsPages(pageSizeHint: 10); + // foreach (var _ in pages) + // { + // // page size hint is ignored in this mock + // } + + // Assert.AreEqual(10, client.RequestedPageSize); + //} + + //[Test] + //public void CanGetRawResponses() + //{ + // MockPageableClient client = new(); + // PageableCollection models = client.GetModels(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // PipelineResponse collectionResponse = models.GetRawResponse(); + // PipelineResponse pageResponse = page.GetRawResponse(); + + // Assert.AreEqual(pageResponse, collectionResponse); + // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); + // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + //[Test] + //public async Task CanEnumerateValuesAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int i = 0; + // await foreach (MockJsonModel model in models) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // Assert.AreEqual(ItemCount, i); + //} + + //[Test] + //public async Task CanEnumeratePagesAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // await foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + //[Test] + //public async Task CanStartPageEnumerationMidwayThroughAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int pageCount = 0; + // int i = 6; + + // // Request just the last page by starting at the last seen value + // // on the prior page -- i.e. item 5. + // await foreach (ResultPage page in models.AsPages(continuationToken: "5")) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(i, model.IntValue); + // Assert.AreEqual(i.ToString(), model.StringValue); + + // i++; + // } + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, i); + // Assert.AreEqual(1, pageCount); + //} + + //[Test] + //public async Task CanSetPageSizeHintAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + // var pages = models.AsPages(pageSizeHint: 10); + // await foreach (var _ in pages) + // { + // // page size hint is ignored in this mock + // } + + // Assert.AreEqual(10, client.RequestedPageSize); + //} + + //[Test] + //public async Task CanGetRawResponsesAsync() + //{ + // MockPageableClient client = new(); + // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); + + // int pageCount = 0; + // int itemCount = 0; + // await foreach (ResultPage page in models.AsPages()) + // { + // foreach (MockJsonModel model in page) + // { + // Assert.AreEqual(itemCount, model.IntValue); + // Assert.AreEqual(itemCount.ToString(), model.StringValue); + + // itemCount++; + // } + + // PipelineResponse collectionResponse = models.GetRawResponse(); + // PipelineResponse pageResponse = page.GetRawResponse(); + + // Assert.AreEqual(pageResponse, collectionResponse); + // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); + // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); + + // pageCount++; + // } + + // Assert.AreEqual(ItemCount, itemCount); + // Assert.AreEqual(PageCount, pageCount); + //} + + #region Helpers + + internal class ProtocolMockPageCollection : IEnumerable + { + private readonly List _results; + + public ProtocolMockPageCollection(List results) + { + _results = results; + } + + public IEnumerator GetEnumerator() + { + foreach (ClientResult result in _results) + { + yield return result; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + #endregion +} diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 44ea7e62b9733..139d5263a0895 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -5,85 +5,72 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -using ClientModel.Tests; using ClientModel.Tests.Mocks; using ClientModel.Tests.Paging; using NUnit.Framework; namespace System.ClientModel.Tests.Paging; -public class PageCollectionTests : SyncAsyncTestBase +// Unit tests for sync and async page collections +public class PageCollectionTests { - // TODO: Async - // TODO: A few more tests - from commented-out tests + private const int Count = 16; + private const int DefaultPageSize = 8; + private static readonly List MockValues = GetMockValues(Count).ToList(); - public PageCollectionTests(bool isAsync) : base(isAsync) + private static IEnumerable GetMockValues(int count) { + for (int i = 0; i < count; i++) + { + yield return i; + } } [Test] - public async Task CanGetAllValues() + public void CanGetAllValues() { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - IAsyncEnumerable values = client.GetAllValuesSyncOrAsync(IsAsync); + PageCollection pages = new MockPageCollection(MockValues, DefaultPageSize); + IEnumerable values = pages.GetAllValues(); int count = 0; - await foreach (ValueItem value in values) + foreach (int value in values) { - Assert.AreEqual(count, value.Id); + Assert.AreEqual(count, value); count++; } - Assert.AreEqual(MockPagingData.Count, count); + Assert.AreEqual(Count, count); } [Test] - public async Task CanGetCurrentPage() + public void CanGetCurrentPage() { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageResult page = await client.GetCurrentPageSyncOrAsync(IsAsync); + PageCollection pages = new MockPageCollection(MockValues, DefaultPageSize); + PageResult page = pages.GetCurrentPage(); Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); - Assert.AreEqual(0, page.Values[0].Id); + Assert.AreEqual(0, page.Values[0]); } [Test] public void CanGetCurrentPageThenGetAllItems() { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; + PageCollection pages = new MockPageCollection(MockValues, DefaultPageSize); + PageResult page = pages.GetCurrentPage(); - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); + Assert.AreEqual(DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0]); - PageResult page = pages.GetCurrentPage(); - - Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); - Assert.AreEqual(0, page.Values[0].Id); - - IEnumerable values = pages.GetAllValues(); + IEnumerable values = pages.GetAllValues(); int count = 0; - foreach (ValueItem value in values) + foreach (int value in values) { - Assert.AreEqual(count, value.Id); + Assert.AreEqual(count, value); count++; } - Assert.AreEqual(MockPagingData.Count, count); + Assert.AreEqual(Count, count); } [Test] diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs new file mode 100644 index 0000000000000..519f7f9bced87 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.Collections.Generic; + +namespace ClientModel.Tests.Mocks; + +public class MockPageCollection : PageCollection +{ + private readonly List _values; + private readonly int _pageSize; + + private int _current; + + public MockPageCollection(List values, int pageSize) + { + _values = values; + _pageSize = pageSize; + } + + protected override IEnumerator> GetEnumeratorCore() + { + while (_current < _values.Count) + { + int pageSize = Math.Min(_pageSize, _values.Count - _current); + List pageValues = _values.GetRange(_current, pageSize); + _current += _pageSize; + + // Make page tokens not useful for mocks. + ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); + yield return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + } + } +} diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs index 10d7650b43e5a..3f1fe120da63d 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs @@ -97,49 +97,4 @@ public static async Task BufferContentSyncOrAsync(this PipelineRespo return response.BufferContent(cancellationToken); } } - - public static IAsyncEnumerable GetAllValuesSyncOrAsync(this PagingClient client, - bool isAsync, - string? order = default, - int? pageSize = default, - int? offset = default) - { - if (isAsync) - { - AsyncPageCollection pages = client.GetValuesAsync(order, pageSize, offset); - return pages.GetAllValuesAsync(); - } - else - { - PageCollection pages = client.GetValues(order, pageSize, offset); - return pages.GetAllValues().ToAsyncEnumerable(); - } - } - - public static async Task> GetCurrentPageSyncOrAsync(this PagingClient client, - bool isAsync, - string? order = default, - int? pageSize = default, - int? offset = default) - { - if (isAsync) - { - AsyncPageCollection pages = client.GetValuesAsync(order, pageSize, offset); - return await pages.GetCurrentPageAsync().ConfigureAwait(false); - } - else - { - PageCollection pages = client.GetValues(order, pageSize, offset); - return pages.GetCurrentPage(); - } - } - - public static async IAsyncEnumerable ToAsyncEnumerable(this IEnumerable enumerable) - { - foreach (T item in enumerable) - { - await Task.Delay(0); - yield return item; - } - } } From 5c0fc3e79c5cd9fff858ac0fd40e9ecabaf7183c Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 12:15:31 -0700 Subject: [PATCH 35/49] clean up sync unit tests --- .../tests/Convenience/PageCollectionTests.cs | 545 +----------------- .../TestFramework/Mocks/MockPageCollection.cs | 3 +- 2 files changed, 17 insertions(+), 531 deletions(-) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 139d5263a0895..de09fc97c3529 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -76,553 +76,38 @@ public void CanGetCurrentPageThenGetAllItems() [Test] public void CanGetCurrentPageWhileEnumeratingItems() { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - - IEnumerable values = pages.GetAllValues(); + PageCollection pages = new MockPageCollection(MockValues, DefaultPageSize); + IEnumerable values = pages.GetAllValues(); int count = 0; - foreach (ValueItem value in values) + foreach (int value in values) { - Assert.AreEqual(count, value.Id); + Assert.AreEqual(count, value); count++; - PageResult page = pages.GetCurrentPage(); + PageResult page = pages.GetCurrentPage(); // Validate that the current item is in range of the page values - Assert.GreaterOrEqual(value.Id, page.Values[0].Id); - Assert.LessOrEqual(value.Id, page.Values[page.Values.Count - 1].Id); - } - - Assert.AreEqual(MockPagingData.Count, count); - } - - [Test] - public void CanRehydratePageCollection() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - PageResult page = pages.GetCurrentPage(); - - ContinuationToken pageToken = page.PageToken; - - PageCollection rehydratedPages = client.GetValues(pageToken); - PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); - - Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); - - List allValues = pages.GetAllValues().ToList(); - List allRehydratedValues = rehydratedPages.GetAllValues().ToList(); - - for (int i = 0; i < allValues.Count; i++) - { - Assert.AreEqual(allValues[i].Id, allRehydratedValues[i].Id); - } - } - - [Test] - public void CanCastToConvenienceFromProtocol() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - - // Call the protocol method on the convenience client. - IEnumerable pageResults = client.GetValues( - order: default, - pageSize: default, - offset: default, - new RequestOptions()); - - // Cast to convience type from protocol return value. - PageCollection pages = (PageCollection)pageResults; - - IEnumerable values = pages.GetAllValues(); - - int count = 0; - foreach (ValueItem value in values) - { - Assert.AreEqual(count, value.Id); - count++; + Assert.GreaterOrEqual(value, page.Values[0]); + Assert.LessOrEqual(value, page.Values[page.Values.Count - 1]); } Assert.AreEqual(MockPagingData.Count, count); } [Test] - public void CanReorderItemsAndRehydrate() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - string order = "desc"; - Assert.AreNotEqual(MockPagingData.DefaultOrder, order); - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(order: order); - PageResult page = pages.GetCurrentPage(); - - ContinuationToken pageToken = page.PageToken; - - PageCollection rehydratedPages = client.GetValues(pageToken); - PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); - - Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); - - // We got the last one first from both pages - Assert.AreEqual(MockPagingData.Count - 1, page.Values[0].Id); - Assert.AreEqual(MockPagingData.Count - 1, rehydratedPage.Values[0].Id); - } - - [Test] - public void CanChangePageSizeAndRehydrate() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - int pageSize = 4; - Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(pageSize: pageSize); - PageResult page = pages.GetCurrentPage(); - - ContinuationToken pageToken = page.PageToken; - - PageCollection rehydratedPages = client.GetValues(pageToken); - PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); - - // Both pages have same non-default page size - Assert.AreEqual(pageSize, page.Values.Count); - Assert.AreEqual(pageSize, rehydratedPage.Values.Count); - } - - [Test] - public void CanSkipItemsAndRehydrate() + public void CanEnumerateClientResults() { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - int offset = 4; - Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(offset: offset); - PageResult page = pages.GetCurrentPage(); - - ContinuationToken pageToken = page.PageToken; - - PageCollection rehydratedPages = client.GetValues(pageToken); - PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); - - Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); - - // Both pages have the same non-default offset value - Assert.AreEqual(offset, page.Values[0].Id); - Assert.AreEqual(offset, rehydratedPage.Values[0].Id); - } - - [Test] - public void CanChangeAllCollectionParametersAndRehydrate() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - string order = "desc"; - Assert.AreNotEqual(MockPagingData.DefaultOrder, order); - - int pageSize = 4; - Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); - - int offset = 4; - Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(order, pageSize, offset); - PageResult page = pages.GetCurrentPage(); - - ContinuationToken pageToken = page.PageToken; - - PageCollection rehydratedPages = client.GetValues(pageToken); - PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); - - // Both page collections and first page are the same on each dimension - - // Last one first and same items skipped - Assert.AreEqual(11, page.Values[0].Id); - Assert.AreEqual(11, rehydratedPage.Values[0].Id); - - // Equal page size - Assert.AreEqual(pageSize, page.Values.Count); - Assert.AreEqual(pageSize, rehydratedPage.Values.Count); - } - - //[Test] - //public void CanEnumeratePages() - //{ - // List values = new() { 0, 1, 2, 3 }; - // int pageSize = 2; - - // List mockResults = new() { - // new MockClientResult(new MockPipelineResponse(0)), - // new MockClientResult(new MockPipelineResponse(1)) - // }; - - // PageCollection pages = new MockPageCollection(values, mockResults, pageSize); - - // int i = 0; - // foreach (PageResult page in pages) - // { - // Assert.AreEqual(i++, page.Values[0]); - // Assert.AreEqual(i++, page.Values[1]); - // } - - // Assert.AreEqual(4, i); - //} - - //[Test] - //public void CanEnumerateClientResults() - //{ - // List mockResults = new() { - // new MockClientResult(new MockPipelineResponse(0)), - // new MockClientResult(new MockPipelineResponse(1)) - // }; - - // IEnumerable results = new ProtocolMockPageCollection(mockResults); - - // int i = 0; - // foreach (ClientResult result in results) - // { - // Assert.AreEqual(i++, result.GetRawResponse().Status); - // } - - // Assert.AreEqual(2, i); - //} - - //[Test] - //public void CanEvolveFromProtocol() - //{ - // List values = new() { 0, 1, 2, 3 }; - // int pageSize = 2; - - // List mockResults = new() { - // new MockClientResult(new MockPipelineResponse(0)), - // new MockClientResult(new MockPipelineResponse(1)) - // }; - - // // Showing that we can use the same code as protocol-only - // // with a convenience return type. - // IEnumerable results = new MockPageCollection(values, mockResults, pageSize); - - // int i = 0; - // foreach (ClientResult result in results) - // { - // Assert.AreEqual(i++, result.GetRawResponse().Status); - // } - - // Assert.AreEqual(2, i); - //} - - //private static readonly string[] MockPageContents = { """ - // [ - // { "intValue" : 0, "stringValue" : "0" }, - // { "intValue" : 1, "stringValue" : "1" }, - // { "intValue" : 2, "stringValue" : "2" } - // ] - // """,""" - // [ - // { "intValue" : 3, "stringValue" : "3" }, - // { "intValue" : 4, "stringValue" : "4" }, - // { "intValue" : 5, "stringValue" : "5" } - // ] - // """,""" - // [ - // { "intValue" : 6, "stringValue" : "6" }, - // { "intValue" : 7, "stringValue" : "7" }, - // { "intValue" : 8, "stringValue" : "8" } - // ] - // """, - // }; - - //private static readonly int PageCount = MockPageContents.Length; - //private static readonly int ItemCount = 9; - - //[Test] - //public void CanEnumerateValues() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int i = 0; - // foreach (MockJsonModel model in models) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // Assert.AreEqual(ItemCount, i); - //} - - //[Test] - //public void CanEnumeratePages() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - //[Test] - //public void CanStartPageEnumerationMidwayThrough() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int pageCount = 0; - // int i = 6; - - // // Request just the last page by starting at the last seen value - // // on the prior page -- i.e. item 5. - // foreach (ResultPage page in models.AsPages(continuationToken: "5")) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, i); - // Assert.AreEqual(1, pageCount); - //} - - //[Test] - //public void CanSetPageSizeHint() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - // var pages = models.AsPages(pageSizeHint: 10); - // foreach (var _ in pages) - // { - // // page size hint is ignored in this mock - // } - - // Assert.AreEqual(10, client.RequestedPageSize); - //} - - //[Test] - //public void CanGetRawResponses() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // PipelineResponse collectionResponse = models.GetRawResponse(); - // PipelineResponse pageResponse = page.GetRawResponse(); - - // Assert.AreEqual(pageResponse, collectionResponse); - // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); - // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - //[Test] - //public async Task CanEnumerateValuesAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int i = 0; - // await foreach (MockJsonModel model in models) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // Assert.AreEqual(ItemCount, i); - //} - - //[Test] - //public async Task CanEnumeratePagesAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // await foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - //[Test] - //public async Task CanStartPageEnumerationMidwayThroughAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int pageCount = 0; - // int i = 6; - - // // Request just the last page by starting at the last seen value - // // on the prior page -- i.e. item 5. - // await foreach (ResultPage page in models.AsPages(continuationToken: "5")) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, i); - // Assert.AreEqual(1, pageCount); - //} - - //[Test] - //public async Task CanSetPageSizeHintAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - // var pages = models.AsPages(pageSizeHint: 10); - // await foreach (var _ in pages) - // { - // // page size hint is ignored in this mock - // } - - // Assert.AreEqual(10, client.RequestedPageSize); - //} - - //[Test] - //public async Task CanGetRawResponsesAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // await foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // PipelineResponse collectionResponse = models.GetRawResponse(); - // PipelineResponse pageResponse = page.GetRawResponse(); - - // Assert.AreEqual(pageResponse, collectionResponse); - // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); - // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - #region Helpers - - internal class ProtocolMockPageCollection : IEnumerable - { - private readonly List _results; - - public ProtocolMockPageCollection(List results) - { - _results = results; - } + PageCollection pages = new MockPageCollection(MockValues, DefaultPageSize); + IEnumerable pageResults = pages; - public IEnumerator GetEnumerator() + int pageCount = 0; + foreach (ClientResult result in pageResults) { - foreach (ClientResult result in _results) - { - yield return result; - } + Assert.AreEqual(200, result.GetRawResponse().Status); + pageCount++; } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + Assert.AreEqual(2, pageCount); } - - #endregion } diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs index 519f7f9bced87..9ae46af12fe61 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs @@ -26,11 +26,12 @@ protected override IEnumerator> GetEnumeratorCore() { int pageSize = Math.Min(_pageSize, _values.Count - _current); List pageValues = _values.GetRange(_current, pageSize); - _current += _pageSize; // Make page tokens not useful for mocks. ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); yield return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + + _current += _pageSize; } } } From f0ec18b19b2ba8e563185ff47a30df80716ed4dc Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 12:25:15 -0700 Subject: [PATCH 36/49] add async unit tests --- .../tests/Convenience/PageCollectionTests.cs | 86 +++++++++++++++++++ .../Mocks/MockAsyncPageCollection.cs | 41 +++++++++ 2 files changed, 127 insertions(+) create mode 100644 sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index de09fc97c3529..c43d0bb77e633 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using ClientModel.Tests.Mocks; using ClientModel.Tests.Paging; using NUnit.Framework; @@ -42,6 +43,22 @@ public void CanGetAllValues() Assert.AreEqual(Count, count); } + [Test] + public async Task CanGetAllValuesAsync() + { + AsyncPageCollection pages = new MockAsyncPageCollection(MockValues, DefaultPageSize); + IAsyncEnumerable values = pages.GetAllValuesAsync(); + + int count = 0; + await foreach (int value in values) + { + Assert.AreEqual(count, value); + count++; + } + + Assert.AreEqual(Count, count); + } + [Test] public void CanGetCurrentPage() { @@ -52,6 +69,16 @@ public void CanGetCurrentPage() Assert.AreEqual(0, page.Values[0]); } + [Test] + public async Task CanGetCurrentPageAsync() + { + AsyncPageCollection pages = new MockAsyncPageCollection(MockValues, DefaultPageSize); + PageResult page = await pages.GetCurrentPageAsync(); + + Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0]); + } + [Test] public void CanGetCurrentPageThenGetAllItems() { @@ -73,6 +100,27 @@ public void CanGetCurrentPageThenGetAllItems() Assert.AreEqual(Count, count); } + [Test] + public async Task CanGetCurrentPageThenGetAllItemsAsync() + { + AsyncPageCollection pages = new MockAsyncPageCollection(MockValues, DefaultPageSize); + PageResult page = await pages.GetCurrentPageAsync(); + + Assert.AreEqual(DefaultPageSize, page.Values.Count); + Assert.AreEqual(0, page.Values[0]); + + IAsyncEnumerable values = pages.GetAllValuesAsync(); + + int count = 0; + await foreach (int value in values) + { + Assert.AreEqual(count, value); + count++; + } + + Assert.AreEqual(Count, count); + } + [Test] public void CanGetCurrentPageWhileEnumeratingItems() { @@ -95,6 +143,28 @@ public void CanGetCurrentPageWhileEnumeratingItems() Assert.AreEqual(MockPagingData.Count, count); } + [Test] + public async Task CanGetCurrentPageWhileEnumeratingItemsAsync() + { + AsyncPageCollection pages = new MockAsyncPageCollection(MockValues, DefaultPageSize); + IAsyncEnumerable values = pages.GetAllValuesAsync(); + + int count = 0; + await foreach (int value in values) + { + Assert.AreEqual(count, value); + count++; + + PageResult page = await pages.GetCurrentPageAsync(); + + // Validate that the current item is in range of the page values + Assert.GreaterOrEqual(value, page.Values[0]); + Assert.LessOrEqual(value, page.Values[page.Values.Count - 1]); + } + + Assert.AreEqual(MockPagingData.Count, count); + } + [Test] public void CanEnumerateClientResults() { @@ -110,4 +180,20 @@ public void CanEnumerateClientResults() Assert.AreEqual(2, pageCount); } + + [Test] + public async Task CanEnumerateClientResultsAsync() + { + AsyncPageCollection pages = new MockAsyncPageCollection(MockValues, DefaultPageSize); + IAsyncEnumerable pageResults = pages; + + int pageCount = 0; + await foreach (ClientResult result in pageResults) + { + Assert.AreEqual(200, result.GetRawResponse().Status); + pageCount++; + } + + Assert.AreEqual(2, pageCount); + } } diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs new file mode 100644 index 0000000000000..138c9f5b3b0d0 --- /dev/null +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ClientModel; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ClientModel.Tests.Mocks; + +public class MockAsyncPageCollection : AsyncPageCollection +{ + private readonly List _values; + private readonly int _pageSize; + + private int _current; + + public MockAsyncPageCollection(List values, int pageSize) + { + _values = values; + _pageSize = pageSize; + } + + protected override async IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken) + { + while (_current < _values.Count) + { + int pageSize = Math.Min(_pageSize, _values.Count - _current); + List pageValues = _values.GetRange(_current, pageSize); + + // Make page tokens not useful for mocks. + ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); + yield return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + + _current += _pageSize; + + await Task.Delay(0); + } + } +} From a30a9dbdd4a29e350d0a4606b7faab530aad5aaa Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 14:30:40 -0700 Subject: [PATCH 37/49] Add GetCurrentPageCore so that GetCurrentPage doesn't advance enumerator --- .../api/System.ClientModel.net6.0.cs | 2 ++ .../api/System.ClientModel.netstandard2.0.cs | 2 ++ .../src/Convenience/AsyncPageCollectionOfT.cs | 27 +++++-------------- .../src/Convenience/PageCollectionOfT.cs | 25 +++++------------ .../Mocks/MockAsyncPageCollection.cs | 24 +++++++++++------ .../TestFramework/Mocks/MockPageCollection.cs | 20 +++++++++----- .../Emitted/PageCollectionHelpers.cs | 7 +++++ .../PagingClient/Emitted/PageEnumerator.cs | 21 +++++++++++++++ 8 files changed, 75 insertions(+), 53 deletions(-) diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs index e6367932fb834..d258e5cb4df20 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs @@ -19,6 +19,7 @@ protected AsyncPageCollection() { } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumeratorCore(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } + protected abstract System.Threading.Tasks.Task> GetCurrentPageAsyncCore(); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -75,6 +76,7 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } + protected abstract System.ClientModel.PageResult GetCurrentPageCore(); protected abstract System.Collections.Generic.IEnumerator> GetEnumeratorCore(); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } diff --git a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs index 11a7ae50c0c7a..e01150b0c72f9 100644 --- a/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs +++ b/sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs @@ -19,6 +19,7 @@ protected AsyncPageCollection() { } public System.Collections.Generic.IAsyncEnumerable GetAllValuesAsync([System.Runtime.CompilerServices.EnumeratorCancellationAttribute] System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } protected abstract System.Collections.Generic.IAsyncEnumerator> GetAsyncEnumeratorCore(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); public System.Threading.Tasks.Task> GetCurrentPageAsync() { throw null; } + protected abstract System.Threading.Tasks.Task> GetCurrentPageAsyncCore(); System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken cancellationToken) { throw null; } } public abstract partial class BinaryContent : System.IDisposable @@ -75,6 +76,7 @@ public abstract partial class PageCollection : System.Collections.Generic.IEn protected PageCollection() { } public System.Collections.Generic.IEnumerable GetAllValues() { throw null; } public System.ClientModel.PageResult GetCurrentPage() { throw null; } + protected abstract System.ClientModel.PageResult GetCurrentPageCore(); protected abstract System.Collections.Generic.IEnumerator> GetEnumeratorCore(); System.Collections.Generic.IEnumerator> System.Collections.Generic.IEnumerable>.GetEnumerator() { throw null; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 9444b5e2e81fc..277d94c70d213 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -20,36 +20,23 @@ protected AsyncPageCollection() : base() } public async Task> GetCurrentPageAsync() - { - IAsyncEnumerator> enumerator = GetAsyncEnumeratorCore(); - - if (enumerator.Current == null) - { - await enumerator.MoveNextAsync().ConfigureAwait(false); - } - - return enumerator.Current!; - } + => await GetCurrentPageAsyncCore().ConfigureAwait(false); public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { - IAsyncEnumerator> enumerator = GetAsyncEnumeratorCore(cancellationToken); - - do + await foreach (PageResult page in this.WithCancellation(cancellationToken).ConfigureAwait(false)) { - if (enumerator.Current is not null) + foreach (T value in page.Values) { - foreach (T value in enumerator.Current.Values) - { - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - yield return value; - } + yield return value; } } - while (await enumerator.MoveNextAsync().ConfigureAwait(false)); } + protected abstract Task> GetCurrentPageAsyncCore(); + protected abstract IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default); IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 236d997ffd392..0b3c0b8a5c882 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -20,34 +20,21 @@ protected PageCollection() : base() } public PageResult GetCurrentPage() - { - IEnumerator> enumerator = GetEnumeratorCore(); - - if (enumerator.Current == null) - { - enumerator.MoveNext(); - } - - return enumerator.Current!; - } + => GetCurrentPageCore(); public IEnumerable GetAllValues() { - IEnumerator> enumerator = GetEnumeratorCore(); - - do + foreach (PageResult page in this) { - if (enumerator.Current is not null) + foreach (T value in page.Values) { - foreach (T value in enumerator.Current.Values) - { - yield return value; - } + yield return value; } } - while (enumerator.MoveNext()); } + protected abstract PageResult GetCurrentPageCore(); + protected abstract IEnumerator> GetEnumeratorCore(); IEnumerator> IEnumerable>.GetEnumerator() diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs index 138c9f5b3b0d0..4d2b9a7d53763 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockAsyncPageCollection.cs @@ -22,20 +22,28 @@ public MockAsyncPageCollection(List values, int pageSize) _pageSize = pageSize; } + protected override async Task> GetCurrentPageAsyncCore() + => await GetPageFromCurrentStateAsync().ConfigureAwait(false); + protected override async IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken) { while (_current < _values.Count) { - int pageSize = Math.Min(_pageSize, _values.Count - _current); - List pageValues = _values.GetRange(_current, pageSize); - - // Make page tokens not useful for mocks. - ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); - yield return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + yield return await GetPageFromCurrentStateAsync().ConfigureAwait(false); _current += _pageSize; - - await Task.Delay(0); } } + + private async Task> GetPageFromCurrentStateAsync() + { + await Task.Delay(0); + + int pageSize = Math.Min(_pageSize, _values.Count - _current); + List pageValues = _values.GetRange(_current, pageSize); + + // Make page tokens not useful for mocks. + ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); + return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + } } diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs index 9ae46af12fe61..47fa94c12c16e 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockPageCollection.cs @@ -20,18 +20,26 @@ public MockPageCollection(List values, int pageSize) _pageSize = pageSize; } + protected override PageResult GetCurrentPageCore() + => GetPageFromCurrentState(); + protected override IEnumerator> GetEnumeratorCore() { while (_current < _values.Count) { - int pageSize = Math.Min(_pageSize, _values.Count - _current); - List pageValues = _values.GetRange(_current, pageSize); - - // Make page tokens not useful for mocks. - ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); - yield return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + yield return GetPageFromCurrentState(); _current += _pageSize; } } + + private PageResult GetPageFromCurrentState() + { + int pageSize = Math.Min(_pageSize, _values.Count - _current); + List pageValues = _values.GetRange(_current, pageSize); + + // Make page tokens not useful for mocks. + ContinuationToken mockPageToken = ContinuationToken.FromBytes(BinaryData.FromString("{}")); + return PageResult.Create(pageValues, mockPageToken, null, new MockPipelineResponse(200)); + } } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs index c195644d6092f..3b1d1649b57d9 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs @@ -4,6 +4,7 @@ using System.ClientModel; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; namespace ClientModel.Tests.Paging; @@ -48,6 +49,9 @@ public EnumeratorPageCollection(PageEnumerator enumerator) _enumerator = enumerator; } + protected override PageResult GetCurrentPageCore() + => _enumerator.GetCurrentPage(); + protected override IEnumerator> GetEnumeratorCore() => _enumerator; } @@ -61,6 +65,9 @@ public AsyncEnumeratorPageCollection(PageEnumerator enumerator) _enumerator = enumerator; } + protected override async Task> GetCurrentPageAsyncCore() + => await _enumerator.GetCurrentPageAsync().ConfigureAwait(false); + protected override IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default) => _enumerator; } diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs index 0227931a980c7..53527a1e1a187 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageEnumerator.cs @@ -3,6 +3,7 @@ using System.ClientModel; using System.Collections.Generic; +using System.Threading.Tasks; namespace ClientModel.Tests.Paging; @@ -12,6 +13,26 @@ internal abstract class PageEnumerator : PageResultEnumerator, { public abstract PageResult GetPageFromResult(ClientResult result); + public PageResult GetCurrentPage() + { + if (Current is null) + { + return GetPageFromResult(GetFirst()); + } + + return ((IEnumerator>)this).Current; + } + + public async Task> GetCurrentPageAsync() + { + if (Current is null) + { + return GetPageFromResult(await GetFirstAsync().ConfigureAwait(false)); + } + + return ((IEnumerator>)this).Current; + } + PageResult IEnumerator>.Current { get From 4a64e562affbe1edfb019497ed95205145c01683 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 15:07:02 -0700 Subject: [PATCH 38/49] complete sync scenario tests --- .../PageCollectionScenarioTests.cs | 540 +++--------------- .../tests/Convenience/PageCollectionTests.cs | 4 +- .../Emitted/PageCollectionHelpers.cs | 16 +- .../PagingClient/ValuesPageEnumerator.cs | 2 + .../ValuesPageResultEnumerator.cs | 25 +- 5 files changed, 105 insertions(+), 482 deletions(-) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs index e00ad639546bc..b77990e7e864c 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs @@ -13,107 +13,14 @@ namespace System.ClientModel.Tests.Paging; -// Scenario tests for sync and async page collections +/// +/// Scenario tests for sync and async page collections. +/// public class PageScenarioCollectionTests { // TODO: Async // TODO: A few more tests - from commented-out tests - [Test] - public void CanGetAllValues() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - IEnumerable values = pages.GetAllValues(); - - int count = 0; - foreach (ValueItem value in values) - { - Assert.AreEqual(count, value.Id); - count++; - } - - Assert.AreEqual(MockPagingData.Count, count); - } - - [Test] - public void CanGetCurrentPage() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - PageResult page = pages.GetCurrentPage(); - - Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); - Assert.AreEqual(0, page.Values[0].Id); - } - - [Test] - public void CanGetCurrentPageThenGetAllItems() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - - PageResult page = pages.GetCurrentPage(); - - Assert.AreEqual(MockPagingData.DefaultPageSize, page.Values.Count); - Assert.AreEqual(0, page.Values[0].Id); - - IEnumerable values = pages.GetAllValues(); - - int count = 0; - foreach (ValueItem value in values) - { - Assert.AreEqual(count, value.Id); - count++; - } - - Assert.AreEqual(MockPagingData.Count, count); - } - - [Test] - public void CanGetCurrentPageWhileEnumeratingItems() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - PageCollection pages = client.GetValues(); - - IEnumerable values = pages.GetAllValues(); - - int count = 0; - foreach (ValueItem value in values) - { - Assert.AreEqual(count, value.Id); - count++; - - PageResult page = pages.GetCurrentPage(); - - // Validate that the current item is in range of the page values - Assert.GreaterOrEqual(value.Id, page.Values[0].Id); - Assert.LessOrEqual(value.Id, page.Values[page.Values.Count - 1].Id); - } - - Assert.AreEqual(MockPagingData.Count, count); - } - [Test] public void CanRehydratePageCollection() { @@ -142,38 +49,6 @@ public void CanRehydratePageCollection() } } - [Test] - public void CanCastToConvenienceFromProtocol() - { - PagingClientOptions options = new() - { - Transport = new MockPipelineTransport("Mock", i => 200) - }; - - PagingClient client = new PagingClient(options); - - // Call the protocol method on the convenience client. - IEnumerable pageResults = client.GetValues( - order: default, - pageSize: default, - offset: default, - new RequestOptions()); - - // Cast to convience type from protocol return value. - PageCollection pages = (PageCollection)pageResults; - - IEnumerable values = pages.GetAllValues(); - - int count = 0; - foreach (ValueItem value in values) - { - Assert.AreEqual(count, value.Id); - count++; - } - - Assert.AreEqual(MockPagingData.Count, count); - } - [Test] public void CanReorderItemsAndRehydrate() { @@ -279,7 +154,11 @@ public void CanChangeAllCollectionParametersAndRehydrate() PageCollection rehydratedPages = client.GetValues(pageToken); PageResult rehydratedPage = rehydratedPages.GetCurrentPage(); - // Both page collections and first page are the same on each dimension + // Both page collections and first pages are the same on each dimension + + // Collections have same non-default number of pages. + Assert.AreEqual(3, pages.Count()); + Assert.AreEqual(3, rehydratedPages.Count()); // Last one first and same items skipped Assert.AreEqual(11, page.Values[0].Id); @@ -290,351 +169,82 @@ public void CanChangeAllCollectionParametersAndRehydrate() Assert.AreEqual(pageSize, rehydratedPage.Values.Count); } - //[Test] - //public void CanEnumeratePages() - //{ - // List values = new() { 0, 1, 2, 3 }; - // int pageSize = 2; - - // List mockResults = new() { - // new MockClientResult(new MockPipelineResponse(0)), - // new MockClientResult(new MockPipelineResponse(1)) - // }; - - // PageCollection pages = new MockPageCollection(values, mockResults, pageSize); - - // int i = 0; - // foreach (PageResult page in pages) - // { - // Assert.AreEqual(i++, page.Values[0]); - // Assert.AreEqual(i++, page.Values[1]); - // } - - // Assert.AreEqual(4, i); - //} - - //[Test] - //public void CanEnumerateClientResults() - //{ - // List mockResults = new() { - // new MockClientResult(new MockPipelineResponse(0)), - // new MockClientResult(new MockPipelineResponse(1)) - // }; - - // IEnumerable results = new ProtocolMockPageCollection(mockResults); - - // int i = 0; - // foreach (ClientResult result in results) - // { - // Assert.AreEqual(i++, result.GetRawResponse().Status); - // } - - // Assert.AreEqual(2, i); - //} - - //[Test] - //public void CanEvolveFromProtocol() - //{ - // List values = new() { 0, 1, 2, 3 }; - // int pageSize = 2; - - // List mockResults = new() { - // new MockClientResult(new MockPipelineResponse(0)), - // new MockClientResult(new MockPipelineResponse(1)) - // }; - - // // Showing that we can use the same code as protocol-only - // // with a convenience return type. - // IEnumerable results = new MockPageCollection(values, mockResults, pageSize); - - // int i = 0; - // foreach (ClientResult result in results) - // { - // Assert.AreEqual(i++, result.GetRawResponse().Status); - // } - - // Assert.AreEqual(2, i); - //} - - //private static readonly string[] MockPageContents = { """ - // [ - // { "intValue" : 0, "stringValue" : "0" }, - // { "intValue" : 1, "stringValue" : "1" }, - // { "intValue" : 2, "stringValue" : "2" } - // ] - // """,""" - // [ - // { "intValue" : 3, "stringValue" : "3" }, - // { "intValue" : 4, "stringValue" : "4" }, - // { "intValue" : 5, "stringValue" : "5" } - // ] - // """,""" - // [ - // { "intValue" : 6, "stringValue" : "6" }, - // { "intValue" : 7, "stringValue" : "7" }, - // { "intValue" : 8, "stringValue" : "8" } - // ] - // """, - // }; - - //private static readonly int PageCount = MockPageContents.Length; - //private static readonly int ItemCount = 9; - - //[Test] - //public void CanEnumerateValues() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int i = 0; - // foreach (MockJsonModel model in models) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // Assert.AreEqual(ItemCount, i); - //} - - //[Test] - //public void CanEnumeratePages() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - //[Test] - //public void CanStartPageEnumerationMidwayThrough() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int pageCount = 0; - // int i = 6; - - // // Request just the last page by starting at the last seen value - // // on the prior page -- i.e. item 5. - // foreach (ResultPage page in models.AsPages(continuationToken: "5")) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, i); - // Assert.AreEqual(1, pageCount); - //} - - //[Test] - //public void CanSetPageSizeHint() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - // var pages = models.AsPages(pageSizeHint: 10); - // foreach (var _ in pages) - // { - // // page size hint is ignored in this mock - // } - - // Assert.AreEqual(10, client.RequestedPageSize); - //} - - //[Test] - //public void CanGetRawResponses() - //{ - // MockPageableClient client = new(); - // PageableCollection models = client.GetModels(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // PipelineResponse collectionResponse = models.GetRawResponse(); - // PipelineResponse pageResponse = page.GetRawResponse(); - - // Assert.AreEqual(pageResponse, collectionResponse); - // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); - // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - //[Test] - //public async Task CanEnumerateValuesAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int i = 0; - // await foreach (MockJsonModel model in models) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // Assert.AreEqual(ItemCount, i); - //} - - //[Test] - //public async Task CanEnumeratePagesAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // await foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - //[Test] - //public async Task CanStartPageEnumerationMidwayThroughAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int pageCount = 0; - // int i = 6; - - // // Request just the last page by starting at the last seen value - // // on the prior page -- i.e. item 5. - // await foreach (ResultPage page in models.AsPages(continuationToken: "5")) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(i, model.IntValue); - // Assert.AreEqual(i.ToString(), model.StringValue); - - // i++; - // } - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, i); - // Assert.AreEqual(1, pageCount); - //} - - //[Test] - //public async Task CanSetPageSizeHintAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - // var pages = models.AsPages(pageSizeHint: 10); - // await foreach (var _ in pages) - // { - // // page size hint is ignored in this mock - // } - - // Assert.AreEqual(10, client.RequestedPageSize); - //} - - //[Test] - //public async Task CanGetRawResponsesAsync() - //{ - // MockPageableClient client = new(); - // AsyncPageableCollection models = client.GetModelsAsync(MockPageContents); - - // int pageCount = 0; - // int itemCount = 0; - // await foreach (ResultPage page in models.AsPages()) - // { - // foreach (MockJsonModel model in page) - // { - // Assert.AreEqual(itemCount, model.IntValue); - // Assert.AreEqual(itemCount.ToString(), model.StringValue); - - // itemCount++; - // } - - // PipelineResponse collectionResponse = models.GetRawResponse(); - // PipelineResponse pageResponse = page.GetRawResponse(); - - // Assert.AreEqual(pageResponse, collectionResponse); - // Assert.AreEqual(MockPageContents[pageCount], pageResponse.Content.ToString()); - // Assert.AreEqual(MockPageContents[pageCount], collectionResponse.Content.ToString()); - - // pageCount++; - // } - - // Assert.AreEqual(ItemCount, itemCount); - // Assert.AreEqual(PageCount, pageCount); - //} - - #region Helpers - - internal class ProtocolMockPageCollection : IEnumerable + [Test] + public void CanCastToConvenienceFromProtocol() { - private readonly List _results; + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + + // Call the protocol method on the convenience client. + IEnumerable pageResults = client.GetValues( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + // Cast to convience type from protocol return value. + PageCollection pages = (PageCollection)pageResults; - public ProtocolMockPageCollection(List results) + IEnumerable values = pages.GetAllValues(); + + int count = 0; + foreach (ValueItem value in values) { - _results = results; + Assert.AreEqual(count, value.Id); + count++; } - public IEnumerator GetEnumerator() + Assert.AreEqual(MockPagingData.Count, count); + } + + [Test] + public void CanEvolveFromProtocol() + { + // This scenario tests validates that user code doesn't break when + // convenience methods are added. We show this by illustrating that + // exactly the same code works the same way when using a client that + // has only protocol methods and a client that has the same protocol + // methods and also convenience methods. + + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + static void Validate(IEnumerable results) { - foreach (ClientResult result in _results) + int pageCount = 0; + foreach (ClientResult result in results) { - yield return result; + Assert.AreEqual(200, result.GetRawResponse().Status); + pageCount++; } + + Assert.AreEqual(MockPagingData.Count / MockPagingData.DefaultPageSize, pageCount); } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } + // Protocol code + PagingProtocolClient protocolClient = new PagingProtocolClient(options); + IEnumerable pageResults = protocolClient.GetValues( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + Validate(pageResults); + + // Convenience code + PagingClient convenienceClient = new PagingClient(options); + IEnumerable pages = convenienceClient.GetValues( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); - #endregion + Validate(pages); + } } diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index c43d0bb77e633..2a39089955ce7 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -12,7 +12,9 @@ namespace System.ClientModel.Tests.Paging; -// Unit tests for sync and async page collections +/// +/// Unit tests for sync and async page collections. +/// public class PageCollectionTests { private const int Count = 16; diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs index 3b1d1649b57d9..21b5fba1a4b24 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/Emitted/PageCollectionHelpers.cs @@ -18,26 +18,18 @@ public static AsyncPageCollection CreateAsync(PageEnumerator enumerator public static IEnumerable Create(PageResultEnumerator enumerator) { - do + while (enumerator.MoveNext()) { - if (enumerator.Current is not null) - { - yield return enumerator.Current; - } + yield return enumerator.Current; } - while (enumerator.MoveNext()); } public static async IAsyncEnumerable CreateAsync(PageResultEnumerator enumerator) { - do + while (await enumerator.MoveNextAsync().ConfigureAwait(false)) { - if (enumerator.Current is not null) - { - yield return enumerator.Current; - } + yield return enumerator.Current; } - while (await enumerator.MoveNextAsync().ConfigureAwait(false)); } private class EnumeratorPageCollection : PageCollection diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs index b2afcfe033256..99a3cdfc3c264 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -9,6 +9,8 @@ namespace ClientModel.Tests.Paging; +// Mocks a page enumerator a client would evolve to for paged endpoints when +// the client adds convenience methods. internal class ValuesPageEnumerator : PageEnumerator { private readonly ClientPipeline _pipeline; diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index fe011f4dbd4ac..b3c364c20c895 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -19,9 +19,12 @@ internal class ValuesPageResultEnumerator : PageResultEnumerator private readonly string? _order; private readonly int? _pageSize; - // This one is special - it keep track of which page we're on. + // This one is special - it keeps track of which page we're on. private int? _offset; + // We need two offsets to be able to create both page tokens. + private int _nextOffset; + private readonly RequestOptions? _options; public ValuesPageResultEnumerator( @@ -45,7 +48,9 @@ public ValuesPageResultEnumerator( public override ClientResult GetFirst() { ClientResult result = GetValuesPage(_order, _pageSize, _offset); - _offset += _pageSize; + + _nextOffset = GetNextOffset(_offset, _pageSize); + return result; } @@ -56,8 +61,12 @@ public override Task GetFirstAsync() public override ClientResult GetNext(ClientResult result) { + _offset = _nextOffset; + ClientResult pageResult = GetValuesPage(_order, _pageSize, _offset); - _offset += _pageSize; + + _nextOffset = GetNextOffset(_offset, _pageSize); + return pageResult; } @@ -68,7 +77,7 @@ public override Task GetNextAsync(ClientResult result) public override bool HasNext(ClientResult result) { - return _offset < MockPagingData.Count; + return _nextOffset < MockPagingData.Count; } // In a real client implementation, thes would be the generated protocol @@ -82,4 +91,12 @@ internal virtual ClientResult GetValuesPage( IEnumerable values = MockPagingData.GetValues(order, pageSize, offset); return MockPagingData.GetPageResult(values); } + + // This helper method is specific to this mock enumerator implementation + private static int GetNextOffset(int? offset, int? pageSize) + { + offset ??= MockPagingData.DefaultOffset; + pageSize ??= MockPagingData.DefaultPageSize; + return offset.Value + pageSize.Value; + } } From ff84661b0a29086793296607bf3f7767d5d9879e Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 15:23:59 -0700 Subject: [PATCH 39/49] Add async tests --- .../PageCollectionScenarioTests.cs | 230 +++++++++++++++++- .../System.ClientModel.Tests.Client.csproj | 1 + .../PagingClient/ValuesPageEnumerator.cs | 31 ++- .../ValuesPageResultEnumerator.cs | 31 ++- 4 files changed, 280 insertions(+), 13 deletions(-) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs index b77990e7e864c..b69a6bde1fbb5 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs @@ -18,9 +18,6 @@ namespace System.ClientModel.Tests.Paging; /// public class PageScenarioCollectionTests { - // TODO: Async - // TODO: A few more tests - from commented-out tests - [Test] public void CanRehydratePageCollection() { @@ -49,6 +46,34 @@ public void CanRehydratePageCollection() } } + [Test] + public async Task CanRehydratePageCollectionAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + AsyncPageCollection pages = client.GetValuesAsync(); + PageResult page = await pages.GetCurrentPageAsync(); + + ContinuationToken pageToken = page.PageToken; + + AsyncPageCollection rehydratedPages = client.GetValuesAsync(pageToken); + PageResult rehydratedPage = await rehydratedPages.GetCurrentPageAsync(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + List allValues = await pages.GetAllValuesAsync().ToListAsync(); + List allRehydratedValues = await rehydratedPages.GetAllValuesAsync().ToListAsync(); + + for (int i = 0; i < allValues.Count; i++) + { + Assert.AreEqual(allValues[i].Id, allRehydratedValues[i].Id); + } + } + [Test] public void CanReorderItemsAndRehydrate() { @@ -76,6 +101,33 @@ public void CanReorderItemsAndRehydrate() Assert.AreEqual(MockPagingData.Count - 1, rehydratedPage.Values[0].Id); } + [Test] + public async Task CanReorderItemsAndRehydrateAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + string order = "desc"; + Assert.AreNotEqual(MockPagingData.DefaultOrder, order); + + PagingClient client = new PagingClient(options); + AsyncPageCollection pages = client.GetValuesAsync(order: order); + PageResult page = await pages.GetCurrentPageAsync(); + + ContinuationToken pageToken = page.PageToken; + + AsyncPageCollection rehydratedPages = client.GetValuesAsync(pageToken); + PageResult rehydratedPage = await rehydratedPages.GetCurrentPageAsync(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + // We got the last one first from both pages + Assert.AreEqual(MockPagingData.Count - 1, page.Values[0].Id); + Assert.AreEqual(MockPagingData.Count - 1, rehydratedPage.Values[0].Id); + } + [Test] public void CanChangePageSizeAndRehydrate() { @@ -101,6 +153,31 @@ public void CanChangePageSizeAndRehydrate() Assert.AreEqual(pageSize, rehydratedPage.Values.Count); } + [Test] + public async Task CanChangePageSizeAndRehydrateAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + int pageSize = 4; + Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); + + PagingClient client = new PagingClient(options); + AsyncPageCollection pages = client.GetValuesAsync(pageSize: pageSize); + PageResult page = await pages.GetCurrentPageAsync(); + + ContinuationToken pageToken = page.PageToken; + + AsyncPageCollection rehydratedPages = client.GetValuesAsync(pageToken); + PageResult rehydratedPage = await rehydratedPages.GetCurrentPageAsync(); + + // Both pages have same non-default page size + Assert.AreEqual(pageSize, page.Values.Count); + Assert.AreEqual(pageSize, rehydratedPage.Values.Count); + } + [Test] public void CanSkipItemsAndRehydrate() { @@ -128,6 +205,33 @@ public void CanSkipItemsAndRehydrate() Assert.AreEqual(offset, rehydratedPage.Values[0].Id); } + [Test] + public async Task CanSkipItemsAndRehydrateAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + int offset = 4; + Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); + + PagingClient client = new PagingClient(options); + AsyncPageCollection pages = client.GetValuesAsync(offset: offset); + PageResult page = await pages.GetCurrentPageAsync(); + + ContinuationToken pageToken = page.PageToken; + + AsyncPageCollection rehydratedPages = client.GetValuesAsync(pageToken); + PageResult rehydratedPage = await rehydratedPages.GetCurrentPageAsync(); + + Assert.AreEqual(page.Values.Count, rehydratedPage.Values.Count); + + // Both pages have the same non-default offset value + Assert.AreEqual(offset, page.Values[0].Id); + Assert.AreEqual(offset, rehydratedPage.Values[0].Id); + } + [Test] public void CanChangeAllCollectionParametersAndRehydrate() { @@ -169,6 +273,47 @@ public void CanChangeAllCollectionParametersAndRehydrate() Assert.AreEqual(pageSize, rehydratedPage.Values.Count); } + [Test] + public async Task CanChangeAllCollectionParametersAndRehydrateAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + string order = "desc"; + Assert.AreNotEqual(MockPagingData.DefaultOrder, order); + + int pageSize = 4; + Assert.AreNotEqual(MockPagingData.DefaultPageSize, pageSize); + + int offset = 4; + Assert.AreNotEqual(MockPagingData.DefaultOffset, offset); + + PagingClient client = new PagingClient(options); + AsyncPageCollection pages = client.GetValuesAsync(order, pageSize, offset); + PageResult page = await pages.GetCurrentPageAsync(); + + ContinuationToken pageToken = page.PageToken; + + AsyncPageCollection rehydratedPages = client.GetValuesAsync(pageToken); + PageResult rehydratedPage = await rehydratedPages.GetCurrentPageAsync(); + + // Both page collections and first pages are the same on each dimension + + // Collections have same non-default number of pages. + Assert.AreEqual(3, await pages.CountAsync()); + Assert.AreEqual(3, await rehydratedPages.CountAsync()); + + // Last one first and same items skipped + Assert.AreEqual(11, page.Values[0].Id); + Assert.AreEqual(11, rehydratedPage.Values[0].Id); + + // Equal page size + Assert.AreEqual(pageSize, page.Values.Count); + Assert.AreEqual(pageSize, rehydratedPage.Values.Count); + } + [Test] public void CanCastToConvenienceFromProtocol() { @@ -201,6 +346,38 @@ public void CanCastToConvenienceFromProtocol() Assert.AreEqual(MockPagingData.Count, count); } + [Test] + public async Task CanCastToConvenienceFromProtocolAsync() + { + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + PagingClient client = new PagingClient(options); + + // Call the protocol method on the convenience client. + IAsyncEnumerable pageResults = client.GetValuesAsync( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + // Cast to convience type from protocol return value. + AsyncPageCollection pages = (AsyncPageCollection)pageResults; + + IAsyncEnumerable values = pages.GetAllValuesAsync(); + + int count = 0; + await foreach (ValueItem value in values) + { + Assert.AreEqual(count, value.Id); + count++; + } + + Assert.AreEqual(MockPagingData.Count, count); + } + [Test] public void CanEvolveFromProtocol() { @@ -247,4 +424,51 @@ static void Validate(IEnumerable results) Validate(pages); } + + [Test] + public async Task CanEvolveFromProtocolAsync() + { + // This scenario tests validates that user code doesn't break when + // convenience methods are added. We show this by illustrating that + // exactly the same code works the same way when using a client that + // has only protocol methods and a client that has the same protocol + // methods and also convenience methods. + + PagingClientOptions options = new() + { + Transport = new MockPipelineTransport("Mock", i => 200) + }; + + static async Task ValidateAsync(IAsyncEnumerable results) + { + int pageCount = 0; + await foreach (ClientResult result in results) + { + Assert.AreEqual(200, result.GetRawResponse().Status); + pageCount++; + } + + Assert.AreEqual(MockPagingData.Count / MockPagingData.DefaultPageSize, pageCount); + } + + // Protocol code + PagingProtocolClient protocolClient = new PagingProtocolClient(options); + IAsyncEnumerable pageResults = protocolClient.GetValuesAsync( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + await ValidateAsync(pageResults); + + // Convenience code + PagingClient convenienceClient = new PagingClient(options); + IAsyncEnumerable pages = convenienceClient.GetValuesAsync( + order: default, + pageSize: default, + offset: default, + new RequestOptions()); + + await ValidateAsync(pages); + } } diff --git a/sdk/core/System.ClientModel/tests/client/System.ClientModel.Tests.Client.csproj b/sdk/core/System.ClientModel/tests/client/System.ClientModel.Tests.Client.csproj index 8a60f3bfaac57..9ed77bfb7b69f 100644 --- a/sdk/core/System.ClientModel/tests/client/System.ClientModel.Tests.Client.csproj +++ b/sdk/core/System.ClientModel/tests/client/System.ClientModel.Tests.Client.csproj @@ -12,6 +12,7 @@ + diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs index 99a3cdfc3c264..67d32ba833db3 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageEnumerator.cs @@ -65,9 +65,13 @@ public override ClientResult GetFirst() return result; } - public override Task GetFirstAsync() + public override async Task GetFirstAsync() { - throw new NotImplementedException(); + ClientResult result = await GetValuesPageAsync(_order, _pageSize, _offset).ConfigureAwait(false); + + _nextOffset = GetNextOffset(_offset, _pageSize); + + return result; } public override ClientResult GetNext(ClientResult result) @@ -81,9 +85,15 @@ public override ClientResult GetNext(ClientResult result) return pageResult; } - public override Task GetNextAsync(ClientResult result) + public override async Task GetNextAsync(ClientResult result) { - throw new NotImplementedException(); + _offset = _nextOffset; + + ClientResult pageResult = await GetValuesPageAsync(_order, _pageSize, _offset).ConfigureAwait(false); + + _nextOffset = GetNextOffset(_offset, _pageSize); + + return pageResult; } public override bool HasNext(ClientResult result) @@ -91,8 +101,19 @@ public override bool HasNext(ClientResult result) return _nextOffset < MockPagingData.Count; } - // In a real client implementation, thes would be the generated protocol + // In a real client implementation, these would be the generated protocol // method used to obtain a page of items. + internal virtual async Task GetValuesPageAsync( + string? order, + int? pageSize, + int? offset, + RequestOptions? options = default) + { + await Task.Delay(0); + IEnumerable values = MockPagingData.GetValues(order, pageSize, offset); + return MockPagingData.GetPageResult(values); + } + internal virtual ClientResult GetValuesPage( string? order, int? pageSize, diff --git a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs index b3c364c20c895..655d49f4bfe97 100644 --- a/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs +++ b/sdk/core/System.ClientModel/tests/client/TestClients/PagingClient/ValuesPageResultEnumerator.cs @@ -54,9 +54,13 @@ public override ClientResult GetFirst() return result; } - public override Task GetFirstAsync() + public override async Task GetFirstAsync() { - throw new NotImplementedException(); + ClientResult result = await GetValuesPageAsync(_order, _pageSize, _offset).ConfigureAwait(false); + + _nextOffset = GetNextOffset(_offset, _pageSize); + + return result; } public override ClientResult GetNext(ClientResult result) @@ -70,9 +74,15 @@ public override ClientResult GetNext(ClientResult result) return pageResult; } - public override Task GetNextAsync(ClientResult result) + public override async Task GetNextAsync(ClientResult result) { - throw new NotImplementedException(); + _offset = _nextOffset; + + ClientResult pageResult = await GetValuesPageAsync(_order, _pageSize, _offset).ConfigureAwait(false); + + _nextOffset = GetNextOffset(_offset, _pageSize); + + return pageResult; } public override bool HasNext(ClientResult result) @@ -80,8 +90,19 @@ public override bool HasNext(ClientResult result) return _nextOffset < MockPagingData.Count; } - // In a real client implementation, thes would be the generated protocol + // In a real client implementation, these would be the generated protocol // method used to obtain a page of items. + internal virtual async Task GetValuesPageAsync( + string? order, + int? pageSize, + int? offset, + RequestOptions? options = default) + { + await Task.Delay(0); + IEnumerable values = MockPagingData.GetValues(order, pageSize, offset); + return MockPagingData.GetPageResult(values); + } + internal virtual ClientResult GetValuesPage( string? order, int? pageSize, From 77388d3ab840a281adc38c51c23d02ae3b15d411 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 15:30:50 -0700 Subject: [PATCH 40/49] nits --- .../tests/Convenience/PageCollectionScenarioTests.cs | 2 -- .../System.ClientModel/tests/Convenience/PageCollectionTests.cs | 2 -- .../tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs | 1 - 3 files changed, 5 deletions(-) diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs index b69a6bde1fbb5..7efc42d41494e 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionScenarioTests.cs @@ -2,11 +2,9 @@ // Licensed under the MIT License. using System.ClientModel.Primitives; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ClientModel.Tests; using ClientModel.Tests.Mocks; using ClientModel.Tests.Paging; using NUnit.Framework; diff --git a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs index 2a39089955ce7..4a6a2c640d3b2 100644 --- a/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs +++ b/sdk/core/System.ClientModel/tests/Convenience/PageCollectionTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.ClientModel.Primitives; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs index 3f1fe120da63d..da2405a3257f5 100644 --- a/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs +++ b/sdk/core/System.ClientModel/tests/TestFramework/Mocks/MockSyncAsyncExtensions.cs @@ -8,7 +8,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using ClientModel.Tests.Paging; namespace ClientModel.Tests.Mocks; From 6642da1a1828a8c1c0cf53b67ded0a17411e98aa Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Wed, 3 Jul 2024 16:05:16 -0700 Subject: [PATCH 41/49] Add refdocs --- .../src/Convenience/AsyncPageCollectionOfT.cs | 43 ++++++++++++++--- .../src/Convenience/ContinuationToken.cs | 35 ++++++++++++-- .../src/Convenience/PageCollectionOfT.cs | 45 ++++++++++++++---- .../src/Convenience/PageResultOfT.cs | 47 ++++++++++++++----- 4 files changed, 137 insertions(+), 33 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs index 277d94c70d213..7786b60d70f6e 100644 --- a/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/AsyncPageCollectionOfT.cs @@ -8,20 +8,41 @@ namespace System.ClientModel; -#pragma warning disable CS1591 - +/// +/// An asynchronous collection of page results returned by a cloud service. +/// Cloud services use pagination to return a collection of items over multiple +/// 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 . To get the current +/// collection page, call . +/// public abstract class AsyncPageCollection : IAsyncEnumerable> { - // Note page collections delay making a first request until either - // GetPage is called or the collection is enumerated, so the constructor - // calls the base class constructor that does not take a response. + /// + /// Create a new instance of . + /// 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. } + /// + /// Get the current page of the collection. + /// + /// The current page in the collection. public async Task> GetCurrentPageAsync() => await GetCurrentPageAsyncCore().ConfigureAwait(false); + /// + /// Get a collection of all the values in the collection requested from the + /// cloud service, rather than the pages of values. + /// + /// The values requested from the cloud service. public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { await foreach (PageResult page in this.WithCancellation(cancellationToken).ConfigureAwait(false)) @@ -35,12 +56,20 @@ public async IAsyncEnumerable GetAllValuesAsync([EnumeratorCancellation] Canc } } + /// + /// Get the current page of the collection. + /// + /// The current page in the collection. protected abstract Task> GetCurrentPageAsyncCore(); + /// + /// Get an async enumerator that can enumerate the pages of values returned + /// by the cloud service. + /// + /// An async enumerator of pages holding the items in the value + /// collection. protected abstract IAsyncEnumerator> GetAsyncEnumeratorCore(CancellationToken cancellationToken = default); IAsyncEnumerator> IAsyncEnumerable>.GetAsyncEnumerator(CancellationToken cancellationToken) => GetAsyncEnumeratorCore(cancellationToken); } - -#pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs index 9e0ed0f54d3a2..3b9c1363b88f4 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs @@ -5,14 +5,26 @@ namespace System.ClientModel; -#pragma warning disable CS1591 - +/// +/// A token that can be passed to a client method to request or rehydrate a +/// subclient. +/// public class ContinuationToken { private readonly BinaryData? _bytes; + /// + /// Create a new instance of . + /// + /// This constructor is used by derived types to create an + /// instance from values held by the client. protected ContinuationToken() { } + /// + /// Create a new instance of . + /// + /// Bytes that can be deserialized into a subtype of + /// . protected ContinuationToken(BinaryData bytes) { Argument.AssertNotNull(bytes, nameof(bytes)); @@ -20,10 +32,25 @@ protected ContinuationToken(BinaryData bytes) _bytes = bytes; } + /// + /// Create a new instance of from the + /// provided . + /// + /// Bytes obtained from calling + /// on a . + /// A that can be passed to a + /// client method to request or rehydrate a subclient equivalent to the one + /// from which the original bytes were + /// obtained. + /// public static ContinuationToken FromBytes(BinaryData bytes) => new(bytes); + /// + /// Write the bytes of this . + /// + /// The bytes of this . + /// Thrown if not implemented + /// in a derived type. public virtual BinaryData ToBytes() => _bytes ?? throw new InvalidOperationException("Unable to write token as bytes."); } - -#pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs index 0b3c0b8a5c882..6ee7e17201620 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageCollectionOfT.cs @@ -6,22 +6,41 @@ namespace System.ClientModel; -#pragma warning disable CS1591 - -// This type is a client that defines a collection of elements and can -// make service requests to retrieve specific pages +/// +/// A collection of page results returned by a cloud service. Cloud services +/// use pagination to return a collection of items over multiple 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 +/// . To get the current collection page, call +/// . +/// public abstract class PageCollection : IEnumerable> { - // Note page collections delay making a first request until either - // GetPage is called or the collection is enumerated, so the constructor - // calls the base class constructor that does not take a response. + /// + /// Create a new instance of . + /// protected PageCollection() : base() { + // Note that page collections delay making a first request until either + // GetCurrentPage is called or the collection returned by GetAllValues + // is enumerated, so this constructor calls the base class constructor + // that does not take a PipelineResponse. } + /// + /// Get the current page of the collection. + /// + /// The current page in the collection. public PageResult GetCurrentPage() => GetCurrentPageCore(); + /// + /// Get a collection of all the values in the collection requested from the + /// cloud service, rather than the pages of values. + /// + /// The values requested from the cloud service. public IEnumerable GetAllValues() { foreach (PageResult page in this) @@ -33,8 +52,18 @@ public IEnumerable GetAllValues() } } + /// + /// Get the current page of the collection. + /// + /// The current page in the collection. protected abstract PageResult GetCurrentPageCore(); + /// + /// Get an enumerator that can enumerate the pages of values returned by + /// the cloud service. + /// + /// An enumerator of pages holding the items in the value + /// collection. protected abstract IEnumerator> GetEnumeratorCore(); IEnumerator> IEnumerable>.GetEnumerator() @@ -42,5 +71,3 @@ IEnumerator> IEnumerable>.GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator(); } - -#pragma warning restore CS1591 diff --git a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs index 44d9b876d5caf..ae1a4375672a8 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs @@ -7,8 +7,14 @@ namespace System.ClientModel; -#pragma warning disable CS1591 - +/// +/// A page of values returned from a cloud service in response to a request for +/// a collection from a paginated endpoint. When used with +/// or , +/// returns a subset of a complete collection of values returned from a service. +/// Each represents the values in a single service +/// response. +/// public class PageResult : ClientResult { private PageResult(IReadOnlyList values, @@ -24,23 +30,38 @@ private PageResult(IReadOnlyList values, NextPageToken = nextPageToken; } - // The values in the page + /// + /// Gets the values in this . + /// public IReadOnlyList Values { get; } - // The token used to retrieve this page -- can uniquely request - // the page AND uniquely rehydrate a page collection that this is - // a page in (first, page 5, whatever). - // i.e. it completely describes a collection where this is a page - // in it. - // This is useful because I can cache this and retrive both the - // full collection this page is in and/or the current page. + /// + /// Gets a token that can be used to request or rehydrate a collection + /// beginning with this page of values. + /// public ContinuationToken PageToken { get; } - // If this is null, the current page is the last page in a collection. + /// + /// Gets a token that can be used to request or rehydrate a collection + /// beginning with the next page of values after this page. If + /// is null, the current page is the last page + /// of values in the collection. + /// public ContinuationToken? NextPageToken { get; } + /// + /// Create a from the provided parameters. + /// + /// The values in the . + /// + /// A token that can be used to request a collection + /// beginning with this page of values. + /// A token that can be used to request a + /// collection beginning with the next page of values. + /// The response that returned the values in the + /// page. + /// A holding the provided values. + /// public static PageResult Create(IReadOnlyList values, ContinuationToken pageToken, ContinuationToken? nextPageToken, PipelineResponse response) => new(values, pageToken, nextPageToken, response); } - -#pragma warning restore CS1591 From 1ade11cc0c6959b9c9124600953da32ee360551c Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Fri, 5 Jul 2024 10:03:24 -0700 Subject: [PATCH 42/49] address pr feedback on clarifying refdoc comments --- .../src/Convenience/ContinuationToken.cs | 2 +- .../src/Convenience/PageResultOfT.cs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs index 3b9c1363b88f4..353348839dc8f 100644 --- a/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs +++ b/sdk/core/System.ClientModel/src/Convenience/ContinuationToken.cs @@ -7,7 +7,7 @@ namespace System.ClientModel; /// /// A token that can be passed to a client method to request or rehydrate a -/// subclient. +/// subclient from the state represented by the token. /// public class ContinuationToken { diff --git a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs index ae1a4375672a8..c8aa4ca3b77d3 100644 --- a/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs +++ b/sdk/core/System.ClientModel/src/Convenience/PageResultOfT.cs @@ -36,17 +36,19 @@ private PageResult(IReadOnlyList values, public IReadOnlyList Values { get; } /// - /// Gets a token that can be used to request or rehydrate a collection - /// beginning with this page of values. + /// Gets a token that can be passed to a client method to obtain a page + /// collection that begins with this page of values. /// + /// for more details. public ContinuationToken PageToken { get; } /// - /// Gets a token that can be used to request or rehydrate a collection - /// beginning with the next page of values after this page. If + /// Gets a token that can be passed to a client method to obtain a page + /// collection that begins with the page of values after this page. If /// is null, the current page is the last page - /// of values in the collection. + /// in the page collection. /// + /// for more details. public ContinuationToken? NextPageToken { get; } /// From 361eaf24972cea80db139754b93fe9ae3faa3433 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 14:13:38 -0700 Subject: [PATCH 43/49] update OAI Test csproj to opt out of project reference pipeline --- sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj index 81d68704d1738..21e2be4ef5f6b 100644 --- a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj @@ -42,6 +42,10 @@ + + + + From ba3485a15fbfaa591670ff47bbb91c86b2728c0c Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 14:35:42 -0700 Subject: [PATCH 44/49] add more ExcludeFromProjectReferenceToConversion --- sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj | 5 +++++ .../Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj | 1 + 2 files changed, 6 insertions(+) diff --git a/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj b/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj index f42413e324b5c..0007c281f7e13 100644 --- a/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj +++ b/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj @@ -22,6 +22,11 @@ + + + + + 0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4 diff --git a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj index 21e2be4ef5f6b..31db08882f1af 100644 --- a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj @@ -44,6 +44,7 @@ + From 6d88d0ebc162914c998eabe235f9737dfa78882a Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 15:14:48 -0700 Subject: [PATCH 45/49] add more ExcludeFromProjectReferenceToConversion --- .../src/Azure.AI.OpenAI.Assistants.csproj | 5 +++++ .../tests/Azure.AI.OpenAI.Assistants.Tests.csproj | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj b/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj index 50238e0a57e17..3887f337ab63f 100644 --- a/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj +++ b/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj @@ -20,6 +20,11 @@ + + + + + diff --git a/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj b/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj index ced27dcaa8d79..6ec65ca703faa 100644 --- a/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj @@ -21,6 +21,11 @@ + + + + + From c3e970385ce89706c9590b4482482ef6bd68e81a Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 16:41:34 -0700 Subject: [PATCH 46/49] move build variables --- .../src/Azure.AI.OpenAI.Assistants.csproj | 4 +--- .../tests/Azure.AI.OpenAI.Assistants.Tests.csproj | 5 +++-- sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj | 4 +--- .../Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj | 8 +++----- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj b/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj index 3887f337ab63f..cac9a52b8fbd0 100644 --- a/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj +++ b/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj @@ -18,13 +18,11 @@ - - - + diff --git a/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj b/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj index 6ec65ca703faa..cda34bd5df37a 100644 --- a/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj @@ -10,6 +10,9 @@ + + + @@ -22,8 +25,6 @@ - - diff --git a/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj b/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj index 0007c281f7e13..d516593712d5e 100644 --- a/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj +++ b/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj @@ -20,9 +20,7 @@ - - - + diff --git a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj index 31db08882f1af..b4c87b7ae7f06 100644 --- a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj @@ -16,6 +16,9 @@ + + + @@ -42,11 +45,6 @@ - - - - - From c467fcbd2c0875fac4c8c01f20a9a61fc20031ef Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 17:05:21 -0700 Subject: [PATCH 47/49] temporarily opt-out Azure.Core.TestFramework --- .../src/Azure.Core.TestFramework.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj b/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj index 3dea180e80824..1e26162909f65 100644 --- a/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj +++ b/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj @@ -24,6 +24,9 @@ + + + From a312bec7c7e42566f4155e173d08f0da726edd34 Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 17:20:29 -0700 Subject: [PATCH 48/49] revert TestFramework change and do it for Azure.Core instead --- .../src/Azure.Core.TestFramework.csproj | 3 --- sdk/core/Azure.Core/src/Azure.Core.csproj | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj b/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj index 1e26162909f65..3dea180e80824 100644 --- a/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj +++ b/sdk/core/Azure.Core.TestFramework/src/Azure.Core.TestFramework.csproj @@ -24,9 +24,6 @@ - - - diff --git a/sdk/core/Azure.Core/src/Azure.Core.csproj b/sdk/core/Azure.Core/src/Azure.Core.csproj index 678fb1e38bb4d..c64fe1233b111 100644 --- a/sdk/core/Azure.Core/src/Azure.Core.csproj +++ b/sdk/core/Azure.Core/src/Azure.Core.csproj @@ -28,6 +28,8 @@ + + From a974063402cb9327c2518c1174b7214cf6f4201f Mon Sep 17 00:00:00 2001 From: Anne Thompson Date: Mon, 8 Jul 2024 19:55:00 -0700 Subject: [PATCH 49/49] try it without OAI csproj mods --- .../src/Azure.AI.OpenAI.Assistants.csproj | 3 --- .../tests/Azure.AI.OpenAI.Assistants.Tests.csproj | 6 ------ sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj | 3 --- .../Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj | 3 --- 4 files changed, 15 deletions(-) diff --git a/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj b/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj index cac9a52b8fbd0..50238e0a57e17 100644 --- a/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj +++ b/sdk/openai/Azure.AI.OpenAI.Assistants/src/Azure.AI.OpenAI.Assistants.csproj @@ -18,9 +18,6 @@ - - - diff --git a/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj b/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj index cda34bd5df37a..ced27dcaa8d79 100644 --- a/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI.Assistants/tests/Azure.AI.OpenAI.Assistants.Tests.csproj @@ -10,9 +10,6 @@ - - - @@ -24,9 +21,6 @@ - - - diff --git a/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj b/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj index d516593712d5e..f42413e324b5c 100644 --- a/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj +++ b/sdk/openai/Azure.AI.OpenAI/src/Azure.AI.OpenAI.csproj @@ -20,9 +20,6 @@ - - - diff --git a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj index b4c87b7ae7f06..81d68704d1738 100644 --- a/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj +++ b/sdk/openai/Azure.AI.OpenAI/tests/Azure.AI.OpenAI.Tests.csproj @@ -16,9 +16,6 @@ - - -