From cf1b99dd3db24c7b4560cfc0d815832828958fec Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 12 Jul 2015 20:59:31 -0700 Subject: [PATCH 01/24] define a custom Repos property when searching issues --- .../Clients/SearchClientTests.cs | 9 ++- Octokit.Tests/Clients/SearchClientTests.cs | 29 ++++++++-- Octokit/Models/Request/SearchIssuesRequest.cs | 55 +++++++++++++++++-- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index cb7a1305b3..a33d370ab2 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; using Octokit; using Octokit.Tests.Integration; @@ -45,6 +47,11 @@ public async Task SearchForFunctionInCode() public async Task SearchForWordInCode() { var request = new SearchIssuesRequest("windows"); + request.Repos = new Collection { + "aspnet/dnx", + "aspnet/dnvm" + }; + request.SortField = IssueSearchSort.Created; request.Order = SortDirection.Descending; diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index c4eb248ce5..0019be8bd1 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using NSubstitute; using Xunit; using System.Threading.Tasks; @@ -1150,13 +1151,13 @@ public void TestingTheRepoQualifier() var connection = Substitute.For(); var client = new SearchClient(connection); var request = new SearchIssuesRequest("something"); - request.Repo = "octokit.net"; + request.Repo = "octokit/octokit.net"; client.SearchIssues(request); connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), - Arg.Is>(d => d["q"] == "something+repo:octokit.net")); + Arg.Is>(d => d["q"] == "something+repo:octokit/octokit.net")); } [Fact] @@ -1165,7 +1166,7 @@ public void TestingTheRepoAndUserAndLabelQualifier() var connection = Substitute.For(); var client = new SearchClient(connection); var request = new SearchIssuesRequest("something"); - request.Repo = "octokit.net"; + request.Repo = "octokit/octokit.net"; request.User = "alfhenrik"; request.Labels = new[] { "bug" }; @@ -1174,7 +1175,7 @@ public void TestingTheRepoAndUserAndLabelQualifier() connection.Received().Get( Arg.Is(u => u.ToString() == "search/issues"), Arg.Is>(d => d["q"] == - "something+label:bug+user:alfhenrik+repo:octokit.net")); + "something+label:bug+user:alfhenrik+repo:octokit/octokit.net")); } } @@ -1487,6 +1488,26 @@ public void TestingTheRepoAndPathAndExtensionQualifiers() Arg.Is>(d => d["q"] == "something+path:tools/FAKE.core+extension:fs+repo:octokit.net")); } + + [Fact] + public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + + var request = new SearchIssuesRequest("windows"); + request.Repos = new Collection { + "haha-business" + }; + + request.SortField = IssueSearchSort.Created; + request.Order = SortDirection.Descending; + + await Assert.ThrowsAsync( + async () => await client.SearchIssues(request)); + } + + } } } diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index 49d0d5e01b..e665952677 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; namespace Octokit { @@ -15,7 +16,10 @@ namespace Octokit [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SearchIssuesRequest : BaseSearchRequest { - public SearchIssuesRequest(string term) : base(term) { } + public SearchIssuesRequest(string term) : base(term) + { + Repos = new Collection(); + } public SearchIssuesRequest(string term, string owner, string name) : this(term) @@ -23,7 +27,9 @@ public SearchIssuesRequest(string term, string owner, string name) Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - this.Repo = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); + var repo = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); + + Repos.Add(repo); } /// @@ -183,7 +189,21 @@ public IEnumerable Labels /// /// https://help.github.com/articles/searching-issues#users-organizations-and-repositories /// - public string Repo { get; set; } + public string Repo + { + get + { + return Repos.FirstOrDefault(); + } + set + { + Repos.Clear(); + Repos.Add(value); + } + } + + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public Collection Repos { get; set; } public override IReadOnlyList MergedQualifiers() { @@ -264,14 +284,39 @@ public override IReadOnlyList MergedQualifiers() parameters.Add(String.Format(CultureInfo.InvariantCulture, "user:{0}", User)); } - if (Repo.IsNotBlank()) + if (Repos.Any()) { - parameters.Add(String.Format(CultureInfo.InvariantCulture, "repo:{0}", Repo)); + var invalidFormatRepos = Repos.Where(x => !IsNameWithOwnerFormat(x)); + if (invalidFormatRepos.Any()) + { + var parameterList = string.Join(", ", invalidFormatRepos); + var message = string.Format( + CultureInfo.InvariantCulture, + "The list of repositories must be formatted as 'owner/name' - these values don't match this rule: {0}", + parameterList); + throw new ArgumentException(message); + } + + parameters.Add( + string.Join("+", Repos.Select(x => "repo:" + x))); } return new ReadOnlyCollection(parameters); } + // what rules do we define here? + + static Regex nameWithOwner = new Regex("[a-zA-Z.]{1,}/[a-zA-Z.]{1,}" +#if (!PORTABLE && !NETFX_CORE) + , RegexOptions.Compiled +#endif + ); + + static bool IsNameWithOwnerFormat(string input) + { + return nameWithOwner.IsMatch(input); + } + internal string DebuggerDisplay { get From 12e9155ce8d93b36818a674548f1e9ad86d3e02a Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 14 Jul 2015 20:49:16 +0930 Subject: [PATCH 02/24] deprecate Repo property and update test usages --- .../Clients/SearchClientTests.cs | 6 ++---- Octokit.Tests/Clients/SearchClientTests.cs | 6 ++---- Octokit/Models/Request/SearchIssuesRequest.cs | 19 ------------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index a33d370ab2..a6a058d93e 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -63,8 +63,7 @@ public async Task SearchForWordInCode() [Fact] public async Task SearchForOpenIssues() { - var request = new SearchIssuesRequest("phone"); - request.Repo = "caliburn-micro/caliburn.micro"; + var request = new SearchIssuesRequest("phone", "caliburn-micro", "caliburn.micro"); request.State = ItemState.Open; var issues = await _gitHubClient.Search.SearchIssues(request); @@ -75,8 +74,7 @@ public async Task SearchForOpenIssues() [Fact] public async Task SearchForAllIssues() { - var request = new SearchIssuesRequest("phone"); - request.Repo = "caliburn-micro/caliburn.micro"; + var request = new SearchIssuesRequest("phone", "caliburn-micro", "caliburn.micro"); var issues = await _gitHubClient.Search.SearchIssues(request); diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index 0019be8bd1..246b26deb1 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1150,8 +1150,7 @@ public void TestingTheRepoQualifier() { var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchIssuesRequest("something"); - request.Repo = "octokit/octokit.net"; + var request = new SearchIssuesRequest("something", "octokit", "octokit.net"); client.SearchIssues(request); @@ -1165,8 +1164,7 @@ public void TestingTheRepoAndUserAndLabelQualifier() { var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchIssuesRequest("something"); - request.Repo = "octokit/octokit.net"; + var request = new SearchIssuesRequest("something", "octokit", "octokit.net"); request.User = "alfhenrik"; request.Labels = new[] { "bug" }; diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index e665952677..e07f47896d 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -183,25 +183,6 @@ public IEnumerable Labels /// public string User { get; set; } - /// - /// Limits searches to a specific repository. - /// - /// - /// https://help.github.com/articles/searching-issues#users-organizations-and-repositories - /// - public string Repo - { - get - { - return Repos.FirstOrDefault(); - } - set - { - Repos.Clear(); - Repos.Add(value); - } - } - [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public Collection Repos { get; set; } From b191ebc3cdbce1a5f35dcbb9fa99b353ff774396 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 17 Jul 2015 07:31:49 +0930 Subject: [PATCH 03/24] extract specific exception for when invalid paths raised --- .../Exceptions/RepositoryFormatException.cs | 65 +++++++++++++++++++ Octokit/Octokit-Mono.csproj | 1 + Octokit/Octokit-MonoAndroid.csproj | 1 + Octokit/Octokit-Monotouch.csproj | 1 + Octokit/Octokit-Portable.csproj | 1 + Octokit/Octokit-netcore45.csproj | 1 + Octokit/Octokit.csproj | 1 + 7 files changed, 71 insertions(+) create mode 100644 Octokit/Exceptions/RepositoryFormatException.cs diff --git a/Octokit/Exceptions/RepositoryFormatException.cs b/Octokit/Exceptions/RepositoryFormatException.cs new file mode 100644 index 0000000000..941a166916 --- /dev/null +++ b/Octokit/Exceptions/RepositoryFormatException.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Octokit +{ +#if !NETFX_CORE + [Serializable] +#endif + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", + Justification = "These exceptions are specific to the GitHub API and not general purpose exceptions")] + public class RepositoryFormatException : Exception + { + readonly string message; + + public RepositoryFormatException(IEnumerable invalidRepositories) + { + var parameterList = string.Join(", ", invalidRepositories); + message = string.Format( + CultureInfo.InvariantCulture, + "The list of repositories must be formatted as 'owner/name' - these values don't match this rule: {0}", + parameterList); + + } + + public override string Message + { + get + { + return message; + } + } + + #if !NETFX_CORE + /// + /// Constructs an instance of LoginAttemptsExceededException + /// + /// + /// The that holds the + /// serialized object data about the exception being thrown. + /// + /// + /// The that contains + /// contextual information about the source or destination. + /// + protected RepositoryFormatException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info == null) return; + message = info.GetString("Message"); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("Message", Message); + } +#endif + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 42a9449406..db3f3233e7 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -396,6 +396,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 8077c4c47e..7c7c420b3a 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -412,6 +412,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 61fb07ffa9..810d8272fb 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -405,6 +405,7 @@ + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 59b0edd083..855578f991 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -394,6 +394,7 @@ + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 2b01180517..4857f2c66c 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -398,6 +398,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 6b0fed0029..a003617fda 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -74,6 +74,7 @@ + From c93eeaaa407c0dcfb4cb5203b386dd51c8306549 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 17 Jul 2015 07:32:42 +0930 Subject: [PATCH 04/24] extract method for reuse as extension --- Octokit/Helpers/StringExtensions.cs | 11 ++++++++++ Octokit/Models/Request/SearchIssuesRequest.cs | 22 ++----------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/Octokit/Helpers/StringExtensions.cs b/Octokit/Helpers/StringExtensions.cs index 1823dc811d..33008b3e15 100644 --- a/Octokit/Helpers/StringExtensions.cs +++ b/Octokit/Helpers/StringExtensions.cs @@ -101,5 +101,16 @@ static IEnumerable SplitUpperCase(this string source) //We need to have the last word. yield return new String(letters, wordStartIndex, letters.Length - wordStartIndex); } + + static Regex nameWithOwner = new Regex("[a-zA-Z.]{1,}/[a-zA-Z.]{1,}" +#if (!PORTABLE && !NETFX_CORE) +, RegexOptions.Compiled +#endif +); + + internal static bool IsNameWithOwnerFormat(this string input) + { + return nameWithOwner.IsMatch(input); + } } } diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index e07f47896d..edbaaf6eb3 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -267,15 +267,10 @@ public override IReadOnlyList MergedQualifiers() if (Repos.Any()) { - var invalidFormatRepos = Repos.Where(x => !IsNameWithOwnerFormat(x)); + var invalidFormatRepos = Repos.Where(x => !x.IsNameWithOwnerFormat()); if (invalidFormatRepos.Any()) { - var parameterList = string.Join(", ", invalidFormatRepos); - var message = string.Format( - CultureInfo.InvariantCulture, - "The list of repositories must be formatted as 'owner/name' - these values don't match this rule: {0}", - parameterList); - throw new ArgumentException(message); + throw new RepositoryFormatException(invalidFormatRepos); } parameters.Add( @@ -285,19 +280,6 @@ public override IReadOnlyList MergedQualifiers() return new ReadOnlyCollection(parameters); } - // what rules do we define here? - - static Regex nameWithOwner = new Regex("[a-zA-Z.]{1,}/[a-zA-Z.]{1,}" -#if (!PORTABLE && !NETFX_CORE) - , RegexOptions.Compiled -#endif - ); - - static bool IsNameWithOwnerFormat(string input) - { - return nameWithOwner.IsMatch(input); - } - internal string DebuggerDisplay { get From 8596019e1456efcaae0191e865802f10841a74b0 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 17 Jul 2015 07:34:31 +0930 Subject: [PATCH 05/24] update SearchCodeRequest to use collection for Repos --- .../Clients/SearchClientTests.cs | 4 ++-- Octokit.Tests/Clients/SearchClientTests.cs | 6 ++--- Octokit/Models/Request/SearchCodeRequest.cs | 23 +++++++++++++++---- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index a6a058d93e..a18a17ce66 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -36,8 +36,8 @@ public async Task SearchForGitHub() [Fact] public async Task SearchForFunctionInCode() { - var request = new SearchCodeRequest("addClass"); - request.Repo = "jquery/jquery"; + var request = new SearchCodeRequest("addClass", "jquery", "jquery"); + var repos = await _gitHubClient.Search.SearchCode(request); Assert.NotEmpty(repos.Items); diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index 246b26deb1..ed59dbbe5d 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1444,8 +1444,7 @@ public void TestingTheRepoQualifier() { var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchCodeRequest("something"); - request.Repo = "octokit.net"; + var request = new SearchCodeRequest("something", "octokit", "octokit.net"); client.SearchCode(request); @@ -1474,8 +1473,7 @@ public void TestingTheRepoAndPathAndExtensionQualifiers() { var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchCodeRequest("something"); - request.Repo = "octokit.net"; + var request = new SearchCodeRequest("something", "octokit", "octokit.net"); request.Path = "tools/FAKE.core"; request.Extension = "fs"; diff --git a/Octokit/Models/Request/SearchCodeRequest.cs b/Octokit/Models/Request/SearchCodeRequest.cs index 0de5c1f97a..95ddb1f507 100644 --- a/Octokit/Models/Request/SearchCodeRequest.cs +++ b/Octokit/Models/Request/SearchCodeRequest.cs @@ -16,7 +16,10 @@ namespace Octokit [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SearchCodeRequest : BaseSearchRequest { - public SearchCodeRequest(string term) : base(term) { } + public SearchCodeRequest(string term) : base(term) + { + Repos = new Collection(); + } public SearchCodeRequest(string term, string owner, string name) : this(term) @@ -24,7 +27,9 @@ public SearchCodeRequest(string term, string owner, string name) Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - this.Repo = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); + var repo = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); + + Repos.Add(repo); } /// @@ -117,7 +122,8 @@ public IEnumerable In /// /// https://help.github.com/articles/searching-code#users-organizations-and-repositories /// - public string Repo { get; set; } + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public Collection Repos { get; set; } [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower")] public override IReadOnlyList MergedQualifiers() @@ -162,9 +168,16 @@ public override IReadOnlyList MergedQualifiers() parameters.Add(String.Format(CultureInfo.InvariantCulture, "user:{0}", User)); } - if (Repo.IsNotBlank()) + if (Repos.Any()) { - parameters.Add(String.Format(CultureInfo.InvariantCulture, "repo:{0}", Repo)); + var invalidFormatRepos = Repos.Where(x => !x.IsNameWithOwnerFormat()); + if (invalidFormatRepos.Any()) + { + throw new RepositoryFormatException(invalidFormatRepos); + } + + parameters.Add( + string.Join("+", Repos.Select(x => "repo:" + x))); } return new ReadOnlyCollection(parameters); From e4ec54d09a3874af87935e844c5a0adfcaf93ea2 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 17 Jul 2015 07:53:18 +0930 Subject: [PATCH 06/24] rework the rules --- Octokit.Tests/Clients/SearchClientTests.cs | 31 ++++++++++++++++------ Octokit/Helpers/StringExtensions.cs | 9 ++++--- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index ed59dbbe5d..39570b0986 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1159,6 +1159,24 @@ public void TestingTheRepoQualifier() Arg.Is>(d => d["q"] == "something+repo:octokit/octokit.net")); } + [Fact] + public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() + { + var connection = Substitute.For(); + var client = new SearchClient(connection); + + var request = new SearchIssuesRequest("windows"); + request.Repos = new Collection { + "haha-business" + }; + + request.SortField = IssueSearchSort.Created; + request.Order = SortDirection.Descending; + + await Assert.ThrowsAsync( + async () => await client.SearchIssues(request)); + } + [Fact] public void TestingTheRepoAndUserAndLabelQualifier() { @@ -1450,7 +1468,7 @@ public void TestingTheRepoQualifier() connection.Received().Get( Arg.Is(u => u.ToString() == "search/code"), - Arg.Is>(d => d["q"] == "something+repo:octokit.net")); + Arg.Is>(d => d["q"] == "something+repo:octokit/octokit.net")); } [Fact] @@ -1482,7 +1500,7 @@ public void TestingTheRepoAndPathAndExtensionQualifiers() connection.Received().Get( Arg.Is(u => u.ToString() == "search/code"), Arg.Is>(d => - d["q"] == "something+path:tools/FAKE.core+extension:fs+repo:octokit.net")); + d["q"] == "something+path:tools/FAKE.core+extension:fs+repo:octokit/octokit.net")); } [Fact] @@ -1491,19 +1509,16 @@ public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchIssuesRequest("windows"); + var request = new SearchCodeRequest("windows"); request.Repos = new Collection { "haha-business" }; - request.SortField = IssueSearchSort.Created; request.Order = SortDirection.Descending; - await Assert.ThrowsAsync( - async () => await client.SearchIssues(request)); + await Assert.ThrowsAsync( + async () => await client.SearchCode(request)); } - - } } } diff --git a/Octokit/Helpers/StringExtensions.cs b/Octokit/Helpers/StringExtensions.cs index 33008b3e15..670eddf244 100644 --- a/Octokit/Helpers/StringExtensions.cs +++ b/Octokit/Helpers/StringExtensions.cs @@ -102,11 +102,14 @@ static IEnumerable SplitUpperCase(this string source) yield return new String(letters, wordStartIndex, letters.Length - wordStartIndex); } - static Regex nameWithOwner = new Regex("[a-zA-Z.]{1,}/[a-zA-Z.]{1,}" + // the rule: + // Username may only contain alphanumeric characters or single hyphens + // and cannot begin or end with a hyphen + static readonly Regex nameWithOwner = new Regex("[a-z0-9.-]{1,}/[a-z0-9.-]{1,}", #if (!PORTABLE && !NETFX_CORE) -, RegexOptions.Compiled + RegexOptions.Compiled | #endif -); + RegexOptions.IgnoreCase); internal static bool IsNameWithOwnerFormat(this string input) { From b19b6b8282a6466ee66bbb25fcd457c4b56ab9b1 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 17 Jul 2015 16:45:50 +0930 Subject: [PATCH 07/24] added some docs for these changes --- docs/search.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/search.md diff --git a/docs/search.md b/docs/search.md new file mode 100644 index 0000000000..58ef1bbe88 --- /dev/null +++ b/docs/search.md @@ -0,0 +1,57 @@ +# Search + +You can use Octokit to search for different sorts of data available +on the GitHub or GitHub Enterprise server: + + - issues + - repositories + - code + - users + +I won't go into the full details here as the [developer documentation](https://developer.github.com/) +covers all the options far better than I could. + +## Search Issues + +You can search for issues containing a given phrase across many repositories: + +```csharp +// a search +var request = new SearchIssuesRequest("linux"); + +// it is highly recommended to search in specific repositories +request.Repos = new Collection { + "aspnet/dnx", + "aspnet/dnvm" +}; + +// other parameters available for tweaking the output +request.SortField = IssueSearchSort.Created; +request.Order = SortDirection.Descending; +``` + +By default, search will return the first page of results and use +a page size of 100 results. + +```csharp +request.Page = 2; +request.PerPage = 30; +``` + + +Once you've set the right parameters, execute the request: + +```csharp +// actually perform the search +var repos = await client.Search.SearchIssues(request); + +Console.WriteLine("Query has {0} matches.", repos.TotalCount); +Console.WriteLine("Response has {0} items.", repos.Items.Count); +Assert.NotEmpty(); +``` + +## Search Repositories + +## Search Code + +## Search Users From 07b8dd26032e719a914356ed8ae944564e7e3bfa Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 08:00:06 +0930 Subject: [PATCH 08/24] tidy up ctors and deprecate usage of ctor with owner and repo name --- .../Clients/SearchClientTests.cs | 23 ++++++++++++++++--- Octokit.Tests/Clients/SearchClientTests.cs | 6 +++-- Octokit/Models/Request/SearchIssuesRequest.cs | 13 +++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index a18a17ce66..c37bda62cf 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -63,7 +63,8 @@ public async Task SearchForWordInCode() [Fact] public async Task SearchForOpenIssues() { - var request = new SearchIssuesRequest("phone", "caliburn-micro", "caliburn.micro"); + var request = new SearchIssuesRequest("phone"); + request.Repos.Add("caliburn-micro/caliburn.micro"); request.State = ItemState.Open; var issues = await _gitHubClient.Search.SearchIssues(request); @@ -72,9 +73,25 @@ public async Task SearchForOpenIssues() } [Fact] - public async Task SearchForAllIssues() + public async Task SearchForAllIssuesWithouTaskUsingTerm() { - var request = new SearchIssuesRequest("phone", "caliburn-micro", "caliburn.micro"); + var request = new SearchIssuesRequest(); + request.Repos.Add("caliburn-micro/caliburn.micro"); + + var issues = await _gitHubClient.Search.SearchIssues(request); + + var closedIssues = issues.Items.Where(x => x.State == ItemState.Closed); + var openedIssues = issues.Items.Where(x => x.State == ItemState.Open); + + Assert.NotEmpty(closedIssues); + Assert.NotEmpty(openedIssues); + } + + [Fact] + public async Task SearchForAllIssuesUsingTerm() + { + var request = new SearchIssuesRequest("phone"); + request.Repos.Add("caliburn-micro/caliburn.micro"); var issues = await _gitHubClient.Search.SearchIssues(request); diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index 39570b0986..1b9cc951fe 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1150,7 +1150,8 @@ public void TestingTheRepoQualifier() { var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchIssuesRequest("something", "octokit", "octokit.net"); + var request = new SearchIssuesRequest("something"); + request.Repos.Add("octokit/octokit.net"); client.SearchIssues(request); @@ -1182,7 +1183,8 @@ public void TestingTheRepoAndUserAndLabelQualifier() { var connection = Substitute.For(); var client = new SearchClient(connection); - var request = new SearchIssuesRequest("something", "octokit", "octokit.net"); + var request = new SearchIssuesRequest("something"); + request.Repos.Add("octokit/octokit.net"); request.User = "alfhenrik"; request.Labels = new[] { "bug" }; diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index edbaaf6eb3..316f71b8fa 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -16,11 +16,24 @@ namespace Octokit [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SearchIssuesRequest : BaseSearchRequest { + /// + /// Search without specifying a keyword + /// + public SearchIssuesRequest() + { + Repos = new Collection(); + } + + /// + /// Search using a specify keyword + /// + /// The term to filter on public SearchIssuesRequest(string term) : base(term) { Repos = new Collection(); } + [Obsolete("this will be deprecated in a future version")] public SearchIssuesRequest(string term, string owner, string name) : this(term) { From cb41c58581790fa7fed8b7c8a442b2d914f4d27c Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 08:39:22 +0930 Subject: [PATCH 09/24] :lipstick: cleanup --- Octokit/Models/Request/SearchIssuesRequest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index 316f71b8fa..12858eeac5 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Globalization; using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; namespace Octokit { From d1bd50cdf3b3efb8e45cb092bfed5632081d1c35 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 08:39:31 +0930 Subject: [PATCH 10/24] wrote some examples --- docs/search.md | 99 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/docs/search.md b/docs/search.md index 58ef1bbe88..4024f79bd5 100644 --- a/docs/search.md +++ b/docs/search.md @@ -8,36 +8,73 @@ on the GitHub or GitHub Enterprise server: - code - users -I won't go into the full details here as the [developer documentation](https://developer.github.com/) -covers all the options far better than I could. - ## Search Issues -You can search for issues containing a given phrase across many repositories: +You can search for issues containing a given phrase across many +repositories: ```csharp -// a search -var request = new SearchIssuesRequest("linux"); +// searching without specifying a term +var request = new SearchIssuesRequest(); + +// you should focus your search to a specific repo +request.Repos.Add("aspnet/dnx"); -// it is highly recommended to search in specific repositories +// or use a series of repositories request.Repos = new Collection { "aspnet/dnx", "aspnet/dnvm" }; +``` -// other parameters available for tweaking the output -request.SortField = IssueSearchSort.Created; -request.Order = SortDirection.Descending; +There's many other options available here to tweak +your search criteria: + +```csharp +// if you're searching for a specific term, you can +// focus your search on specific criteria +request.In = new[] { + IssueInQualifier.Title, + IssueInQualifier.Body +}; + +// you can restrict your search to issues or pull requests +request.Type = IssueTypeQualifier.Issue; + +// you can filter on when the issue was created or updated +var aWeekAgo = DateTime.Now.Subtract(TimeSpan.FromDays(7)); +request.Created = new DateRange(aWeekAgo, SearchQualifierOperator.GreaterThan) + +// you can search for issues created by, assigned to +// or mentioning a specific user +request.Author = "davidfowl"; +request.Assignee = "damianedwards"; +request.Mentions = "shiftkey"; +request.Commenter = "haacked"; + +// rather than setting all these, you can use this to find +// all the above for a specific user with this one-liner +request.Involves = "davidfowl"; + +// by default this will search on open issues, set this if +// you want to get all issues +request.State = ItemState.All; +// or to just search closed issues +request.State = ItemState.Closed; ``` -By default, search will return the first page of results and use -a page size of 100 results. +And there's other options available for how the results are returned: ```csharp -request.Page = 2; +request.SortField = IssueSearchSort.Created; +request.Order = SortDirection.Descending; + +// 100 results per page as default request.PerPage = 30; -``` +// execute this when you want to fetch multiple pages +request.Page = 2; +``` Once you've set the right parameters, execute the request: @@ -47,11 +84,43 @@ var repos = await client.Search.SearchIssues(request); Console.WriteLine("Query has {0} matches.", repos.TotalCount); Console.WriteLine("Response has {0} items.", repos.Items.Count); -Assert.NotEmpty(); ``` +## Search Pull Requests + +Another scenario to consider is how to search broadly: + +```csharp +var threeMonthsAgoIsh = DateTime.Now.Subtract(TimeSpan.FromDays(90)); + +// search for a specific term +var request = new SearchIssuesRequest("linux") +{ + // only search pull requests + Type = IssueTypeQualifier.PR, + + // search across open and closed PRs + State = ItemState.All, + + // search repositories which contain code + // matching a given language + Language = Language.CSharp, + + // focus on pull requests updated recently + Updated = new DateRange(threeMonthsAgoIsh, SearchQualifierOperator.GreaterThan) +}; +``` + + + ## Search Repositories +**TODO** + ## Search Code +**TODO** + ## Search Users + +**TODO** From f3fab080f9d27c6be309c41f9cf63f6fe8672557 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 08:42:14 +0930 Subject: [PATCH 11/24] tweaks --- docs/search.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/search.md b/docs/search.md index 4024f79bd5..19970f9e86 100644 --- a/docs/search.md +++ b/docs/search.md @@ -10,11 +10,10 @@ on the GitHub or GitHub Enterprise server: ## Search Issues -You can search for issues containing a given phrase across many -repositories: +A common scenario is to search for issues to triage: ```csharp -// searching without specifying a term +// you can also specify a search term here var request = new SearchIssuesRequest(); // you should focus your search to a specific repo @@ -63,7 +62,7 @@ request.State = ItemState.All; request.State = ItemState.Closed; ``` -And there's other options available for how the results are returned: +There's other options available to control how the results are returned: ```csharp request.SortField = IssueSearchSort.Created; From 3b2150273f4ac172e6be33046849a3a147157659 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 08:44:57 +0930 Subject: [PATCH 12/24] some more tweaks --- docs/search.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/search.md b/docs/search.md index 19970f9e86..0506d9811a 100644 --- a/docs/search.md +++ b/docs/search.md @@ -71,14 +71,13 @@ request.Order = SortDirection.Descending; // 100 results per page as default request.PerPage = 30; -// execute this when you want to fetch multiple pages +// set this when you want to fetch subsequent pages request.Page = 2; ``` Once you've set the right parameters, execute the request: ```csharp -// actually perform the search var repos = await client.Search.SearchIssues(request); Console.WriteLine("Query has {0} matches.", repos.TotalCount); From af2db5abd0e2f040001dee145ef5e39cb83b726f Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 11:57:01 +0930 Subject: [PATCH 13/24] HACK: disable xml-doc checks so i can any possible msbuild issues --- Octokit.Reactive/Octokit.Reactive.csproj | 3 ++- Octokit/Octokit-Portable.csproj | 3 ++- Octokit/Octokit-netcore45.csproj | 3 ++- Octokit/Octokit.csproj | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index ef406e8080..8d00a7f60e 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -39,7 +39,8 @@ false true ..\Octokit.ruleset - bin\Release\Net45\Octokit.Reactive.XML + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 855578f991..1bc14c813f 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -37,7 +37,8 @@ 4 true ..\Octokit.ruleset - bin\Release\Portable\Octokit.XML + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index 4857f2c66c..8e332231ca 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -41,7 +41,8 @@ 4 true ..\Octokit.ruleset - bin\Release\NetCore45\Octokit.XML + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index a003617fda..8e404d2c80 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -41,7 +41,8 @@ false true ..\Octokit.ruleset - bin\Release\Net45\Octokit.XML + + From 4ca3aa672fee336440298c5f5e7d744405e3000a Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 11:57:26 +0930 Subject: [PATCH 14/24] bump fake.core --- build.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cmd b/build.cmd index 5358f51d77..4cf5c3dbcb 100644 --- a/build.cmd +++ b/build.cmd @@ -1,7 +1,7 @@ @echo off "tools\nuget\nuget.exe" "install" "xunit.runner.console" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "2.0.0" -"tools\nuget\nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "3.12.2" +"tools\nuget\nuget.exe" "install" "FAKE.Core" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "3.37.1" "tools\nuget\nuget.exe" "install" "SourceLink.Fake" "-OutputDirectory" "tools" "-ExcludeVersion" "-version" "1.0.0" :Build From fae9fc0ab8a14f7149f2dc9de23f0ac7c04f99c4 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 11:58:14 +0930 Subject: [PATCH 15/24] update gitignore to skip FAKE's temporary folder --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 8f11a4e8ab..d053335d43 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,9 @@ tools/xunit.runner.console *.ncrunch* *.GhostDoc.xml +# FAKE temporary files +.fake/ + # New VS Test Runner creates arbitrary folders with PDBs *.pdb pingme.txt \ No newline at end of file From cf86cb1ba1e43f3849c6af5e98225f37e688cdcd Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 12:40:03 +0930 Subject: [PATCH 16/24] tie things to version 12.0 of MSBuild --- build.fsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.fsx b/build.fsx index 77893c299a..0c616fa768 100644 --- a/build.fsx +++ b/build.fsx @@ -29,7 +29,10 @@ let releaseNotes = let buildMode = getBuildParamOrDefault "buildMode" "Release" -MSBuildDefaults <- { MSBuildDefaults with Verbosity = Some MSBuildVerbosity.Minimal } +MSBuildDefaults <- { + MSBuildDefaults with + ToolsVersion = Some "12.0" + Verbosity = Some MSBuildVerbosity.Minimal } Target "Clean" (fun _ -> CleanDirs [buildDir; reactiveBuildDir; testResultsDir; packagingRoot; packagingDir; reactivePackagingDir] From 2cd0a37f386bac54e28ef51a67b7fb438187b9f1 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 19 Jul 2015 12:47:22 +0930 Subject: [PATCH 17/24] trying a different approach to get this compiling using the right version of msbuild --- build.fsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/build.fsx b/build.fsx index 0c616fa768..c226326da4 100644 --- a/build.fsx +++ b/build.fsx @@ -65,9 +65,19 @@ Target "FixProjects" (fun _ -> |> Fake.MSBuild.ProjectSystem.FixProjectFiles "./Octokit.Reactive/Octokit.Reactive.csproj" ) +let setParams defaults = { + defaults with + ToolsVersion = Some("12.0") + Targets = ["Build"] + Properties = + [ + "Configuration", buildMode + ] + } + Target "BuildApp" (fun _ -> - MSBuild null "Build" ["Configuration", buildMode] ["./Octokit.sln"] - |> Log "AppBuild-Output: " + build setParams "./Octokit.sln" + |> DoNothing ) Target "ConventionTests" (fun _ -> From 7bd0447bd62173195ab2c2d4f3993d834c69cd5a Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Sun, 19 Jul 2015 13:01:23 +0200 Subject: [PATCH 18/24] Changed repos to a specialized collection --- .../Clients/SearchClientTests.cs | 12 ++-- Octokit.Tests/Clients/SearchClientTests.cs | 12 ++-- Octokit/Models/Request/SearchCodeRequest.cs | 8 +-- Octokit/Models/Request/SearchIssuesRequest.cs | 60 +++++++++++++++++-- 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index c37bda62cf..301c73ef43 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -47,9 +47,9 @@ public async Task SearchForFunctionInCode() public async Task SearchForWordInCode() { var request = new SearchIssuesRequest("windows"); - request.Repos = new Collection { - "aspnet/dnx", - "aspnet/dnvm" + request.Repos = new RepositoryCollection { + { "aspnet", "dnx" }, + { "aspnet", "dnvm" } }; request.SortField = IssueSearchSort.Created; @@ -64,7 +64,7 @@ public async Task SearchForWordInCode() public async Task SearchForOpenIssues() { var request = new SearchIssuesRequest("phone"); - request.Repos.Add("caliburn-micro/caliburn.micro"); + request.Repos.Add("caliburn-micro", "caliburn.micro"); request.State = ItemState.Open; var issues = await _gitHubClient.Search.SearchIssues(request); @@ -76,7 +76,7 @@ public async Task SearchForOpenIssues() public async Task SearchForAllIssuesWithouTaskUsingTerm() { var request = new SearchIssuesRequest(); - request.Repos.Add("caliburn-micro/caliburn.micro"); + request.Repos.Add("caliburn-micro", "caliburn.micro"); var issues = await _gitHubClient.Search.SearchIssues(request); @@ -91,7 +91,7 @@ public async Task SearchForAllIssuesWithouTaskUsingTerm() public async Task SearchForAllIssuesUsingTerm() { var request = new SearchIssuesRequest("phone"); - request.Repos.Add("caliburn-micro/caliburn.micro"); + request.Repos.Add("caliburn-micro", "caliburn.micro"); var issues = await _gitHubClient.Search.SearchIssues(request); diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index 1b9cc951fe..4547b4006c 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1151,7 +1151,7 @@ public void TestingTheRepoQualifier() var connection = Substitute.For(); var client = new SearchClient(connection); var request = new SearchIssuesRequest("something"); - request.Repos.Add("octokit/octokit.net"); + request.Repos.Add("octokit", "octokit.net"); client.SearchIssues(request); @@ -1167,8 +1167,8 @@ public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() var client = new SearchClient(connection); var request = new SearchIssuesRequest("windows"); - request.Repos = new Collection { - "haha-business" + request.Repos = new RepositoryCollection { + { "haha-business", "some&name" } }; request.SortField = IssueSearchSort.Created; @@ -1184,7 +1184,7 @@ public void TestingTheRepoAndUserAndLabelQualifier() var connection = Substitute.For(); var client = new SearchClient(connection); var request = new SearchIssuesRequest("something"); - request.Repos.Add("octokit/octokit.net"); + request.Repos.Add("octokit", "octokit.net"); request.User = "alfhenrik"; request.Labels = new[] { "bug" }; @@ -1512,8 +1512,8 @@ public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() var client = new SearchClient(connection); var request = new SearchCodeRequest("windows"); - request.Repos = new Collection { - "haha-business" + request.Repos = new RepositoryCollection { + { "haha-business", "some&name" } }; request.Order = SortDirection.Descending; diff --git a/Octokit/Models/Request/SearchCodeRequest.cs b/Octokit/Models/Request/SearchCodeRequest.cs index 95ddb1f507..570b5fb814 100644 --- a/Octokit/Models/Request/SearchCodeRequest.cs +++ b/Octokit/Models/Request/SearchCodeRequest.cs @@ -18,7 +18,7 @@ public class SearchCodeRequest : BaseSearchRequest { public SearchCodeRequest(string term) : base(term) { - Repos = new Collection(); + Repos = new RepositoryCollection(); } public SearchCodeRequest(string term, string owner, string name) @@ -27,9 +27,7 @@ public SearchCodeRequest(string term, string owner, string name) Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - var repo = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); - - Repos.Add(repo); + Repos.Add(owner, name); } /// @@ -123,7 +121,7 @@ public IEnumerable In /// https://help.github.com/articles/searching-code#users-organizations-and-repositories /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Collection Repos { get; set; } + public RepositoryCollection Repos { get; set; } [SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "System.String.ToLower")] public override IReadOnlyList MergedQualifiers() diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index 12858eeac5..024e86a4fd 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using Octokit.Internal; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; @@ -20,7 +21,7 @@ public class SearchIssuesRequest : BaseSearchRequest /// public SearchIssuesRequest() { - Repos = new Collection(); + Repos = new RepositoryCollection(); } /// @@ -29,7 +30,7 @@ public SearchIssuesRequest() /// The term to filter on public SearchIssuesRequest(string term) : base(term) { - Repos = new Collection(); + Repos = new RepositoryCollection(); } [Obsolete("this will be deprecated in a future version")] @@ -39,9 +40,7 @@ public SearchIssuesRequest(string term, string owner, string name) Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - var repo = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); - - Repos.Add(repo); + Repos.Add(owner, name); } /// @@ -196,7 +195,7 @@ public IEnumerable Labels public string User { get; set; } [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Collection Repos { get; set; } + public RepositoryCollection Repos { get; set; } public override IReadOnlyList MergedQualifiers() { @@ -337,4 +336,53 @@ public enum IssueTypeQualifier [Parameter(Value = "issue")] Issue } + + public class RepositoryCollection : IEnumerable + { + readonly ICollection _repositories = new List(); + + public IEnumerator GetEnumerator() + { + return _repositories.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + _repositories.Add(GetRepositoryName(owner, name)); + } + + public void Clear() + { + _repositories.Clear(); + } + + public bool Contains(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _repositories.Contains(GetRepositoryName(owner, name)); + } + + public bool Remove(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return _repositories.Remove(GetRepositoryName(owner, name)); + } + + private static string GetRepositoryName(string owner, string name) + { + return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); + } + } } From f98ff15211d4e2c712f4baa44e00b63e0c926a0b Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Sun, 19 Jul 2015 13:30:32 +0200 Subject: [PATCH 19/24] Added overloads with a single string --- .../Clients/SearchClientTests.cs | 2 +- Octokit.Tests/Clients/SearchClientTests.cs | 4 ++-- Octokit/Models/Request/SearchIssuesRequest.cs | 21 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/SearchClientTests.cs b/Octokit.Tests.Integration/Clients/SearchClientTests.cs index 301c73ef43..ba6f4d89fd 100644 --- a/Octokit.Tests.Integration/Clients/SearchClientTests.cs +++ b/Octokit.Tests.Integration/Clients/SearchClientTests.cs @@ -76,7 +76,7 @@ public async Task SearchForOpenIssues() public async Task SearchForAllIssuesWithouTaskUsingTerm() { var request = new SearchIssuesRequest(); - request.Repos.Add("caliburn-micro", "caliburn.micro"); + request.Repos.Add("caliburn-micro/caliburn.micro"); var issues = await _gitHubClient.Search.SearchIssues(request); diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index 4547b4006c..359b7c607e 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1184,7 +1184,7 @@ public void TestingTheRepoAndUserAndLabelQualifier() var connection = Substitute.For(); var client = new SearchClient(connection); var request = new SearchIssuesRequest("something"); - request.Repos.Add("octokit", "octokit.net"); + request.Repos.Add("octokit/octokit.net"); request.User = "alfhenrik"; request.Labels = new[] { "bug" }; @@ -1513,7 +1513,7 @@ public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() var request = new SearchCodeRequest("windows"); request.Repos = new RepositoryCollection { - { "haha-business", "some&name" } + "haha-business" }; request.Order = SortDirection.Descending; diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index 024e86a4fd..b93b88bd3c 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -359,6 +359,13 @@ public void Add(string owner, string name) _repositories.Add(GetRepositoryName(owner, name)); } + public void Add(string nameWithOwner) + { + Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"); + + _repositories.Add(nameWithOwner); + } + public void Clear() { _repositories.Clear(); @@ -372,6 +379,13 @@ public bool Contains(string owner, string name) return _repositories.Contains(GetRepositoryName(owner, name)); } + public bool Contains(string nameWithOwner) + { + Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"); + + return _repositories.Contains(nameWithOwner); + } + public bool Remove(string owner, string name) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); @@ -380,6 +394,13 @@ public bool Remove(string owner, string name) return _repositories.Remove(GetRepositoryName(owner, name)); } + public bool Remove(string nameWithOwner) + { + Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"); + + return _repositories.Remove(nameWithOwner); + } + private static string GetRepositoryName(string owner, string name) { return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); From 43feb744d8b76194d3253f301862bb8e42063486 Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Mon, 20 Jul 2015 22:51:16 +0200 Subject: [PATCH 20/24] Changed collection to inherit from Collection --- Octokit/Models/Request/SearchIssuesRequest.cs | 57 ++----------------- 1 file changed, 6 insertions(+), 51 deletions(-) diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index b93b88bd3c..953eb75c86 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using Octokit.Internal; using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; @@ -337,72 +336,28 @@ public enum IssueTypeQualifier Issue } - public class RepositoryCollection : IEnumerable + public class RepositoryCollection : Collection { - readonly ICollection _repositories = new List(); - - public IEnumerator GetEnumerator() - { - return _repositories.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - public void Add(string owner, string name) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - - _repositories.Add(GetRepositoryName(owner, name)); - } - - public void Add(string nameWithOwner) - { - Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"); - - _repositories.Add(nameWithOwner); - } - - public void Clear() - { - _repositories.Clear(); + Add(GetRepositoryName(owner, name)); } public bool Contains(string owner, string name) { - Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); - Ensure.ArgumentNotNullOrEmptyString(name, "name"); - - return _repositories.Contains(GetRepositoryName(owner, name)); + return Contains(GetRepositoryName(owner, name)); } - public bool Contains(string nameWithOwner) + public bool Remove(string owner, string name) { - Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"); - - return _repositories.Contains(nameWithOwner); + return Remove(GetRepositoryName(owner, name)); } - public bool Remove(string owner, string name) + private static string GetRepositoryName(string owner, string name) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); - return _repositories.Remove(GetRepositoryName(owner, name)); - } - - public bool Remove(string nameWithOwner) - { - Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"); - - return _repositories.Remove(nameWithOwner); - } - - private static string GetRepositoryName(string owner, string name) - { return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); } } From 78796fbbd1db07da74bd5f5bc58702c82c0eac37 Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Mon, 20 Jul 2015 23:16:03 +0200 Subject: [PATCH 21/24] Updated search docs with new collection type --- docs/search.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/search.md b/docs/search.md index 0506d9811a..a2e819e6b6 100644 --- a/docs/search.md +++ b/docs/search.md @@ -18,12 +18,18 @@ var request = new SearchIssuesRequest(); // you should focus your search to a specific repo request.Repos.Add("aspnet/dnx"); +request.Repos.Add("aspnet", "dnvm"); // or use a series of repositories -request.Repos = new Collection { +request.Repos = new RepositoryCollection { "aspnet/dnx", "aspnet/dnvm" }; + +request.Repos = new RepositoryCollection { + { "aspnet", "dnx" }, + { "aspnet", "dnvm" } +}; ``` There's many other options available here to tweak From bb438bfd7cfe5a95907aeda68622d7537277b2f0 Mon Sep 17 00:00:00 2001 From: Kristian Hellang Date: Mon, 20 Jul 2015 23:17:51 +0200 Subject: [PATCH 22/24] Updated comment :lipstick: --- docs/search.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/search.md b/docs/search.md index a2e819e6b6..c8cd57e55b 100644 --- a/docs/search.md +++ b/docs/search.md @@ -16,7 +16,7 @@ A common scenario is to search for issues to triage: // you can also specify a search term here var request = new SearchIssuesRequest(); -// you should focus your search to a specific repo +// you can add individual repos to focus your search request.Repos.Add("aspnet/dnx"); request.Repos.Add("aspnet", "dnvm"); From ee1c9b90a20a589d9f1e9ce7179ac843b471a3f2 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Thu, 23 Jul 2015 21:52:39 +0930 Subject: [PATCH 23/24] oops, this test should fail --- Octokit.Tests/Clients/SearchClientTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Octokit.Tests/Clients/SearchClientTests.cs b/Octokit.Tests/Clients/SearchClientTests.cs index 359b7c607e..ab4cd108e9 100644 --- a/Octokit.Tests/Clients/SearchClientTests.cs +++ b/Octokit.Tests/Clients/SearchClientTests.cs @@ -1168,7 +1168,7 @@ public async Task ErrorOccursWhenSpecifyingInvalidFormatForRepos() var request = new SearchIssuesRequest("windows"); request.Repos = new RepositoryCollection { - { "haha-business", "some&name" } + "haha-business" }; request.SortField = IssueSearchSort.Created; From b763985aa9b0e8b132fb65bddb6f6e510265ad89 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Thu, 23 Jul 2015 22:04:25 +0930 Subject: [PATCH 24/24] one convention test needing review --- Octokit/Models/Request/SearchIssuesRequest.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Octokit/Models/Request/SearchIssuesRequest.cs b/Octokit/Models/Request/SearchIssuesRequest.cs index 953eb75c86..bf18fb1c2c 100644 --- a/Octokit/Models/Request/SearchIssuesRequest.cs +++ b/Octokit/Models/Request/SearchIssuesRequest.cs @@ -336,6 +336,7 @@ public enum IssueTypeQualifier Issue } + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class RepositoryCollection : Collection { public void Add(string owner, string name) @@ -353,12 +354,20 @@ public bool Remove(string owner, string name) return Remove(GetRepositoryName(owner, name)); } - private static string GetRepositoryName(string owner, string name) + static string GetRepositoryName(string owner, string name) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(name, "name"); return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", owner, name); } + + internal string DebuggerDisplay + { + get + { + return String.Format(CultureInfo.InvariantCulture, "Repositories: {0}", Count); + } + } } }