-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ClientModel: AsyncResultCollection<T> and SSE event collection implem…
…entation (#43840) * Add async and sync enumerable client results * Implement IAsyncDisposable * Remove constrant and disposable; update ClientResult so response can be replaced in a polling paradigm * rename and upate tests * initial addition of files from joseharriaga/openai-in-typespec#68 * make it build * hello world test * bootstrap more tests * more internal tests * adding enumerator tests; haven't figured out the batch piece yet * Make batch test pass * remove collection-event functionality and add tests for public type * reshuffle * Add mock convenience SSE type to give POC of lazy request sending * add tests of delayed request * Add BinaryData factory method * remove funcs for creating enumerators * renames * postpone call to protocol method from convenience APIs * implement IAsyncDisposable correctly * initial pass over cancellation token * Per FDG, throw OperationCanceledException if cancellation token is cancelled. * remove factory method taking Func<T> and provide example of layering convenience implementation in a way that postpones sending the request * rename internal types and WIP adding reader tests * nits * parameterize terminal event; TBD to provide virtual method on collection type * WIP: nits * WIP: added concatenation of data lines per SSE spec * updates and bug fixes * add tests and update per SSE spec * WIP: refactor to reuse field processing across sync and async methods * make look a little more like the BCL type proposal * simplify field implementation a bit * cosmetic reworking of creating an event from a pending event * Remove factory method from public API; move MockSseClient to Tests.Internal to access internal SSE types * update API; reimplement mock client implementations without internal BinaryData enumerable * Add sync client result collection abstraction * tidy up and add tests * add default constructor to ClientResult * more tidy-up * rename and add refdocs * comments * pr fb * rework last event id and retry per BCL design shift * add CHANGELOG entry
- Loading branch information
1 parent
8d0a33c
commit 2a0e020
Showing
22 changed files
with
1,512 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
sdk/core/System.ClientModel/src/Convenience/AsyncResultCollectionOfT.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.ClientModel.Primitives; | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
|
||
namespace System.ClientModel; | ||
|
||
/// <summary> | ||
/// Represents a collection of results returned from a cloud service operation. | ||
/// </summary> | ||
public abstract class AsyncResultCollection<T> : ClientResult, IAsyncEnumerable<T> | ||
{ | ||
/// <summary> | ||
/// Create a new instance of <see cref="AsyncResultCollection{T}"/>. | ||
/// </summary> | ||
/// <remarks>If no <see cref="PipelineResponse"/> is provided when the | ||
/// <see cref="ClientResult"/> instance is created, it is expected that | ||
/// a derived type will call <see cref="ClientResult.SetRawResponse(PipelineResponse)"/> | ||
/// prior to a user calling <see cref="ClientResult.GetRawResponse"/>. | ||
/// This constructor is indended for use by collection implementations that | ||
/// postpone sending a request until <see cref="GetAsyncEnumerator(CancellationToken)"/> | ||
/// is called. Such implementations will typically be returned from client | ||
/// convenience methods so that callers of the methods don't need to | ||
/// dispose the return value. </remarks> | ||
protected internal AsyncResultCollection() : base() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Create a new instance of <see cref="AsyncResultCollection{T}"/>. | ||
/// </summary> | ||
/// <param name="response">The <see cref="PipelineResponse"/> holding the | ||
/// items in the collection, or the first set of the items in the collection. | ||
/// </param> | ||
protected internal AsyncResultCollection(PipelineResponse response) : base(response) | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public abstract IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
sdk/core/System.ClientModel/src/Convenience/ResultCollectionOfT.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// 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; | ||
|
||
/// <summary> | ||
/// Represents a collection of results returned from a cloud service operation. | ||
/// </summary> | ||
public abstract class ResultCollection<T> : ClientResult, IEnumerable<T> | ||
{ | ||
/// <summary> | ||
/// Create a new instance of <see cref="ResultCollection{T}"/>. | ||
/// </summary> | ||
/// <remarks>If no <see cref="PipelineResponse"/> is provided when the | ||
/// <see cref="ClientResult"/> instance is created, it is expected that | ||
/// a derived type will call <see cref="ClientResult.SetRawResponse(PipelineResponse)"/> | ||
/// prior to a user calling <see cref="ClientResult.GetRawResponse"/>. | ||
/// This constructor is indended for use by collection implementations that | ||
/// postpone sending a request until <see cref="GetEnumerator()"/> | ||
/// is called. Such implementations will typically be returned from client | ||
/// convenience methods so that callers of the methods don't need to | ||
/// dispose the return value. </remarks> | ||
protected internal ResultCollection() : base() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Create a new instance of <see cref="ResultCollection{T}"/>. | ||
/// </summary> | ||
/// <param name="response">The <see cref="PipelineResponse"/> holding the | ||
/// items in the collection, or the first set of the items in the collection. | ||
/// </param> | ||
protected internal ResultCollection(PipelineResponse response) : base(response) | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public abstract IEnumerator<T> GetEnumerator(); | ||
|
||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
} |
82 changes: 82 additions & 0 deletions
82
sdk/core/System.ClientModel/src/Internal/SSE/AsyncServerSentEventEnumerable.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace System.ClientModel.Internal; | ||
|
||
/// <summary> | ||
/// Represents a collection of SSE events that can be enumerated as a C# async stream. | ||
/// </summary> | ||
internal class AsyncServerSentEventEnumerable : IAsyncEnumerable<ServerSentEvent> | ||
{ | ||
private readonly Stream _contentStream; | ||
|
||
public AsyncServerSentEventEnumerable(Stream contentStream) | ||
{ | ||
Argument.AssertNotNull(contentStream, nameof(contentStream)); | ||
|
||
_contentStream = contentStream; | ||
|
||
LastEventId = string.Empty; | ||
ReconnectionInterval = Timeout.InfiniteTimeSpan; | ||
} | ||
|
||
public string LastEventId { get; private set; } | ||
|
||
public TimeSpan ReconnectionInterval { get; private set; } | ||
|
||
public IAsyncEnumerator<ServerSentEvent> GetAsyncEnumerator(CancellationToken cancellationToken = default) | ||
{ | ||
return new AsyncServerSentEventEnumerator(_contentStream, this, cancellationToken); | ||
} | ||
|
||
private sealed class AsyncServerSentEventEnumerator : IAsyncEnumerator<ServerSentEvent> | ||
{ | ||
private readonly ServerSentEventReader _reader; | ||
private readonly AsyncServerSentEventEnumerable _enumerable; | ||
private readonly CancellationToken _cancellationToken; | ||
|
||
public ServerSentEvent Current { get; private set; } | ||
|
||
public AsyncServerSentEventEnumerator(Stream contentStream, | ||
AsyncServerSentEventEnumerable enumerable, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
_reader = new(contentStream); | ||
_enumerable = enumerable; | ||
_cancellationToken = cancellationToken; | ||
} | ||
|
||
public async ValueTask<bool> MoveNextAsync() | ||
{ | ||
ServerSentEvent? nextEvent = await _reader.TryGetNextEventAsync(_cancellationToken).ConfigureAwait(false); | ||
_enumerable.LastEventId = _reader.LastEventId; | ||
_enumerable.ReconnectionInterval = _reader.ReconnectionInterval; | ||
|
||
if (nextEvent.HasValue) | ||
{ | ||
Current = nextEvent.Value; | ||
return true; | ||
} | ||
|
||
Current = default; | ||
return false; | ||
} | ||
|
||
public ValueTask DisposeAsync() | ||
{ | ||
// The creator of the enumerable has responsibility for disposing | ||
// the content stream passed to the enumerable constructor. | ||
|
||
#if NET6_0_OR_GREATER | ||
return ValueTask.CompletedTask; | ||
#else | ||
return new ValueTask(); | ||
#endif | ||
} | ||
} | ||
} |
Oops, something went wrong.