diff --git a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs index ef0f5636..5d61bd7b 100644 --- a/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/IObservableRepositoriesClient.cs @@ -592,5 +592,13 @@ namespace Octokit.Reactive /// See the Repository Invitations API documentation for more information. /// IObservableRepositoryInvitationsClient Invitation { get; } + + /// + /// Access GitHub's Repository Traffic API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ + /// + IObservableRepositoryTrafficClient Traffic { get; } } } diff --git a/Octokit.Reactive/Clients/IObservableRepositoryTrafficClient.cs b/Octokit.Reactive/Clients/IObservableRepositoryTrafficClient.cs new file mode 100644 index 00000000..637d6145 --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableRepositoryTrafficClient.cs @@ -0,0 +1,77 @@ +using System; + +namespace Octokit.Reactive +{ + /// + /// A client for GitHub's Repository Traffic API. + /// + /// + /// See the Repository Traffic API documentation for more information. + /// + public interface IObservableRepositoryTrafficClient + { + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + /// The name of the repository + IObservable GetReferrers(string owner, string name); + + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + IObservable GetReferrers(long repositoryId); + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + /// The name of the repository + IObservable GetPaths(string owner, string name); + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + IObservable GetPaths(long repositoryId); + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + IObservable GetViews(string owner, string name, RepositoryTrafficRequest per); + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// Breakdown per day or week + IObservable GetViews(long repositoryId, RepositoryTrafficRequest per); + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + IObservable GetClones(string owner, string name, RepositoryTrafficRequest per); + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// Breakdown per day or week + IObservable GetClones(long repositoryId, RepositoryTrafficRequest per); + } +} diff --git a/Octokit.Reactive/Clients/ObservableReleasesClient.cs b/Octokit.Reactive/Clients/ObservableReleasesClient.cs index 5f233fda..2a98e85f 100644 --- a/Octokit.Reactive/Clients/ObservableReleasesClient.cs +++ b/Octokit.Reactive/Clients/ObservableReleasesClient.cs @@ -38,7 +38,7 @@ namespace Octokit.Reactive Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return GetAll(owner, name, ApiOptions.None); + return GetAll(owner, name, ApiOptions.None); } /// diff --git a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs index 5f1d2fd0..7d7370df 100644 --- a/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs +++ b/Octokit.Reactive/Clients/ObservableRepositoriesClient.cs @@ -36,6 +36,7 @@ namespace Octokit.Reactive Merging = new ObservableMergingClient(client); Page = new ObservableRepositoryPagesClient(client); Invitation = new ObservableRepositoryInvitationsClient(client); + Traffic = new ObservableRepositoryTrafficClient(client); } /// @@ -880,6 +881,14 @@ namespace Octokit.Reactive /// /// See the Repository Invitations API documentation for more information. /// - public IObservableRepositoryInvitationsClient Invitation { get; private set; } + public IObservableRepositoryInvitationsClient Invitation { get; private set; } + + /// + /// Access GitHub's Repository Traffic API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ + /// + public IObservableRepositoryTrafficClient Traffic { get; private set; } } } diff --git a/Octokit.Reactive/Clients/ObservableRepositoryTrafficClient.cs b/Octokit.Reactive/Clients/ObservableRepositoryTrafficClient.cs new file mode 100644 index 00000000..5978e49c --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableRepositoryTrafficClient.cs @@ -0,0 +1,124 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; + +namespace Octokit.Reactive +{ + public class ObservableRepositoryTrafficClient : IObservableRepositoryTrafficClient + { + readonly IRepositoryTrafficClient _client; + + public ObservableRepositoryTrafficClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _client = client.Repository.Traffic; + } + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + public IObservable GetPaths(long repositoryId) + { + return _client.GetPaths(repositoryId).ToObservable().SelectMany(x => x); + } + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + /// The name of the repository + public IObservable GetPaths(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _client.GetPaths(owner, name).ToObservable().SelectMany(x => x); + } + + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + public IObservable GetReferrers(long repositoryId) + { + return _client.GetReferrers(repositoryId).ToObservable().SelectMany(x => x); + } + + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + /// The name of the repository + public IObservable GetReferrers(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _client.GetReferrers(owner, name).ToObservable().SelectMany(x => x); + } + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// Breakdown per day or week + public IObservable GetClones(long repositoryId, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNull(per, "per"); + + return _client.GetClones(repositoryId, per).ToObservable(); + } + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + public IObservable GetClones(string owner, string name, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(per, "per"); + + return _client.GetClones(owner, name, per).ToObservable(); + } + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// Breakdown per day or week + public IObservable GetViews(long repositoryId, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNull(per, "per"); + + return _client.GetViews(repositoryId, per).ToObservable(); + } + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + public IObservable GetViews(string owner, string name, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(per, "per"); + + return _client.GetViews(owner, name, per).ToObservable(); + } + } +} diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj index 25287f12..1f39b439 100644 --- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj @@ -196,6 +196,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj index 38148d1d..2d50f637 100644 --- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj +++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj @@ -212,6 +212,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj index 4d123580..c38c9094 100644 --- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj @@ -208,6 +208,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index 59ed1851..70733536 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -99,6 +99,7 @@ + @@ -141,6 +142,7 @@ + diff --git a/Octokit.Tests.Integration/Clients/RepositoryTrafficClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryTrafficClientTests.cs new file mode 100644 index 00000000..92720c65 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/RepositoryTrafficClientTests.cs @@ -0,0 +1,111 @@ +using Octokit; +using Octokit.Tests.Integration; +using System.Threading.Tasks; +using Xunit; + +public class RepositoryTrafficClientTests +{ + readonly IRepositoryTrafficClient _fixture; + readonly IGitHubClient _github; + readonly string _owner; + readonly string _repo; + readonly long _repoId; + + public RepositoryTrafficClientTests() + { + _github = Helper.GetAuthenticatedClient(); + _fixture = _github.Repository.Traffic; + + _owner = "octokit"; + _repo = "octokit.net"; + _repoId = _github.Repository.Get(_owner, _repo).Result.Id; + } + + public class TheGetReferrersMethod : RepositoryTrafficClientTests + { + [IntegrationTest] + public async Task GetsReferrers() + { + var referrers = await _fixture.GetReferrers(_owner, _repo); + + Assert.True(referrers.Count > 0); + } + + [IntegrationTest] + public async Task GetsReferrersWithRepositoryId() + { + var referrers = await _fixture.GetReferrers(_repoId); + + Assert.True(referrers.Count > 0); + } + } + + public class TheGetPathsMethod : RepositoryTrafficClientTests + { + [IntegrationTest] + public async Task GetsPaths() + { + var paths = await _fixture.GetPaths(_owner, _repo); + + Assert.True(paths.Count > 0); + } + + [IntegrationTest] + public async Task GetsPathsWithRepositoryId() + { + var paths = await _fixture.GetPaths(_repoId); + + Assert.True(paths.Count > 0); + } + } + + public class TheGetClonesMethod : RepositoryTrafficClientTests + { + [IntegrationTest] + public async Task GetsClones() + { + var request = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + var clones = await _fixture.GetClones(_owner, _repo, request); + + Assert.True(clones.Count > 0); + Assert.True(clones.Clones.Count > 0); + Assert.True(clones.Uniques > 0); + } + + [IntegrationTest] + public async Task GetsClonesWithRepositoryId() + { + var request = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + var clones = await _fixture.GetClones(_repoId, request); + + Assert.True(clones.Count > 0); + Assert.True(clones.Clones.Count > 0); + Assert.True(clones.Uniques > 0); + } + } + + public class TheGetViewsMethod : RepositoryTrafficClientTests + { + [IntegrationTest] + public async Task GetsViews() + { + var request = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + var views = await _fixture.GetViews(_owner, _repo, request); + + Assert.True(views.Count > 0); + Assert.True(views.Views.Count > 0); + Assert.True(views.Uniques > 0); + } + + [IntegrationTest] + public async Task GetsViewsWithRepositoryId() + { + var request = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + var views = await _fixture.GetViews(_repoId, request); + + Assert.True(views.Count > 0); + Assert.True(views.Views.Count > 0); + Assert.True(views.Uniques > 0); + } + } +} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index f0176627..6aef864f 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -112,6 +112,7 @@ + diff --git a/Octokit.Tests/Clients/RepositoryTrafficClientTests.cs b/Octokit.Tests/Clients/RepositoryTrafficClientTests.cs new file mode 100644 index 00000000..4fc2ed97 --- /dev/null +++ b/Octokit.Tests/Clients/RepositoryTrafficClientTests.cs @@ -0,0 +1,181 @@ +using NSubstitute; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class RepositoryTrafficClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws( + () => new RepositoryTrafficClient(null)); + } + } + + public class TheGetAllReferrersMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + + await client.GetReferrers("fake", "repo"); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/traffic/popular/referrers"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + + await client.GetReferrers(1); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repositories/1/traffic/popular/referrers"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositoryTrafficClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetReferrers(null, "name")); + await Assert.ThrowsAsync(() => client.GetReferrers("owner", null)); + + await Assert.ThrowsAsync(() => client.GetReferrers("", "name")); + await Assert.ThrowsAsync(() => client.GetReferrers("owner", "")); + } + } + + public class TheGetAllPathsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + + await client.GetPaths("fake", "repo"); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repos/fake/repo/traffic/popular/paths"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + + await client.GetPaths(1); + + connection.Received().GetAll(Arg.Is(u => u.ToString() == "repositories/1/traffic/popular/paths"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositoryTrafficClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetPaths(null, "name")); + await Assert.ThrowsAsync(() => client.GetPaths("owner", null)); + + await Assert.ThrowsAsync(() => client.GetPaths("", "name")); + await Assert.ThrowsAsync(() => client.GetPaths("owner", "")); + } + } + + public class TheGetClonesMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + await client.GetClones("fake", "repo", per); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repos/fake/repo/traffic/clones"), Arg.Is>(s => s["per"] == "day"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + await client.GetClones(1, per); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repositories/1/traffic/clones"), Arg.Is>(s => s["per"] == "day"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositoryTrafficClient(Substitute.For()); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + await Assert.ThrowsAsync(() => client.GetClones(null, "name", per)); + await Assert.ThrowsAsync(() => client.GetClones("owner", null, per)); + await Assert.ThrowsAsync(() => client.GetClones("owner", "name", null)); + + await Assert.ThrowsAsync(() => client.GetClones("", "name", per)); + await Assert.ThrowsAsync(() => client.GetClones("owner", "", per)); + + await Assert.ThrowsAsync(() => client.GetClones(1, null)); + } + } + + public class TheGetViewsMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + await client.GetViews("fake", "repo", per); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repos/fake/repo/traffic/views"), Arg.Is>(s => s["per"] == "day"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task RequestsCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new RepositoryTrafficClient(connection); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + await client.GetViews(1, per); + + connection.Received().Get(Arg.Is(u => u.ToString() == "repositories/1/traffic/views"), Arg.Is>(s => s["per"] == "day"), "application/vnd.github.spiderman-preview"); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new RepositoryTrafficClient(Substitute.For()); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + await Assert.ThrowsAsync(() => client.GetViews(null, "name", per)); + await Assert.ThrowsAsync(() => client.GetViews("owner", null, per)); + await Assert.ThrowsAsync(() => client.GetViews("owner", "name", null)); + + await Assert.ThrowsAsync(() => client.GetViews("", "name", per)); + await Assert.ThrowsAsync(() => client.GetViews("owner", "", per)); + + await Assert.ThrowsAsync(() => client.GetViews(1, null)); + } + } + } +} diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index e5715425..866b7ce4 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -109,6 +109,7 @@ + @@ -254,6 +255,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservableRepositoryTrafficClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositoryTrafficClientTests.cs new file mode 100644 index 00000000..7cb4dc25 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableRepositoryTrafficClientTests.cs @@ -0,0 +1,179 @@ +using NSubstitute; +using Octokit.Reactive; +using System; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableRepositoryTrafficClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArguments() + { + Assert.Throws(() => new ObservableRepositoryTrafficClient(null)); + } + } + + public class TheGetAllReferrersMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + + client.GetReferrers("fake", "repo"); + + gitHubClient.Received().Repository.Traffic.GetReferrers("fake", "repo"); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + + client.GetReferrers(1); + + gitHubClient.Received().Repository.Traffic.GetReferrers(1); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableRepositoryTrafficClient(Substitute.For()); + + Assert.Throws(() => client.GetReferrers(null, "name")); + Assert.Throws(() => client.GetReferrers("owner", null)); + + Assert.Throws(() => client.GetReferrers("", "name")); + Assert.Throws(() => client.GetReferrers("owner", "")); + } + } + + public class TheGetAllPathsMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + + client.GetPaths("fake", "repo"); + + gitHubClient.Received().Repository.Traffic.GetPaths("fake", "repo"); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + + client.GetPaths(1); + + gitHubClient.Received().Repository.Traffic.GetPaths(1); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableRepositoryTrafficClient(Substitute.For()); + + Assert.Throws(() => client.GetPaths(null, "name")); + Assert.Throws(() => client.GetPaths("owner", null)); + + Assert.Throws(() => client.GetPaths("", "name")); + Assert.Throws(() => client.GetPaths("owner", "")); + } + } + + public class TheGetClonesMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + client.GetClones("fake", "repo", per); + + gitHubClient.Received().Repository.Traffic.GetClones("fake", "repo", per); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + client.GetClones(1, per); + + gitHubClient.Received().Repository.Traffic.GetClones(1, per); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableRepositoryTrafficClient(Substitute.For()); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + Assert.Throws(() => client.GetClones(null, "name", per)); + Assert.Throws(() => client.GetClones("owner", null, per)); + Assert.Throws(() => client.GetClones("owner", "name", null)); + + Assert.Throws(() => client.GetClones("", "name", per)); + Assert.Throws(() => client.GetClones("owner", "", per)); + + Assert.Throws(() => client.GetClones(1, null)); + } + } + + public class TheGetViewsMethod + { + [Fact] + public void RequestsCorrectUrl() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + client.GetViews("fake", "repo", per); + + gitHubClient.Received().Repository.Traffic.GetViews("fake", "repo", per); + } + + [Fact] + public void RequestsCorrectUrlWithRepositoryId() + { + var gitHubClient = Substitute.For(); + var client = new ObservableRepositoryTrafficClient(gitHubClient); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + client.GetViews(1, per); + + gitHubClient.Received().Repository.Traffic.GetViews(1, per); + } + + [Fact] + public void EnsuresNonNullArguments() + { + var client = new ObservableRepositoryTrafficClient(Substitute.For()); + var per = new RepositoryTrafficRequest(TrafficDayOrWeek.Day); + + Assert.Throws(() => client.GetViews(null, "name", per)); + Assert.Throws(() => client.GetViews("owner", null, per)); + Assert.Throws(() => client.GetViews("owner", "name", null)); + + Assert.Throws(() => client.GetViews("", "name", per)); + Assert.Throws(() => client.GetViews("owner", "", per)); + + Assert.Throws(() => client.GetViews(1, null)); + } + } + } +} diff --git a/Octokit/Clients/IRepositoriesClient.cs b/Octokit/Clients/IRepositoriesClient.cs index e1375da8..8d9ab4a8 100644 --- a/Octokit/Clients/IRepositoriesClient.cs +++ b/Octokit/Clients/IRepositoriesClient.cs @@ -655,5 +655,13 @@ namespace Octokit /// See the Repository Invitations API documentation for more information. /// IRepositoryInvitationsClient Invitation { get; } + + /// + /// Access GitHub's Repository Traffic API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ + /// + IRepositoryTrafficClient Traffic { get; } } } diff --git a/Octokit/Clients/IRepositoryTrafficClient.cs b/Octokit/Clients/IRepositoryTrafficClient.cs new file mode 100644 index 00000000..d4be3eec --- /dev/null +++ b/Octokit/Clients/IRepositoryTrafficClient.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + /// + /// A client for GitHub's Repository Traffic API. + /// + /// + /// See the Repository Traffic API documentation for more information. + /// + public interface IRepositoryTrafficClient + { + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + /// The name of the repository + [ExcludeFromPaginationConventionTest] + Task> GetReferrers(string owner, string name); + + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + [ExcludeFromPaginationConventionTest] + Task> GetReferrers(long repositoryId); + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + /// The name of the repository + [ExcludeFromPaginationConventionTest] + Task> GetPaths(string owner, string name); + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + [ExcludeFromPaginationConventionTest] + Task> GetPaths(long repositoryId); + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + Task GetViews(string owner, string name, RepositoryTrafficRequest per); + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// Breakdown per day or week + Task GetViews(long repositoryId, RepositoryTrafficRequest per); + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + Task GetClones(string owner, string name, RepositoryTrafficRequest per); + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// Breakdown per day or week + Task GetClones(long repositoryId, RepositoryTrafficRequest per); + } +} diff --git a/Octokit/Clients/RepositoriesClient.cs b/Octokit/Clients/RepositoriesClient.cs index 67201d66..816bf7a4 100644 --- a/Octokit/Clients/RepositoriesClient.cs +++ b/Octokit/Clients/RepositoriesClient.cs @@ -39,6 +39,7 @@ namespace Octokit Page = new RepositoryPagesClient(apiConnection); Invitation = new RepositoryInvitationsClient(apiConnection); Branch = new RepositoryBranchesClient(apiConnection); + Traffic = new RepositoryTrafficClient(apiConnection); } /// @@ -957,5 +958,13 @@ namespace Octokit /// See the Repository Invitations API documentation for more information. /// public IRepositoryInvitationsClient Invitation { get; private set; } + + /// + /// Access GitHub's Repository Traffic API + /// + /// + /// Refer to the API documentation for more information: https://developer.github.com/v3/repos/traffic/ + /// + public IRepositoryTrafficClient Traffic { get; private set; } } } diff --git a/Octokit/Clients/RepositoryTrafficClient.cs b/Octokit/Clients/RepositoryTrafficClient.cs new file mode 100644 index 00000000..302b316c --- /dev/null +++ b/Octokit/Clients/RepositoryTrafficClient.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + public class RepositoryTrafficClient : ApiClient, IRepositoryTrafficClient + { + public RepositoryTrafficClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + public Task> GetPaths(long repositoryId) + { + return ApiConnection.GetAll(ApiUrls.RepositoryTrafficPaths(repositoryId), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// List the top 10 popular contents over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-paths + /// The owner of the repository + /// The name of the repository + public Task> GetPaths(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.GetAll(ApiUrls.RepositoryTrafficPaths(owner, name), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + public Task> GetReferrers(long repositoryId) + { + return ApiConnection.GetAll(ApiUrls.RepositoryTrafficReferrers(repositoryId), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// List the top 10 referrers over the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#list-referrers + /// The owner of the repository + /// The name of the repository + public Task> GetReferrers(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.GetAll(ApiUrls.RepositoryTrafficReferrers(owner, name), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// Breakdown per day or week + public Task GetClones(long repositoryId, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNull(per, "per"); + + return ApiConnection.Get(ApiUrls.RepositoryTrafficClones(repositoryId), per.ToParametersDictionary(), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// Get the total number of clones and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#clones + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + public Task GetClones(string owner, string name, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(per, "per"); + + return ApiConnection.Get(ApiUrls.RepositoryTrafficClones(owner, name), per.ToParametersDictionary(), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// Breakdown per day or week + public Task GetViews(long repositoryId, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNull(per, "per"); + + return ApiConnection.Get(ApiUrls.RepositoryTrafficViews(repositoryId), per.ToParametersDictionary(), AcceptHeaders.RepositoryTrafficApiPreview); + } + + /// + /// Get the total number of views and breakdown per day or week for the last 14 days + /// + /// https://developer.github.com/v3/repos/traffic/#views + /// The owner of the repository + /// The name of the repository + /// Breakdown per day or week + public Task GetViews(string owner, string name, RepositoryTrafficRequest per) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNull(per, "per"); + + return ApiConnection.Get(ApiUrls.RepositoryTrafficViews(owner, name), per.ToParametersDictionary(), AcceptHeaders.RepositoryTrafficApiPreview); + } + } +} diff --git a/Octokit/Helpers/AcceptHeaders.cs b/Octokit/Helpers/AcceptHeaders.cs index f85ebc32..20e01e51 100644 --- a/Octokit/Helpers/AcceptHeaders.cs +++ b/Octokit/Helpers/AcceptHeaders.cs @@ -40,5 +40,7 @@ namespace Octokit public const string PagesApiPreview = "application/vnd.github.mister-fantastic-preview+json"; public const string IssueTimelineApiPreview = "application/vnd.github.mockingbird-preview"; + + public const string RepositoryTrafficApiPreview = "application/vnd.github.spiderman-preview"; } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index bd88186a..6ae6b0b7 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -3203,5 +3203,89 @@ namespace Octokit { return "user/repository_invitations/{0}".FormatUri(invitationId); } + + /// + /// Returns the for repository traffice referrers. + /// + /// The owner of repo + /// The name of repo + /// The for repository traffic referrers. + public static Uri RepositoryTrafficReferrers(string owner, string repo) + { + return "repos/{0}/{1}/traffic/popular/referrers".FormatUri(owner, repo); + } + + /// + /// Returns the for repository traffice referrers. + /// + /// The id of the repository + /// The for repository traffic referrers. + public static Uri RepositoryTrafficReferrers(long repositoryId) + { + return "repositories/{0}/traffic/popular/referrers".FormatUri(repositoryId); + } + + /// + /// Returns the for repository traffice paths. + /// + /// The owner of repo + /// The name of repo + /// The for repository traffic paths. + public static Uri RepositoryTrafficPaths(string owner, string repo) + { + return "repos/{0}/{1}/traffic/popular/paths".FormatUri(owner, repo); + } + + /// + /// Returns the for repository traffice paths. + /// + /// The id of the repository + /// The for repository traffic paths. + public static Uri RepositoryTrafficPaths(long repositoryId) + { + return "repositories/{0}/traffic/popular/paths".FormatUri(repositoryId); + } + + /// + /// Returns the for repository traffice views. + /// + /// The owner of repo + /// The name of repo + /// The for repository traffic views. + public static Uri RepositoryTrafficViews(string owner, string repo) + { + return "repos/{0}/{1}/traffic/views".FormatUri(owner, repo); + } + + /// + /// Returns the for repository traffice views. + /// + /// The id of the repository + /// The for repository traffic views. + public static Uri RepositoryTrafficViews(long repositoryId) + { + return "repositories/{0}/traffic/views".FormatUri(repositoryId); + } + + /// + /// Returns the for repository traffice clones. + /// + /// The owner of repo + /// The name of repo + /// The for repository traffic clones. + public static Uri RepositoryTrafficClones(string owner, string repo) + { + return "repos/{0}/{1}/traffic/clones".FormatUri(owner, repo); + } + + /// + /// Returns the for repository traffice clones. + /// + /// The id of the repository + /// The for repository traffic clones. + public static Uri RepositoryTrafficClones(long repositoryId) + { + return "repositories/{0}/traffic/clones".FormatUri(repositoryId); + } } } diff --git a/Octokit/Models/Request/RepositoryTrafficRequest.cs b/Octokit/Models/Request/RepositoryTrafficRequest.cs new file mode 100644 index 00000000..9679a119 --- /dev/null +++ b/Octokit/Models/Request/RepositoryTrafficRequest.cs @@ -0,0 +1,23 @@ +using System.Diagnostics; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficRequest : RequestParameters + { + public RepositoryTrafficRequest() { } + + public RepositoryTrafficRequest(TrafficDayOrWeek per) + { + Per = per; + } + + public TrafficDayOrWeek Per { get; private set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Per: {0}", Per); } + } + } +} diff --git a/Octokit/Models/Response/RepositoryTrafficClone.cs b/Octokit/Models/Response/RepositoryTrafficClone.cs new file mode 100644 index 00000000..9baf715e --- /dev/null +++ b/Octokit/Models/Response/RepositoryTrafficClone.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Octokit.Helpers; +using Octokit.Internal; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficCloneSummary + { + public RepositoryTrafficCloneSummary() { } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public RepositoryTrafficCloneSummary(int count, int uniques, IReadOnlyList clones) + { + Count = count; + Uniques = uniques; + Clones = clones; + } + + public int Count { get; protected set; } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public int Uniques { get; protected set; } + + public IReadOnlyList Clones { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Number: {0} Uniques: {1}", Count, Uniques); } + } + } + + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficClone + { + public RepositoryTrafficClone() { } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public RepositoryTrafficClone(long timestamp, int count, int uniques) + { + TimestampAsUtcEpochSeconds = timestamp; + Count = count; + Uniques = uniques; + } + + [Parameter(Key = "ignoreThisField")] + public DateTimeOffset Timestamp + { + get { return TimestampAsUtcEpochSeconds.FromUnixTime(); } + } + + [Parameter(Key = "timestamp")] + public long TimestampAsUtcEpochSeconds { get; protected set; } + + public int Count { get; protected set; } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public int Uniques { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Number: {0} Uniques: {1}", Count, Uniques); } + } + } + + public enum TrafficDayOrWeek + { + Day, + Week + } +} diff --git a/Octokit/Models/Response/RepositoryTrafficPath.cs b/Octokit/Models/Response/RepositoryTrafficPath.cs new file mode 100644 index 00000000..fcac8ccc --- /dev/null +++ b/Octokit/Models/Response/RepositoryTrafficPath.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficPath + { + public RepositoryTrafficPath() { } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public RepositoryTrafficPath(string path, string title, int count, int uniques) + { + Path = path; + Title = title; + Count = count; + Uniques = uniques; + } + + public string Path { get; protected set; } + + public string Title { get; protected set; } + + public int Count { get; protected set; } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public int Uniques { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Path: {0}, Title: {1}", Path, Title); } + } + } +} diff --git a/Octokit/Models/Response/RepositoryTrafficReferrer.cs b/Octokit/Models/Response/RepositoryTrafficReferrer.cs new file mode 100644 index 00000000..cc4d8932 --- /dev/null +++ b/Octokit/Models/Response/RepositoryTrafficReferrer.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficReferrer + { + public RepositoryTrafficReferrer() { } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public RepositoryTrafficReferrer(string referrer, int count, int uniques) + { + Referrer = referrer; + Count = count; + Uniques = uniques; + } + + public string Referrer { get; protected set; } + + public int Count { get; protected set; } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public int Uniques { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Referrer: {0}, Count: {1}", Referrer, Count); } + } + } +} diff --git a/Octokit/Models/Response/RepositoryTrafficView.cs b/Octokit/Models/Response/RepositoryTrafficView.cs new file mode 100644 index 00000000..c9c8ae5a --- /dev/null +++ b/Octokit/Models/Response/RepositoryTrafficView.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Octokit.Helpers; +using Octokit.Internal; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficViewSummary + { + public RepositoryTrafficViewSummary() { } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public RepositoryTrafficViewSummary(int count, int uniques, IReadOnlyList views) + { + Count = count; + Uniques = uniques; + Views = views; + } + + public int Count { get; protected set; } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public int Uniques { get; protected set; } + + public IReadOnlyList Views { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Number: {0} Uniques: {1}", Count, Uniques); } + } + } + + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class RepositoryTrafficView + { + public RepositoryTrafficView() { } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public RepositoryTrafficView(long timestamp, int count, int uniques) + { + TimestampAsUtcEpochSeconds = timestamp; + Count = count; + Uniques = uniques; + } + + [Parameter(Key = "ignoreThisField")] + public DateTimeOffset Timestamp + { + get { return TimestampAsUtcEpochSeconds.FromUnixTime(); } + } + + [Parameter(Key = "timestamp")] + public long TimestampAsUtcEpochSeconds { get; protected set; } + + public int Count { get; protected set; } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "It's a property from the api.")] + public int Uniques { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Timestamp: {0} Number: {1} Uniques: {2}", Timestamp, Count, Uniques); } + } + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 4f78f1f8..1890b1d9 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -494,6 +494,13 @@ + + + + + + + diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index a75a9594..843d485f 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -505,6 +505,13 @@ + + + + + + + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 652c55be..03f59ce5 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -501,6 +501,13 @@ + + + + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 35140d86..2bef9a43 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -491,6 +491,13 @@ + + + + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 456dc553..5c1b1ed4 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -498,6 +498,13 @@ + + + + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index bf40c6e0..9deb1cd6 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -65,6 +65,7 @@ + @@ -110,6 +111,7 @@ + @@ -160,6 +162,7 @@ + @@ -219,6 +222,10 @@ + + + +