Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic pagination support for fetching values #10

Merged
merged 2 commits into from
Aug 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion ConsoleTests/Program.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
using SharpBucket.V1;
using SharpBucket.V1.Pocos;
using SharpBucket.V2;
using SharpBucket.V2.EndPoints;
using SharpBucket.V2.Pocos;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Comment = SharpBucket.V1.Pocos.Comment;
using Repository = SharpBucket.V2.Pocos.Repository;
using Version = SharpBucket.V1.Pocos.Version;
using System.Diagnostics.Contracts;

namespace ConsoleTests{
internal class Program{
Expand Down Expand Up @@ -56,6 +63,7 @@ private static void TestApiV2(){
//TestUsersEndPoint(sharpBucket);
//TestTeamsEndPoint(sharpBucket);
TestRestRepositoriesEndPoint(sharpBucket);
TestV2Pagination(sharpBucket);
}

private static void ReadTestDataOauth(){
Expand Down Expand Up @@ -328,5 +336,48 @@ private static void TestRestRepositoriesEndPoint(SharpBucketV2 sharpBucket){
//var cId2 = 10;
//var PR2Comment = pullRequestEP.GetPullRequestComment(cId2);
}

private static void TestV2Pagination (SharpBucketV2 sharpBucket) {
RepositoriesEndPoint repositoriesEndPoint = sharpBucket.RepositoriesEndPoint();
Contract.Assert(repositoriesEndPoint != null);

List<Repository> repositories = repositoriesEndPoint.ListRepositories("mirror");
Contract.Assert(repositories != null && repositories.Count > 10);

var repositoryResource = repositoriesEndPoint.RepositoryResource("mirror", "mercurial");
Contract.Assert(repositoryResource != null);

var testRepository = repositoryResource.GetRepository();
Contract.Assert(testRepository != null);
Contract.Assert(testRepository.name == "mercurial");

var watchers = repositoryResource.ListWatchers();
Contract.Assert(watchers.Count > 10);

HashSet<string> uniqueNames = new HashSet<string>();

foreach (var watcher in watchers) {
Contract.Assert(watcher != null);
string id = watcher.username + watcher.display_name;
Contract.Assert(!uniqueNames.Contains(id));
uniqueNames.Add(id);
}

uniqueNames.Clear();

var forks = repositoryResource.ListForks();
Contract.Assert(forks.Count > 10);

foreach (var fork in forks) {
Contract.Assert(fork != null);
Contract.Assert(!uniqueNames.Contains(fork.full_name));
uniqueNames.Add(fork.full_name);
}

var commits = repositoryResource.ListCommits(max: 3);
Contract.Assert(commits.Count == 3);
commits = repositoryResource.ListCommits(max: 501);
Contract.Assert(commits.Count == 501);
}
}
}
}
4 changes: 4 additions & 0 deletions SharpBucket/SharpBucket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ namespace SharpBucket{
/// </summary>
public class SharpBucket{
private Authenticate authenticator;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should change this in all files or on none. Otherwise it will just get quite inconsistent. Lets not change it for now and maybe do a PR just for that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, will do

/// <summary>
/// The base URL exposing the BitBucket API.
/// </summary>
protected string _baseUrl;

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion SharpBucket/SharpBucket.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Compile Include="V1\Pocos\RepositoryPrivileges.cs" />
<Compile Include="V1\Pocos\RepositoryPrivilegesUser.cs" />
<Compile Include="V1\Pocos\RepositorySimple.cs" />
<Compile Include="V2\EndPoints\EndPoint.cs" />
<Compile Include="V2\EndPoints\PullRequestResource.cs" />
<Compile Include="V2\EndPoints\PullRequestsResource.cs" />
<Compile Include="V2\EndPoints\RepositoryResource.cs" />
Expand All @@ -66,6 +67,8 @@
<Compile Include="V2\Pocos\CommitInfo.cs" />
<Compile Include="V2\Pocos\Fork.cs" />
<Compile Include="V2\Pocos\ForkInfo.cs" />
<Compile Include="V2\Pocos\IteratorBasedPage.cs" />
<Compile Include="V2\Pocos\ListBasedPage.cs" />
<Compile Include="V2\Pocos\Parent.cs" />
<Compile Include="V2\Pocos\Activity.cs" />
<Compile Include="V2\Pocos\ActivityInfo.cs" />
Expand All @@ -84,7 +87,6 @@
<Compile Include="V2\Pocos\PullRequestsInfo.cs" />
<Compile Include="V2\Pocos\Repository.cs" />
<Compile Include="V2\Pocos\Owner.cs" />
<Compile Include="V2\Pocos\RepositoryInfo.cs" />
<Compile Include="V2\Pocos\Source.cs" />
<Compile Include="V2\Pocos\User.cs" />
<Compile Include="V2\EndPoints\RepositoriesEndPoint.cs" />
Expand Down
62 changes: 62 additions & 0 deletions SharpBucket/V2/EndPoints/EndPoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using SharpBucket.V2.Pocos;
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace SharpBucket.V2.EndPoints {
public class EndPoint {
protected readonly SharpBucketV2 _sharpBucketV2;
protected readonly string _baseUrl;

public EndPoint (SharpBucketV2 sharpBucketV2, string resourcePath) {
_sharpBucketV2 = sharpBucketV2;
_baseUrl = resourcePath;
}

/// <summary>
/// Iterates through a series of paginated values.
/// </summary>
/// <typeparam name="TValue"></typeparam>
/// <param name="overrideUrl"></param>
/// <param name="max">Set to 0 for unlimited size.</param>
/// <returns></returns>
protected List<TValue> GetPaginatedValues<TValue> (string overrideUrl, int max = 0) {
// default page length in many cases is 10, requiring lots of requests for larger collections
const int DEFAULT_PAGE_LEN = 100;
int pageLen = (max > 0 && max < DEFAULT_PAGE_LEN) ? max : DEFAULT_PAGE_LEN;

Debug.Assert(!overrideUrl.Contains("?"));
overrideUrl += "?pagelen=" + pageLen;

IteratorBasedPage<TValue> response = _sharpBucketV2.Get(new IteratorBasedPage<TValue>(), overrideUrl);
List<TValue> values = null;

if (response != null && response.values != null) {

values = response.values;

while (!String.IsNullOrEmpty(response.next) && (max <= 0 || values.Count < max)) {
overrideUrl = response.next.Replace(SharpBucketV2.BITBUCKET_URL, "");
Debug.Assert(overrideUrl.Length < response.next.Length);

response = _sharpBucketV2.Get(new IteratorBasedPage<TValue>(), overrideUrl);

// should this case throw?
if (response == null) { break; }

// if BitBucket is providing what should be a valid "next" URL,
// why is this ever null?
if (response.values != null) {
values.AddRange(response.values);
}
}
}

if (values != null && max > 0 && values.Count > max) {
values.RemoveRange(max - 1, values.Count - max);
}

return values;
}
}
}
5 changes: 3 additions & 2 deletions SharpBucket/V2/EndPoints/PullRequestsResource.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using SharpBucket.V2.Pocos;
using System.Collections.Generic;
using SharpBucket.V2.Pocos;

namespace SharpBucket.V2.EndPoints{
/// <summary>
Expand All @@ -23,7 +24,7 @@ public PullRequestsResource(string accountName, string repository, RepositoriesE
/// List all of a repository's open pull requests.
/// </summary>
/// <returns></returns>
public PullRequestsInfo ListPullRequests(){
public List<PullRequest> ListPullRequests(){
return _repositoriesEndPoint.ListPullRequests(_accountName, _repository);
}

Expand Down
64 changes: 31 additions & 33 deletions SharpBucket/V2/EndPoints/RepositoriesEndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,12 @@ namespace SharpBucket.V2.EndPoints{
/// More info:
/// https://confluence.atlassian.com/display/BITBUCKET/repositories+Endpoint
/// </summary>
public class RepositoriesEndPoint{
private readonly SharpBucketV2 _sharpBucketV2;
private readonly string _baseUrl;
public class RepositoriesEndPoint : EndPoint {

#region Repositories End Point

public RepositoriesEndPoint(SharpBucketV2 sharpBucketV2){
_sharpBucketV2 = sharpBucketV2;
_baseUrl = "repositories/";
public RepositoriesEndPoint (SharpBucketV2 sharpBucketV2)
: base(sharpBucketV2, "repositories/") {
}

/// <summary>
Expand All @@ -28,20 +25,22 @@ public RepositoriesEndPoint(SharpBucketV2 sharpBucketV2){
/// Otherwise, this method returns a collection of the public repositories.
/// </summary>
/// <param name="accountName">The account whose repositories you wish to get.</param>
/// <param name="max">The maximum number of items to return. 0 returns all items.</param>
/// <returns></returns>
public List<Repository> ListRepositories(string accountName){
public List<Repository> ListRepositories(string accountName, int max = 0){
var overrideUrl = _baseUrl + accountName + "/";
return _sharpBucketV2.Get(new RepositoryInfo(), overrideUrl).values;
return GetPaginatedValues<Repository>(overrideUrl, max);
}

/// <summary>
/// List of all the public repositories on Bitbucket. This produces a paginated response.
/// Pagination only goes forward (it's not possible to navigate to previous pages) and navigation is done by following the URL for the next page.
/// The returned repositories are ordered by creation date, oldest repositories first. Only public repositories are returned.
/// </summary>
/// <param name="max">The maximum number of items to return. 0 returns all items.</param>
/// <returns></returns>
public List<Repository> ListPublicRepositories(){
return _sharpBucketV2.Get(new RepositoryInfo(), _baseUrl).values;
public List<Repository> ListPublicRepositories(int max = 0){
return GetPaginatedValues<Repository>(_baseUrl, max);
}

#endregion
Expand Down Expand Up @@ -81,14 +80,14 @@ private string GetRepositoryUrl(string accountName, string repository, string ap
return string.Format(format, accountName, repository, append);
}

internal List<Watcher> ListWatchers(string accountName, string repository){
internal List<Watcher> ListWatchers(string accountName, string repository, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "watchers");
return _sharpBucketV2.Get(new WatcherInfo(), overrideUrl).values;
return GetPaginatedValues<Watcher>(overrideUrl, max);
}

internal List<Fork> ListForks(string accountName, string repository){
internal List<Fork> ListForks(string accountName, string repository, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "forks");
return _sharpBucketV2.Get(new ForkInfo(), overrideUrl).values;
return GetPaginatedValues<Fork>(overrideUrl, max);
}

#endregion
Expand All @@ -99,9 +98,9 @@ public PullRequestsResource PullReqestsResource(string accountName, string repos
return new PullRequestsResource(accountName, repository, this);
}

internal PullRequestsInfo ListPullRequests(string accountName, string repository){
internal List<PullRequest> ListPullRequests(string accountName, string repository, int max = 0) {
var overrideUrl = GetRepositoryUrl(accountName, repository, "pullrequests/");
return _sharpBucketV2.Get(new PullRequestsInfo(), overrideUrl);
return GetPaginatedValues<PullRequest>(overrideUrl, max);
}

internal PullRequest PostPullRequest(string accountName, string repository, PullRequest pullRequest){
Expand All @@ -114,9 +113,9 @@ internal PullRequest PutPullRequest(string accountName, string repository, PullR
return _sharpBucketV2.Put(pullRequest, overrideUrl);
}

internal ActivityInfo GetPullRequestLog(string accountName, string repository){
internal List<Activity> GetPullRequestLog(string accountName, string repository, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "pullrequests/activity/");
return _sharpBucketV2.Get(new ActivityInfo(), overrideUrl);
return GetPaginatedValues<Activity>(overrideUrl, max);
}

#endregion
Expand All @@ -128,12 +127,12 @@ internal PullRequest GetPullRequest(string accountName, string repository, int p
return _sharpBucketV2.Get(new PullRequest(), overrideUrl);
}

internal CommitInfo ListPullRequestCommits(string accountName, string repository, int pullRequestId){
internal List<Commit> ListPullRequestCommits(string accountName, string repository, int pullRequestId, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "pullrequests/" + pullRequestId + "/commits/");
return _sharpBucketV2.Get(new CommitInfo(), overrideUrl);
return GetPaginatedValues<Commit>(overrideUrl, max);
}

internal PullRequestInfo ApprovePullRequest(string accountName, string repository, int pullRequestId){
internal PullRequestInfo ApprovePullRequest(string accountName, string repository, int pullRequestId) {
var overrideUrl = GetRepositoryUrl(accountName, repository, "pullrequests/" + pullRequestId + "/approve/");
return _sharpBucketV2.Post(new PullRequestInfo(), overrideUrl);
}
Expand All @@ -148,9 +147,9 @@ internal object GetDiffForPullRequest(string accountName, string repository, int
return _sharpBucketV2.Get(new Object(), overrideUrl);
}

internal ActivityInfo GetPullRequestActivity(string accountName, string repository, int pullRequestId){
internal List<Activity> GetPullRequestActivity(string accountName, string repository, int pullRequestId, int max = 0) {
var overrideUrl = GetRepositoryUrl(accountName, repository, "pullrequests/" + pullRequestId + "/activity/");
return _sharpBucketV2.Get(new ActivityInfo(), overrideUrl);
return GetPaginatedValues<Activity>(overrideUrl, max);
}

internal Merge AcceptAndMergePullRequest(string accountName, string repository, int pullRequestId){
Expand All @@ -163,9 +162,9 @@ internal Merge DeclinePullRequest(string accountName, string repository, int pul
return _sharpBucketV2.Get(new Merge(), overrideUrl);
}

internal object ListPullRequestComments(string accountName, string repository, int pullRequestId){
internal List<Comment> ListPullRequestComments(string accountName, string repository, int pullRequestId, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "pullrequests/" + pullRequestId + "/comments/");
return _sharpBucketV2.Get(new ActivityInfo(), overrideUrl);
return GetPaginatedValues<Comment>(overrideUrl, max);
}

internal Comment GetPullRequestComment(string accountName, string repository, int pullRequestId, int commentId){
Expand All @@ -177,10 +176,9 @@ internal Comment GetPullRequestComment(string accountName, string repository, in

#region Branch Restrictions resource

internal List<BranchRestriction> ListBranchRestrictions(string accountName, string repository){
internal List<BranchRestriction> ListBranchRestrictions(string accountName, string repository, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "branch-restrictions/");
var branchRestrictions = _sharpBucketV2.Get(new BranchRestrictionInfo(), overrideUrl);
return branchRestrictions == null ? null : branchRestrictions.values;
return GetPaginatedValues<BranchRestriction>(overrideUrl, max);
}

internal BranchRestriction PostBranchRestriction(string accountName, string repository, BranchRestriction restriction){
Expand Down Expand Up @@ -221,22 +219,22 @@ internal object GetPatch(string accountName, string repository, object options){

#region Commits Resource

internal CommitInfo ListCommits(string accountName, string repository, string branchortag = null ){
internal List<Commit> ListCommits(string accountName, string repository, string branchortag = null, int max = 0){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should allow to set the max in all the paginated calls?
Should the default be to get all the results? Just the first page?
It would be nice to decide on this before we push the changes since it means api changes (it's an optional parameter, but still).

var overrideUrl = GetRepositoryUrl(accountName, repository, "commits/");
if ( !string.IsNullOrEmpty( branchortag )){
overrideUrl += branchortag;
}
return _sharpBucketV2.Get(new CommitInfo(), overrideUrl);
return GetPaginatedValues<Commit>(overrideUrl, max);
}

internal Commit GetCommit(string accountName, string repository, string revision){
var overrideUrl = GetRepositoryUrl(accountName, repository, "commit/" + revision);
return _sharpBucketV2.Get(new Commit(), overrideUrl);
}

internal List<object> ListCommitComments(string accountName, string repository, string revision){
internal List<Comment> ListCommitComments(string accountName, string repository, string revision, int max = 0){
var overrideUrl = GetRepositoryUrl(accountName, repository, "commits/" + revision + "/comments/");
return _sharpBucketV2.Get(new List<object>(), overrideUrl);
return GetPaginatedValues<Comment>(overrideUrl, max);
}

internal object GetCommitComment(string accountName, string repository, string revision, int commentId){
Expand All @@ -256,4 +254,4 @@ internal object DeleteCommitApproval(string accountName, string repository, stri

#endregion
}
}
}
10 changes: 5 additions & 5 deletions SharpBucket/V2/EndPoints/RepositoryResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ public object GetPatch(object options){
/// By default, this call returns all the commits across all branches, bookmarks, and tags. The newest commit is first.
/// </summary>
/// <param name="branchortag">The branch or tag to get, for example, master or default.</param>
/// <param name="max">Values greater than 0 will set a maximum number of records to return. 0 or less returns all.</param>
/// <returns></returns>
public object ListCommits(string branchortag = null)
{
return _repositoriesEndPoint.ListCommits(_accountName, _repository, branchortag);
public List<Commit> ListCommits(string branchortag = null, int max = 0) {
return _repositoriesEndPoint.ListCommits(_accountName, _repository, branchortag, max);
}

/// <summary>
Expand All @@ -176,7 +176,7 @@ public Commit GetCommit(string revision){
/// </summary>
/// <param name="revision">The commit's SHA1.</param>
/// <returns></returns>
public object ListCommitComments(string revision){
public List<Comment> ListCommitComments(string revision){
return _repositoriesEndPoint.ListCommitComments(_accountName, _repository, revision);
}

Expand Down Expand Up @@ -211,4 +211,4 @@ public object DeleteCommitApproval(string revision){

#endregion
}
}
}
Loading