-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] implement first-class paging support #740
Closed
Closed
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
8e75ad3
a first pass at a simple way to implement paging for requests
shiftkey 3f05a2b
mute some fx:cop:
shiftkey e8b3eea
off by one error
shiftkey 090f271
if the func returns nothing, give up
shiftkey 999752a
even better testing
shiftkey b04c9ad
don't page unless it's been changed
shiftkey f7ebc2f
the test now passes
shiftkey d2229b7
added support for lazy lists in convention tests
shiftkey 63ac22a
#types
shiftkey 8c0560e
for testability, switching this back to Task<T>
shiftkey 53e0adf
added a test helper for ApiInfo
shiftkey 52f3dd6
:lipstick: simpler tests yo
shiftkey 8b83255
switched over a couple of other endpoints
shiftkey 5826cb3
rename ILazyRequest to IDeferredRequest
shiftkey 1cce3b8
switch over an issue endpoint
shiftkey 42dd5f5
added failing test for providing custom Accept
shiftkey c3bce04
and some more tests
shiftkey 9491ec6
added custom deserialization of issue body
shiftkey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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,15 @@ | ||
using System.Collections.Generic; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Octokit | ||
{ | ||
public static class CustomAwaiter | ||
{ | ||
public static TaskAwaiter<IReadOnlyList<T>> GetAwaiter<T>(this IDeferredRequest<T> request) | ||
{ | ||
Ensure.ArgumentNotNull(request, "request"); | ||
|
||
return request.ToTask().GetAwaiter(); | ||
} | ||
} | ||
} |
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,112 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading.Tasks; | ||
using Octokit.Internal; | ||
using System.Globalization; | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace Octokit | ||
{ | ||
public class DeferredRequest<T> : IDeferredRequest<T> | ||
{ | ||
readonly IApiPagination _pagination = new ApiPagination(); | ||
readonly IApiConnection _apiConnection; | ||
|
||
readonly Uri _uri; | ||
readonly IDictionary<string, string> _parameters; | ||
|
||
ApiOptions _options = new ApiOptions(); | ||
|
||
public DeferredRequest(IApiConnection apiConnection, Uri uri) | ||
: this(apiConnection, uri, new Dictionary<string, string>()) { } | ||
|
||
public DeferredRequest(IApiConnection apiConnection, Uri uri, IDictionary<string, string> parameters) | ||
{ | ||
_apiConnection = apiConnection; | ||
_uri = uri; | ||
_parameters = parameters; | ||
} | ||
|
||
public IDeferredRequest<T> WithOptions(ApiOptions options) | ||
{ | ||
this._options = options; | ||
return this; | ||
} | ||
|
||
public Task<IReadOnlyList<T>> ToTask() | ||
{ | ||
if (_options.PageSize.HasValue) | ||
{ | ||
_parameters.Add("per_page", _options.PageSize.Value.ToString(CultureInfo.InvariantCulture)); | ||
} | ||
|
||
if (_options.StartPage.HasValue) | ||
{ | ||
_parameters.Add("page", _options.StartPage.Value.ToString(CultureInfo.InvariantCulture)); | ||
} | ||
|
||
return _pagination.GetAllPages( | ||
async () => await GetPage<T>(_uri, _parameters, null).ConfigureAwait(false), _uri); | ||
} | ||
|
||
|
||
async Task<IReadOnlyPagedCollection<TU>> GetPage<TU>( | ||
Uri uri, | ||
IDictionary<string, string> parameters, | ||
string accepts) | ||
{ | ||
Ensure.ArgumentNotNull(uri, "uri"); | ||
|
||
var connection = _apiConnection.Connection; | ||
|
||
var response = await connection.Get<List<TU>>(uri, parameters, accepts).ConfigureAwait(false); | ||
return new ReadOnlyPagedCollection<TU>( | ||
response, | ||
nextPageUri => | ||
{ | ||
if (nextPageUri.Query.Contains("page=") && _options.PageCount.HasValue) | ||
{ | ||
var allValues = ToQueryStringDictionary(nextPageUri); | ||
|
||
string pageValue; | ||
if (allValues.TryGetValue("page", out pageValue)) | ||
{ | ||
var startPage = _options.StartPage ?? 1; | ||
var pageCount = _options.PageCount.Value; | ||
|
||
var endPage = startPage + pageCount; | ||
if (pageValue.Equals(endPage.ToString(), StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
return connection.Get<List<TU>>(nextPageUri, parameters, accepts); | ||
}); | ||
} | ||
|
||
static Dictionary<string, string> ToQueryStringDictionary(Uri uri) | ||
{ | ||
var allValues = uri.Query.Split('&') | ||
.Select(keyValue => | ||
{ | ||
var indexOf = keyValue.IndexOf('='); | ||
if (indexOf > 0) | ||
{ | ||
var key = keyValue.Substring(0, indexOf); | ||
var value = keyValue.Substring(indexOf + 1); | ||
return new KeyValuePair<string, string>(key, value); | ||
} | ||
|
||
//just a plain old value, return it | ||
return new KeyValuePair<string, string>(keyValue, null); | ||
}) | ||
.ToDictionary(x => x.Key, x => x.Value); | ||
return allValues; | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I could 🔥 this awaiter and make the user call
.ToTask()
for these methods.It's less magical, but takes away the hot/cold issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still a breaking change. The API itself has changed from hot to cold and existing code now needs to .ToTask()
Right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not sure I can magic up a type here that's both Hot and also modifiable using WithOptions.
So I'm weighing up varying degrees of breaking changes to see how they feel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, any existing API calls will no longer magically work with
await
. So you're updating anyway.