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

Reactive Milestone Client #138

Merged
merged 8 commits into from
Nov 1, 2013
Merged
Show file tree
Hide file tree
Changes from 3 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
79 changes: 79 additions & 0 deletions Octokit.Reactive/Clients/IObservableMilestonesClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reactive;

namespace Octokit
{
public interface IObservableMilestonesClient
{
/// <summary>
/// Gets a single Milestone by number.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/issues/milestones/#get-a-single-milestone
/// </remarks>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get",
Justification = "Method makes a network request")]
IObservable<Milestone> Get(string owner, string name, int number);

/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
IObservable<Milestone> GetForRepository(string owner, string name);

/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="request">Used to filter and sort the list of Milestones returned</param>
/// <returns></returns>
IObservable<Milestone> GetForRepository(string owner, string name, MilestoneRequest request);

/// <summary>
/// Creates a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/issues/milestones/#create-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="newMilestone">A <see cref="NewMilestone"/> instance describing the new Milestone to create</param>
/// <returns></returns>
IObservable<Milestone> Create(string owner, string name, NewMilestone newMilestone);

/// <summary>
/// Creates a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/issues/milestones/#update-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The Milestone number</param>
/// <param name="milestoneUpdate">An <see cref="MilestoneUpdate"/> instance describing the changes to make to the Milestone
/// </param>
/// <returns></returns>
IObservable<Milestone> Update(string owner, string name, int number, MilestoneUpdate milestoneUpdate);

/// <summary>
/// Deletes a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/issues/milestones/#delete-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The milestone number</param>
/// <returns></returns>
IObservable<Unit> Delete(string owner, string name, int number);
}
}
125 changes: 125 additions & 0 deletions Octokit.Reactive/Clients/ObservableMilestonesClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Reactive;
using System.Reactive.Threading.Tasks;
using Octokit.Reactive.Internal;

namespace Octokit.Reactive.Clients
{
public class ObservableMilestonesClient : IObservableMilestonesClient
{
readonly IMilestonesClient _client;
readonly IConnection _connection;

public ObservableMilestonesClient(IGitHubClient client)
{
Ensure.ArgumentNotNull(client, "client");

_client = client.Issue.Milestone;
_connection = client.Connection;
}

/// <summary>
/// Gets a single Milestone by number.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/issues/milestones/#get-a-single-milestone
/// </remarks>
/// <returns></returns>
public IObservable<Milestone> Get(string owner, string name, int number)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");

return _client.Get(owner, name, number).ToObservable();
}

/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <returns></returns>
public IObservable<Milestone> GetForRepository(string owner, string name)
{
return _connection.GetAndFlattenAllPages<Milestone>(ApiUrls.Milestones(owner, name));
}

/// <summary>
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="request">Used to filter and sort the list of Milestones returned</param>
/// <returns></returns>
public IObservable<Milestone> GetForRepository(string owner, string name, MilestoneRequest request)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(request, "request");

return _connection.GetAndFlattenAllPages<Milestone>(ApiUrls.Milestones(owner, name),
request.ToParametersDictionary());
}

/// <summary>
/// Creates an Milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/issues/milestones/#create-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="newMilestone">A <see cref="NewMilestone"/> instance describing the new Milestone to create</param>
/// <returns></returns>
public IObservable<Milestone> Create(string owner, string name, NewMilestone newMilestone)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(newMilestone, "newMilestone");

return _client.Create(owner, name, newMilestone).ToObservable();
}

/// <summary>
/// Creates an Milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/issues/milestones/#create-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The Milestone number</param>
/// <param name="milestoneUpdate">An <see cref="MilestoneUpdate"/> instance describing the changes to make to the Milestone
/// </param>
/// <returns></returns>
public IObservable<Milestone> Update(string owner, string name, int number, MilestoneUpdate milestoneUpdate)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");
Ensure.ArgumentNotNull(milestoneUpdate, "milestoneUpdate");

return _client.Update(owner, name, number, milestoneUpdate).ToObservable();
}

/// <summary>
/// Deletes a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/issues/milestones/#delete-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The milestone number</param>
/// <returns></returns>
public IObservable<Unit> Delete(string owner, string name, int number)
{
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
Ensure.ArgumentNotNullOrEmptyString(name, "name");

return _client.Delete(owner, name, number).ToObservable();
}
}
}
4 changes: 2 additions & 2 deletions Octokit.Reactive/Helpers/ConnectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ namespace Octokit.Reactive.Internal
{
internal static class ConnectionExtensions
{
public static IObservable<T> GetAndFlattenAllPages<T>(this IConnection connection, Uri url)
public static IObservable<T> GetAndFlattenAllPages<T>(this IConnection connection, Uri url, IDictionary<string, string> parameters = null, string accepts = null)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure the parameters is needed. The nextPageUrl is returned as part of the previous page's response in the Link header. I would assume it would be a fully formed URL for retrieving the next page of results. See the pagination docs for the GitHub API for more details. Were you finding that this was not the case?

The accepts parameter, on the other hand, makes total sense!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me take a closer look at this - I'm pretty sure we need parameters for the initial call but you're probably right that it's redundant for successive calls. Assuming that's the case, how does this look?

         public static IObservable<T> GetAndFlattenAllPages<T>(this IConnection connection, Uri url, IDictionary<string, string> parameters = null, string accepts = null)
         {
-            return GetPages(url, nextPageUrl => connection.GetAsync<List<T>>(nextPageUrl, parameters, accepts).ToObservable());
+            return GetPages(url, parameters, (pageUrl, pageParams) => connection.GetAsync<List<T>>(pageUrl, pageParams, accepts).ToObservable());
         }

-        static IObservable<T> GetPages<T>(Uri uri,
-            Func<Uri, IObservable<IResponse<List<T>>>> getPageFunc)
+        static IObservable<T> GetPages<T>(Uri uri, IDictionary<string, string> parameters,
+            Func<Uri, IDictionary<string, string>, IObservable<IResponse<List<T>>>> getPageFunc)
         {
-            return getPageFunc(uri).Expand(resp =>
+            return getPageFunc(uri, parameters).Expand(resp =>
             {
                 var nextPageUrl = resp.ApiInfo.GetNextPageUrl();
                 return nextPageUrl == null
                     ? Observable.Empty<IResponse<List<T>>>()
-                    : Observable.Defer(() => getPageFunc(nextPageUrl));
+                    : Observable.Defer(() => getPageFunc(nextPageUrl, null));
             })
             .Where(resp => resp != null)
             .SelectMany(resp => resp.BodyAsObject);

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, don't we already apply the parameters to the URL before we call this? If we do that, we never need to worry about it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is our only chance to apply parameters - without passing them in here, they're not respected. I don't suppose you or your colleagues know of a public repo with more than a page worth of Milestones for testing purposes? At least for the first page, this does correctly apply parameters.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is our only chance to apply parameters - without passing them in here, they're not respected.

Ah right. Why'd you remove the accepts parameter? That seems like it should be necessary if we need to pass something other than the default. Or do we not need to do that here?

know of a public repo with more than a page worth of Milestones for testing purposes?

I don't offhand. I suppose we could create one. We could have the integration test create one. By default, it's 30 a page.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah right. Why'd you remove the accepts parameter? That seems like it should be necessary if we need to pass something other than the default. Or do we not need to do that here?

MilestonesClient doesn't pass accepts, so I'm not either. I believe it's still correctly wired up in GetAndFlattenAllPages though.

{
return GetPages(url, nextPageUrl => connection.GetAsync<List<T>>(nextPageUrl).ToObservable());
return GetPages(url, nextPageUrl => connection.GetAsync<List<T>>(nextPageUrl, parameters, accepts).ToObservable());
}

static IObservable<T> GetPages<T>(Uri uri,
Expand Down
2 changes: 2 additions & 0 deletions Octokit.Reactive/Octokit.Reactive.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
<Compile Include="..\SolutionInfo.cs">
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
<Compile Include="Clients\IObservableMilestonesClient.cs" />
<Compile Include="Clients\ObservableMilestonesClient.cs" />
<Compile Include="Clients\ObservableAssigneesClient.cs" />
<Compile Include="Clients\IObservableCommitStatusClient.cs" />
<Compile Include="Clients\ObservableCommitStatusClient.cs" />
Expand Down
15 changes: 7 additions & 8 deletions Octokit/Clients/IMilestonesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ namespace Octokit
public interface IMilestonesClient
{
/// <summary>
/// Gets all Milestones across all the authenticated user’s visible repositories including owned repositories,
/// member repositories, and organization repositories.
/// Gets a single Milestone by number.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#get-a-single-Milestone
/// http://developer.github.com/v3/issues/milestones/#get-a-single-milestone
/// </remarks>
/// <returns></returns>
[SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get",
Expand All @@ -22,7 +21,7 @@ public interface IMilestonesClient
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
Expand All @@ -33,7 +32,7 @@ public interface IMilestonesClient
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
Expand All @@ -45,7 +44,7 @@ public interface IMilestonesClient
/// Creates a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#create-an-Milestone</remarks>
/// <remarks>http://developer.github.com/v3/issues/milestones/#create-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="newMilestone">A <see cref="NewMilestone"/> instance describing the new Milestone to create</param>
Expand All @@ -56,7 +55,7 @@ public interface IMilestonesClient
/// Creates a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#update-a-milestone</remarks>
/// <remarks>http://developer.github.com/v3/issues/milestones/#update-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The Milestone number</param>
Expand All @@ -69,7 +68,7 @@ public interface IMilestonesClient
/// Deletes a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#delete-a-milestone</remarks>
/// <remarks>http://developer.github.com/v3/issues/milestones/#delete-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The milestone number</param>
Expand Down
15 changes: 7 additions & 8 deletions Octokit/Clients/MilestonesClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ public MilestonesClient(IApiConnection apiConnection) : base(apiConnection)
}

/// <summary>
/// Gets all Milestones across all the authenticated user’s visible repositories including owned repositories,
/// member repositories, and organization repositories.
/// Gets a single Milestone by number.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#get-a-single-Milestone
/// http://developer.github.com/v3/issues/milestones/#get-a-single-Milestone
/// </remarks>
/// <returns></returns>
public Task<Milestone> Get(string owner, string name, int number)
Expand All @@ -30,7 +29,7 @@ public Task<Milestone> Get(string owner, string name, int number)
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
Expand All @@ -44,7 +43,7 @@ public Task<IReadOnlyList<Milestone>> GetForRepository(string owner, string name
/// Gets all open milestones for the repository.
/// </summary>
/// <remarks>
/// http://developer.github.com/v3/Milestones/#list-Milestones-for-a-repository
/// http://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
/// </remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
Expand All @@ -64,7 +63,7 @@ public Task<IReadOnlyList<Milestone>> GetForRepository(string owner, string name
/// Creates an Milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#create-an-Milestone</remarks>
/// <remarks>http://developer.github.com/v3/issues/milestones/#create-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="newMilestone">A <see cref="NewMilestone"/> instance describing the new Milestone to create</param>
Expand All @@ -82,7 +81,7 @@ public Task<Milestone> Create(string owner, string name, NewMilestone newMilesto
/// Creates an Milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#create-an-Milestone</remarks>
/// <remarks>http://developer.github.com/v3/issues/milestones/#create-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The Milestone number</param>
Expand All @@ -102,7 +101,7 @@ public Task<Milestone> Update(string owner, string name, int number, MilestoneUp
/// Deletes a milestone for the specified repository. Any user with pull access to a repository can create an
/// Milestone.
/// </summary>
/// <remarks>http://developer.github.com/v3/Milestones/#delete-a-milestone</remarks>
/// <remarks>http://developer.github.com/v3/issues/milestones/#delete-a-milestone</remarks>
/// <param name="owner">The owner of the repository</param>
/// <param name="name">The name of the repository</param>
/// <param name="number">The milestone number</param>
Expand Down