diff --git a/Octokit.Reactive.nuspec b/Octokit.Reactive.nuspec index 22e4db7e..ba8fdc70 100644 --- a/Octokit.Reactive.nuspec +++ b/Octokit.Reactive.nuspec @@ -8,7 +8,7 @@ @summary@ https://github.com/octokit/octokit.net/blob/master/LICENSE.txt https://github.com/octokit/octokit.net - https://f.cloud.github.com/assets/19977/1441274/160fba8c-41a9-11e3-831d-61d88fa886f4.png + https://f.cloud.github.com/assets/19977/1510987/64af2b26-4a9d-11e3-89fc-96a185171c75.png false @description@ @releaseNotes@ diff --git a/Octokit.Tests.Integration/ReleasesClientTests.cs b/Octokit.Tests.Integration/ReleasesClientTests.cs index 788b91e0..ebe9c8a8 100644 --- a/Octokit.Tests.Integration/ReleasesClientTests.cs +++ b/Octokit.Tests.Integration/ReleasesClientTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Net.Http.Headers; using System.Threading.Tasks; using Xunit; @@ -7,17 +8,32 @@ namespace Octokit.Tests.Integration { public class ReleasesClientTests { - public class TheGetReleasesMethod + public class TheGetReleasesMethod : IDisposable { - [IntegrationTest] - public async Task ReturnsReleases() + readonly IReleasesClient _releaseClient; + readonly Repository _repository; + readonly string _repositoryOwner; + readonly string _repositoryName; + readonly GitHubClient _github; + + public TheGetReleasesMethod() { - var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + _github = new GitHubClient(new ProductHeaderValue("OctokitTests")) { Credentials = Helper.Credentials }; + _releaseClient = _github.Release; - var releases = await github.Release.GetAll("git-tfs", "git-tfs"); + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + _repository = _github.Repository.Create(new NewRepository { Name = repoName, AutoInit = true }).Result; + _repositoryOwner = _repository.Owner.Login; + _repositoryName = _repository.Name; + } + + [IntegrationTest] + public async Task ReturnsReleases() + { + var releases = await _releaseClient.GetAll("git-tfs", "git-tfs"); Assert.True(releases.Count > 5); Assert.True(releases.Any(release => release.TagName == "v0.18.0")); @@ -26,16 +42,20 @@ namespace Octokit.Tests.Integration [IntegrationTest] public async Task ReturnsReleasesWithNullPublishDate() { - var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) - { - Credentials = Helper.Credentials - }; + // create a release without a publish date + var releaseWithNoUpdate = new ReleaseUpdate("0.1") { Draft = true }; + var release = _releaseClient.CreateRelease(_repositoryOwner, _repositoryName, releaseWithNoUpdate).Result; - var releases = await github.Release.GetAll("Particular", "ServiceInsight"); + var releases = await _releaseClient.GetAll(_repositoryOwner, _repositoryName); Assert.True(releases.Count == 1); Assert.False(releases.First().PublishedAt.HasValue); } + + public void Dispose() + { + Helper.DeleteRepo(_repository); + } } } } diff --git a/Octokit.Tests/Clients/StarredClientTests.cs b/Octokit.Tests/Clients/StarredClientTests.cs new file mode 100644 index 00000000..7203f2fc --- /dev/null +++ b/Octokit.Tests/Clients/StarredClientTests.cs @@ -0,0 +1,129 @@ +using Octokit.Internal; +using System; +using System.Net; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; +using Xunit.Extensions; + +namespace Octokit.Tests.Clients +{ + public class StarredClientTests + { + public class TheGetAllForCurrentMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("user/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + client.GetAllForCurrent(); + + connection.Received().GetAll(endpoint); + } + } + + public class TheGetAllForUserMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("users/banana/starred", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + client.GetAllForUser("banana"); + + connection.Received().GetAll(endpoint); + } + } + + public class TheGetAllStargazersForRepoMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var endpoint = new Uri("repos/fight/club/stargazers", UriKind.Relative); + var connection = Substitute.For(); + var client = new StarredClient(connection); + + client.GetAllStargazers("fight", "club"); + + connection.Received().GetAll(endpoint); + } + } + + public class TheCheckStarredMethod + { + [Theory] + [InlineData(HttpStatusCode.NoContent, true)] + [InlineData(HttpStatusCode.NotFound, false)] + public async Task ReturnsCorrectResultBasedOnStatus(HttpStatusCode status, bool expected) + { + var response = Task.Factory.StartNew>(() => + new ApiResponse { StatusCode = status }); + var connection = Substitute.For(); + connection.GetAsync(Arg.Is(u => u.ToString() == "user/starred/yes/no"), null, null) + .Returns(response); + var apiConnection = Substitute.For(); + apiConnection.Connection.Returns(connection); + + var client = new StarredClient(apiConnection); + var result = await client.CheckStarred("yes", "no"); + + Assert.Equal(expected, result); + } + } + + public class TheStarRepoMethod + { + [Theory] + [InlineData(HttpStatusCode.NoContent, true)] + [InlineData(HttpStatusCode.NotFound, false)] + [InlineData(HttpStatusCode.OK, false)] + public async Task ReturnsCorrectResultBasedOnStatus(HttpStatusCode status, bool expected) + { + var response = Task.Factory.StartNew>(() => + new ApiResponse { StatusCode = status }); + + var connection = Substitute.For(); + connection.PutAsync(Arg.Is(u => u.ToString() == "user/starred/yes/no"), + Args.Object, Args.String).Returns(response); + + var apiConnection = Substitute.For(); + apiConnection.Connection.Returns(connection); + + var client = new StarredClient(apiConnection); + var result = await client.StarRepo("yes", "no"); + + Assert.Equal(expected, result); + } + } + + public class TheRemoveStarFromRepoMethod + { + [Theory] + [InlineData(HttpStatusCode.NoContent, true)] + [InlineData(HttpStatusCode.NotFound, false)] + [InlineData(HttpStatusCode.OK, false)] + public async Task ReturnsCorrectResultBasedOnStatus(HttpStatusCode status, bool expected) + { + var response = Task.Factory.StartNew(() => status); + + var connection = Substitute.For(); + connection.DeleteAsync(Arg.Is(u => u.ToString() == "user/starred/yes/no")) + .Returns(response); + + var apiConnection = Substitute.For(); + apiConnection.Connection.Returns(connection); + + var client = new StarredClient(apiConnection); + var result = await client.RemoveStarFromRepo("yes", "no"); + + Assert.Equal(expected, result); + } + } + } +} diff --git a/Octokit.Tests/Http/ApiConnectionTests.cs b/Octokit.Tests/Http/ApiConnectionTests.cs index 9b5df215..d76967f8 100644 --- a/Octokit.Tests/Http/ApiConnectionTests.cs +++ b/Octokit.Tests/Http/ApiConnectionTests.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; @@ -248,9 +249,9 @@ namespace Octokit.Tests.Http public async Task MakesDeleteRequest() { var deleteUri = new Uri("anything", UriKind.Relative); - IResponse response = new ApiResponse {BodyAsObject = new object()}; + HttpStatusCode statusCode = HttpStatusCode.NoContent; var connection = Substitute.For(); - connection.DeleteAsync(Args.Uri).Returns(Task.FromResult(response)); + connection.DeleteAsync(Args.Uri).Returns(Task.FromResult(statusCode)); var apiConnection = new ApiConnection(connection); await apiConnection.Delete(deleteUri); diff --git a/Octokit.Tests/OctoKit.Tests-NetCore45.csproj b/Octokit.Tests/OctoKit.Tests-NetCore45.csproj index 7c005989..c1bc8627 100644 --- a/Octokit.Tests/OctoKit.Tests-NetCore45.csproj +++ b/Octokit.Tests/OctoKit.Tests-NetCore45.csproj @@ -67,6 +67,7 @@ + diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index 84daa12e..cff9ef07 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -74,6 +74,7 @@ + diff --git a/Octokit/Clients/ActivitiesClient.cs b/Octokit/Clients/ActivitiesClient.cs index ade4ac5d..bf07a291 100644 --- a/Octokit/Clients/ActivitiesClient.cs +++ b/Octokit/Clients/ActivitiesClient.cs @@ -6,8 +6,10 @@ : base(apiConnection) { Events = new EventsClient(apiConnection); + Starring = new StarredClient(apiConnection); } public IEventsClient Events { get; private set; } + public IStarredClient Starring { get; private set; } } } diff --git a/Octokit/Clients/IActivitiesClient.cs b/Octokit/Clients/IActivitiesClient.cs index 81c4f294..ad1e4fcc 100644 --- a/Octokit/Clients/IActivitiesClient.cs +++ b/Octokit/Clients/IActivitiesClient.cs @@ -3,5 +3,6 @@ public interface IActivitiesClient { IEventsClient Events { get; } + IStarredClient Starring { get; } } } \ No newline at end of file diff --git a/Octokit/Clients/IStarredClient.cs b/Octokit/Clients/IStarredClient.cs new file mode 100644 index 00000000..ffb8695c --- /dev/null +++ b/Octokit/Clients/IStarredClient.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + public interface IStarredClient + { + /// + /// Retrieves all of the stargazers for the passed repository. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown if the client is not authenticated. + /// A of . + Task> GetAllStargazers(string owner, string name); + + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Thrown if the client is not authenticated. + /// A of . + Task> GetAllForCurrent(); + + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Star-specific request parameters that sort the results + /// Thrown if the client is not authenticated. + /// A of . + Task> GetAllForCurrent(StarredRequest request); + + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Thrown if the client is not authenticated. + /// A starred by the specified user. + Task> GetAllForUser(string user); + + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Thrown if the client is not authenticated. + /// A starred by the specified user. + Task> GetAllForUser(string user, StarredRequest request); + + /// + /// Check if a repository is starred by the current authenticated user. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown if the client is not authenticated. + /// A bool representing the success of the operation + Task CheckStarred(string owner, string name); + + /// + /// Stars a repository for the authenticated user. + /// + /// The owner of the repository to star + /// The name of the repository to star + /// A bool representing the success of starring + Task StarRepo(string owner, string name); + + /// + /// Unstars a repository for the authenticated user. + /// + /// The owner of the repository to unstar + /// The name of the repository to unstar + /// A bool representing the success of the operation + Task RemoveStarFromRepo(string owner, string name); + } +} diff --git a/Octokit/Clients/StarredClient.cs b/Octokit/Clients/StarredClient.cs new file mode 100644 index 00000000..06e46c53 --- /dev/null +++ b/Octokit/Clients/StarredClient.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace Octokit +{ + public class StarredClient : ApiClient, IStarredClient + { + public StarredClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + /// + /// Retrieves all of the stargazers for the passed repository. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown if the client is not authenticated. + /// A of . + public Task> GetAllStargazers(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.GetAll(ApiUrls.Stargazers(owner, name)); + } + + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Thrown if the client is not authenticated. + /// A of . + public Task> GetAllForCurrent() + { + return ApiConnection.GetAll(ApiUrls.Starred()); + } + + /// + /// Retrieves all of the starred (ies) for the current user. + /// + /// Star-specific request parameters that sort the results + /// Thrown if the client is not authenticated. + /// A of . + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", + Justification = "But i think i do need star-specific request parameters")] + public Task> GetAllForCurrent(StarredRequest request) + { + Ensure.ArgumentNotNull(request, "request"); + + return ApiConnection.GetAll(ApiUrls.Starred(), request.ToParametersDictionary()); + } + + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Thrown if the client is not authenticated. + /// A starred by the specified user. + public Task> GetAllForUser(string user) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + + return ApiConnection.GetAll(ApiUrls.StarredByUser(user)); + } + + /// + /// Retrieves all of the (ies) starred by the specified user. + /// + /// The login of the user + /// Star-specific request parameters that sort the results + /// Thrown if the client is not authenticated. + /// A starred by the specified user. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] + public Task> GetAllForUser(string user, StarredRequest request) + { + Ensure.ArgumentNotNullOrEmptyString(user, "user"); + Ensure.ArgumentNotNull(request, "request"); + + return ApiConnection.GetAll(ApiUrls.StarredByUser(user), request.ToParametersDictionary()); + } + + /// + /// Check if a repository is starred by the current authenticated user. + /// + /// The owner of the repository + /// The name of the repository + /// Thrown if the client is not authenticated. + /// A bool representing the success of the operation + public async Task CheckStarred(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + try + { + var response = await Connection.GetAsync(ApiUrls.Starred(owner, name), null, null) + .ConfigureAwait(false); + + return response.StatusCode == HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } + + /// + /// Stars a repository for the authenticated user. + /// + /// The owner of the repository to star + /// The name of the repository to star + /// A bool representing the success of starring the repository. + public async Task StarRepo(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + try + { + var response = await Connection.PutAsync(ApiUrls.Starred(owner, name), null, null) + .ConfigureAwait(false); + + return response.StatusCode == HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } + + /// + /// Unstars a repository for the authenticated user. + /// + /// The owner of the repository to unstar + /// The name of the repository to unstar + /// A bool representing the success of unstarring the repository. + public async Task RemoveStarFromRepo(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + try + { + var statusCode = await Connection.DeleteAsync(ApiUrls.Starred(owner, name)) + .ConfigureAwait(false); + + return statusCode == HttpStatusCode.NoContent; + } + catch (NotFoundException) + { + return false; + } + } + } +} diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index d151a2c1..9a0e4a60 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -10,6 +10,7 @@ namespace Octokit static readonly Uri _currentUserRepositoriesUrl = new Uri("user/repos", UriKind.Relative); static readonly Uri _currentUserOrganizationsUrl = new Uri("user/orgs", UriKind.Relative); static readonly Uri _currentUserSshKeys = new Uri("user/keys", UriKind.Relative); + static readonly Uri _currentUserStars = new Uri("user/starred", UriKind.Relative); static readonly Uri _currentUserEmailsEndpoint = new Uri("user/emails", UriKind.Relative); static readonly Uri _currentUserAuthorizationsEndpoint = new Uri("authorizations", UriKind.Relative); static readonly Uri _currentUserNotificationsEndpoint = new Uri("notifications", UriKind.Relative); @@ -378,6 +379,41 @@ namespace Octokit } /// + /// Returns the that lists the starred repositories for the authenticated user. + /// + public static Uri Stargazers(string owner, string repo) + { + return "repos/{0}/{1}/stargazers".FormatUri(owner, repo); + } + + /// + /// Returns the that lists the starred repositories for the authenticated user. + /// + public static Uri Starred() + { + return _currentUserStars; + } + + /// + /// Returns the that lists the starred repositories for the specified user. + /// + /// The user that has the stars + public static Uri StarredByUser(string user) + { + return "users/{0}/starred".FormatUri(user); + } + + /// + /// Returns the that shows whether the repo is starred by the current user. + /// + /// The owner of the repository + /// The name of the repository + /// + public static Uri Starred(string owner, string repo) + { + return "user/starred/{0}/{1}".FormatUri(owner, repo); + } + /// Returns the for the specified tag. /// /// The owner of the repository diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs index 61d92378..e41b77e4 100644 --- a/Octokit/Http/Connection.cs +++ b/Octokit/Http/Connection.cs @@ -205,16 +205,17 @@ namespace Octokit return Run(request); } - public Task DeleteAsync(Uri uri) + public async Task DeleteAsync(Uri uri) { Ensure.ArgumentNotNull(uri, "uri"); - return Run(new Request + var response = await Run(new Request { Method = HttpMethod.Delete, BaseAddress = BaseAddress, Endpoint = uri }); + return response.StatusCode; } public Uri BaseAddress { get; private set; } diff --git a/Octokit/Http/IConnection.cs b/Octokit/Http/IConnection.cs index 548da217..6d513ba8 100644 --- a/Octokit/Http/IConnection.cs +++ b/Octokit/Http/IConnection.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; namespace Octokit @@ -13,7 +14,7 @@ namespace Octokit Task> PutAsync(Uri uri, object body); Task> PutAsync(Uri uri, object body, string twoFactorAuthenticationCode); - Task DeleteAsync(Uri uri); + Task DeleteAsync(Uri uri); Uri BaseAddress { get; } diff --git a/Octokit/Models/Request/StarredRequest.cs b/Octokit/Models/Request/StarredRequest.cs new file mode 100644 index 00000000..4d71a41a --- /dev/null +++ b/Octokit/Models/Request/StarredRequest.cs @@ -0,0 +1,25 @@ +using Octokit.Internal; + +namespace Octokit +{ + public class StarredRequest : RequestParameters + { + public StarredRequest() + { + SortProperty = StarredSort.Created; + SortDirection = SortDirection.Ascending; + } + + [Parameter(Key = "sort")] + public StarredSort SortProperty { get; set; } + + [Parameter(Key = "direction")] + public SortDirection SortDirection { get; set; } + } + + public enum StarredSort + { + Created, + Updated + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index f4caf68d..9f1e8bdf 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -57,10 +57,12 @@ + + @@ -77,6 +79,7 @@ + diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index a2a495c2..495049cd 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -1,4 +1,4 @@ - + Debug @@ -211,6 +211,10 @@ + + + + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index d3242bc3..eb9acc49 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -1,4 +1,4 @@ - + Debug @@ -206,6 +206,10 @@ + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index f47f6c7b..00f0cda1 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -77,6 +77,7 @@ + @@ -89,6 +90,7 @@ + @@ -160,6 +162,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 695a5cdf..991dbef0 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -71,10 +71,12 @@ + + @@ -89,6 +91,7 @@ + @@ -233,4 +236,4 @@ --> - \ No newline at end of file + diff --git a/build.cmd b/build.cmd index 55b1e728..431be99c 100644 --- a/build.cmd +++ b/build.cmd @@ -1,10 +1,20 @@ @echo off +SET MinimalFAKEVersion=639 +SET FAKEVersion=1 +cls + +if exist tools\FAKE.Core\tools\PatchVersion.txt ( + FOR /F "tokens=*" %%i in (tools\FAKE.Core\tools\PatchVersion.txt) DO (SET FAKEVersion=%%i) +) + +if %MinimalFAKEVersion% lss %FAKEVersion% goto Build +if %MinimalFAKEVersion%==%FAKEVersion% goto Build + +"tools\nuget\nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-Prerelease" + :Build cls -if not exist tools\FAKE.Core\tools\Fake.exe ( - "tools\nuget\nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-Prerelease" -) SET TARGET="Default" diff --git a/build.fsx b/build.fsx index 2a5e3aae..34c8e91b 100644 --- a/build.fsx +++ b/build.fsx @@ -47,7 +47,7 @@ Target "CheckProjects" (fun _ -> Target "FixProjects" (fun _ -> !! "./Octokit/Octokit*.csproj" - |> Fake.MSBuild.ProjectSystem.FixMissingFiles "./Octokit/Octokit.csproj" + |> Fake.MSBuild.ProjectSystem.FixProjectFiles "./Octokit/Octokit.csproj" ) Target "BuildApp" (fun _ ->