Skip to content

Commit

Permalink
Support for asynchronous autopagination
Browse files Browse the repository at this point in the history
  • Loading branch information
ob-stripe committed Mar 7, 2020
1 parent 8c1141a commit 0bfe8b4
Show file tree
Hide file tree
Showing 6 changed files with 495 additions and 8 deletions.
7 changes: 7 additions & 0 deletions src/Stripe.net/Services/Coupons/CouponService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ public virtual IEnumerable<Coupon> ListAutoPaging(CouponListOptions options = nu
return this.ListEntitiesAutoPaging(options, requestOptions);
}

#if !NET45
public virtual IAsyncEnumerable<Coupon> ListAutoPagingAsync(CouponListOptions options = null, RequestOptions requestOptions = null, CancellationToken cancellationToken = default)
{
return this.ListEntitiesAutoPagingAsync(options, requestOptions, cancellationToken);
}
#endif

public virtual Coupon Update(string couponId, CouponUpdateOptions options, RequestOptions requestOptions = null)
{
return this.UpdateEntity(couponId, options, requestOptions);
Expand Down
104 changes: 103 additions & 1 deletion src/Stripe.net/Services/_base/Service.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ namespace Stripe
using System.Net;
using System.Net.Http;
using System.Reflection;
#if !NET45
using System.Runtime.CompilerServices;
#endif
using System.Threading;
using System.Threading.Tasks;
using Stripe.Infrastructure;

/// <summary>Abstract base class for all services.</summary>
/// <typeparam name="TEntityReturned">
Expand Down Expand Up @@ -163,6 +165,20 @@ protected TEntityReturned CreateEntity(BaseOptions options, RequestOptions reque
requestOptions);
}

#if !NET45
protected IAsyncEnumerable<TEntityReturned> ListEntitiesAutoPagingAsync(
ListOptions options,
RequestOptions requestOptions,
CancellationToken cancellationToken)
{
return this.ListRequestAutoPagingAsync<TEntityReturned>(
this.ClassUrl(),
options,
requestOptions,
cancellationToken);
}
#endif

protected TEntityReturned UpdateEntity(
string id,
BaseOptions options,
Expand Down Expand Up @@ -245,6 +261,91 @@ protected TEntityReturned CreateEntity(BaseOptions options, RequestOptions reque
cancellationToken).ConfigureAwait(false);
}

#if !NET45
protected IEnumerable<T> ListRequestAutoPaging<T>(
string url,
ListOptions options,
RequestOptions requestOptions)
where T : IStripeEntity
{
return this.ListRequestAutoPagingAsync<T>(url, options, requestOptions)
.ToEnumerable();
}

protected async IAsyncEnumerable<T> ListRequestAutoPagingAsync<T>(
string url,
ListOptions options,
RequestOptions requestOptions,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
where T : IStripeEntity
{
var page = await this.RequestAsync<StripeList<T>>(
HttpMethod.Get,
url,
options,
requestOptions,
cancellationToken);

options = options ?? new ListOptions();
bool iterateBackward = false;

// Backward iterating activates if we have an `EndingBefore`
// constraint and not a `StartingAfter` constraint
if (!string.IsNullOrEmpty(options.EndingBefore) && string.IsNullOrEmpty(options.StartingAfter))
{
iterateBackward = true;
}

while (true)
{
cancellationToken.ThrowIfCancellationRequested();

if (iterateBackward)
{
page.Reverse();
}

string itemId = null;
foreach (var item in page)
{
cancellationToken.ThrowIfCancellationRequested();

// Elements in `StripeList` instances are decoded by `StripeObjectConverter`,
// which returns `null` for objects it doesn't know how to decode.
// When auto-paginating, we simply ignore these null elements but still return
// other elements.
if (item == null)
{
continue;
}

itemId = ((IHasId)item).Id;
yield return item;
}

if (!page.HasMore || string.IsNullOrEmpty(itemId))
{
break;
}

if (iterateBackward)
{
options.EndingBefore = itemId;
}
else
{
options.StartingAfter = itemId;
}

page = await this.RequestAsync<StripeList<T>>(
HttpMethod.Get,
url,
options,
requestOptions,
cancellationToken);
}
}
#else
protected IEnumerable<T> ListRequestAutoPaging<T>(
string url,
ListOptions options,
Expand Down Expand Up @@ -311,6 +412,7 @@ protected TEntityReturned CreateEntity(BaseOptions options, RequestOptions reque
requestOptions);
}
}
#endif

protected RequestOptions SetupRequestOptions(RequestOptions requestOptions)
{
Expand Down
12 changes: 7 additions & 5 deletions src/Stripe.net/Stripe.net.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<PropertyGroup>
<Description>Stripe.net is a sync/async .NET 4.5+ client, and a portable class library for the Stripe API. (Official Library)</Description>
<AssemblyTitle>Stripe.net</AssemblyTitle>
<LangVersion>8</LangVersion>
<VersionPrefix>35.3.0</VersionPrefix>
<Version>35.3.0</Version>
<Authors>Stripe, Jayme Davis</Authors>
Expand Down Expand Up @@ -38,6 +39,12 @@
<CodeAnalysisRuleSet>..\_stylecop\StyleCopRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="1.1.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0" />
<PackageReference Include="System.Linq.Async" Version="4.0.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<PackageReference Include="System.Runtime.InteropServices.RuntimeInformation" Version="4.3.0" />
<Reference Include="Microsoft.CSharp" />
Expand All @@ -53,9 +60,4 @@
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0" />
</ItemGroup>

</Project>

0 comments on commit 0bfe8b4

Please sign in to comment.