From 2a2bd216a18ffc01efaa98c3283adf75e804001e Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 3 Aug 2016 18:26:07 +1000 Subject: [PATCH 01/22] Implement Issue Timeline preview API --- Octokit/Clients/IIssueTimelineClient.cs | 11 ++++++ Octokit/Clients/IssueTimelineClient.cs | 22 ++++++++++++ Octokit/Helpers/ApiUrls.cs | 5 +++ Octokit/Models/Response/RenameInfo.cs | 8 +++++ Octokit/Models/Response/SourceInfo.cs | 9 +++++ Octokit/Models/Response/TimelineEventInfo.cs | 36 ++++++++++++++++++++ Octokit/Octokit-Mono.csproj | 5 +++ Octokit/Octokit-MonoAndroid.csproj | 5 +++ Octokit/Octokit-Monotouch.csproj | 5 +++ Octokit/Octokit-Portable.csproj | 5 +++ Octokit/Octokit-netcore45.csproj | 5 +++ Octokit/Octokit.csproj | 5 +++ 12 files changed, 121 insertions(+) create mode 100644 Octokit/Clients/IIssueTimelineClient.cs create mode 100644 Octokit/Clients/IssueTimelineClient.cs create mode 100644 Octokit/Models/Response/RenameInfo.cs create mode 100644 Octokit/Models/Response/SourceInfo.cs create mode 100644 Octokit/Models/Response/TimelineEventInfo.cs diff --git a/Octokit/Clients/IIssueTimelineClient.cs b/Octokit/Clients/IIssueTimelineClient.cs new file mode 100644 index 0000000000..55d1b764f1 --- /dev/null +++ b/Octokit/Clients/IIssueTimelineClient.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Octokit +{ + public interface IIssueTimelineClient + { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] + Task> Get(string owner, string repo, int number); + } +} diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs new file mode 100644 index 0000000000..9e19116236 --- /dev/null +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -0,0 +1,22 @@ +#if NET_45 +using System.Collections.Generic; +using System.Threading.Tasks; +#endif + +namespace Octokit +{ + public class IssueTimelineClient: ApiClient, IIssueTimelineClient + { + public IssueTimelineClient(IApiConnection apiConnection) : base(apiConnection) + { + } + + public Task> Get(string owner, string repo, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); + + return ApiConnection.GetAll(ApiUrls.IssueTimeline(owner, repo, number), ApiOptions.None); + } + } +} diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 5dbfe3d313..3f9eea84cb 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -330,6 +330,11 @@ public static Uri IssueReactions(string owner, string name, int number) return "repos/{0}/{1}/issues/{2}/reactions".FormatUri(owner, name, number); } + public static Uri IssueTimeline(string owner, string repo, int number) + { + return "repos/{0}/{1}/issues/{2}/timeline".FormatUri(owner, repo, number); + } + /// /// Returns the for the reaction of a specified issue. /// diff --git a/Octokit/Models/Response/RenameInfo.cs b/Octokit/Models/Response/RenameInfo.cs new file mode 100644 index 0000000000..77b390a0fa --- /dev/null +++ b/Octokit/Models/Response/RenameInfo.cs @@ -0,0 +1,8 @@ +namespace Octokit +{ + public class RenameInfo + { + public string From { get; protected set; } + public string To { get; protected set; } + } +} diff --git a/Octokit/Models/Response/SourceInfo.cs b/Octokit/Models/Response/SourceInfo.cs new file mode 100644 index 0000000000..ca895e75b9 --- /dev/null +++ b/Octokit/Models/Response/SourceInfo.cs @@ -0,0 +1,9 @@ +namespace Octokit +{ + public class SourceInfo + { + public User Actor { get; protected set; } + public int Id { get; protected set; } + public string Url { get; protected set; } + } +} diff --git a/Octokit/Models/Response/TimelineEventInfo.cs b/Octokit/Models/Response/TimelineEventInfo.cs new file mode 100644 index 0000000000..da32d18094 --- /dev/null +++ b/Octokit/Models/Response/TimelineEventInfo.cs @@ -0,0 +1,36 @@ +using System; + +namespace Octokit +{ + public class TimelineEventInfo + { + public TimelineEventInfo() { } + + public TimelineEventInfo(int id, string url, User actor, string commitId, EventInfoState @event, DateTimeOffset createdAt, Label label, User assignee, Milestone milestone, SourceInfo source, RenameInfo rename) + { + Id = id; + Url = url; + Actor = actor; + CommitId = commitId; + Event = @event; + CreatedAt = createdAt; + Label = label; + Assignee = assignee; + Milestone = milestone; + Source = source; + Rename = rename; + } + + public int Id { get; protected set; } + public string Url { get; protected set; } + public User Actor { get; protected set; } + public string CommitId { get; protected set; } + public EventInfoState Event { get; protected set; } + public DateTimeOffset CreatedAt { get; protected set; } + public Label Label { get; protected set; } + public User Assignee { get; protected set; } + public Milestone Milestone { get; protected set; } + public SourceInfo Source { get; protected set; } + public RenameInfo Rename { get; protected set; } + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index dda54a6619..6671612640 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -484,6 +484,11 @@ + + + + + \ No newline at end of file diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 2c472d67d9..3b494a38e5 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -495,6 +495,11 @@ + + + + + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 16c0e17c21..40f8ba1911 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -491,6 +491,11 @@ + + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index f28e8a25ac..3270a8efa6 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -481,6 +481,11 @@ + + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index d807f7c109..c0b3f04f29 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -488,6 +488,11 @@ + + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index 4adbffc62a..0e0f5347c4 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -61,10 +61,12 @@ + + @@ -207,7 +209,10 @@ + + + From 9bb96e55c1b678953a533981b1d3a7a9b52ad59f Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 3 Aug 2016 19:19:54 +1000 Subject: [PATCH 02/22] Add DebuggerDisplay to response models --- Octokit/Models/Response/RenameInfo.cs | 11 ++++++++++- Octokit/Models/Response/SourceInfo.cs | 11 ++++++++++- Octokit/Models/Response/TimelineEventInfo.cs | 8 ++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Octokit/Models/Response/RenameInfo.cs b/Octokit/Models/Response/RenameInfo.cs index 77b390a0fa..d21f0f3234 100644 --- a/Octokit/Models/Response/RenameInfo.cs +++ b/Octokit/Models/Response/RenameInfo.cs @@ -1,8 +1,17 @@ -namespace Octokit +using System.Diagnostics; +using System.Globalization; + +namespace Octokit { + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class RenameInfo { public string From { get; protected set; } public string To { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "From: {0} To: {1}", From, To); } + } } } diff --git a/Octokit/Models/Response/SourceInfo.cs b/Octokit/Models/Response/SourceInfo.cs index ca895e75b9..ff91bdfec5 100644 --- a/Octokit/Models/Response/SourceInfo.cs +++ b/Octokit/Models/Response/SourceInfo.cs @@ -1,9 +1,18 @@ -namespace Octokit +using System.Diagnostics; +using System.Globalization; + +namespace Octokit { + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class SourceInfo { public User Actor { get; protected set; } public int Id { get; protected set; } public string Url { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Id: {0} Url: {1}", Id, Url); } + } } } diff --git a/Octokit/Models/Response/TimelineEventInfo.cs b/Octokit/Models/Response/TimelineEventInfo.cs index da32d18094..f67c6fc7dd 100644 --- a/Octokit/Models/Response/TimelineEventInfo.cs +++ b/Octokit/Models/Response/TimelineEventInfo.cs @@ -1,7 +1,10 @@ using System; +using System.Diagnostics; +using System.Globalization; namespace Octokit { + [DebuggerDisplay("{DebuggerDisplay,nq}")] public class TimelineEventInfo { public TimelineEventInfo() { } @@ -32,5 +35,10 @@ public TimelineEventInfo(int id, string url, User actor, string commitId, EventI public Milestone Milestone { get; protected set; } public SourceInfo Source { get; protected set; } public RenameInfo Rename { get; protected set; } + + internal string DebuggerDisplay + { + get { return string.Format(CultureInfo.InvariantCulture, "Id: {0} CreatedAt: {1} Event: {2}", Id, CreatedAt, Event); } + } } } From 176447a3e1f3f0da5bd90a9718237045c60e2cd8 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 3 Aug 2016 19:20:55 +1000 Subject: [PATCH 03/22] Rename method --- Octokit/Clients/IIssueTimelineClient.cs | 7 ++++--- Octokit/Clients/IssueTimelineClient.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Octokit/Clients/IIssueTimelineClient.cs b/Octokit/Clients/IIssueTimelineClient.cs index 55d1b764f1..31c001aa6f 100644 --- a/Octokit/Clients/IIssueTimelineClient.cs +++ b/Octokit/Clients/IIssueTimelineClient.cs @@ -1,11 +1,12 @@ -using System.Collections.Generic; +#if NET_45 +using System.Collections.Generic; using System.Threading.Tasks; +#endif namespace Octokit { public interface IIssueTimelineClient { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Get")] - Task> Get(string owner, string repo, int number); + Task> GetAllForIssue(string owner, string repo, int number); } } diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs index 9e19116236..5e63f434e6 100644 --- a/Octokit/Clients/IssueTimelineClient.cs +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -11,7 +11,7 @@ public IssueTimelineClient(IApiConnection apiConnection) : base(apiConnection) { } - public Task> Get(string owner, string repo, int number) + public Task> GetAllForIssue(string owner, string repo, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); From 2ed77b2d0c7d50e5b03d255c399c10e283e0830b Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Wed, 3 Aug 2016 19:21:22 +1000 Subject: [PATCH 04/22] Add Observable Issue Timeline client --- .../Clients/IObservableIssueTimelineClient.cs | 9 +++++++ .../Clients/ObservableIssueTimelineClient.cs | 25 +++++++++++++++++++ .../Clients/ObservableIssuesClient.cs | 3 +++ Octokit.Reactive/Octokit.Reactive-Mono.csproj | 2 ++ .../Octokit.Reactive-MonoAndroid.csproj | 2 ++ .../Octokit.Reactive-Monotouch.csproj | 2 ++ Octokit.Reactive/Octokit.Reactive.csproj | 2 ++ Octokit/Clients/IIssuesClient.cs | 2 ++ Octokit/Clients/IssuesClient.cs | 3 +++ 9 files changed, 50 insertions(+) create mode 100644 Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs create mode 100644 Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs diff --git a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs new file mode 100644 index 0000000000..79b8ba573f --- /dev/null +++ b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs @@ -0,0 +1,9 @@ +using System; + +namespace Octokit.Reactive +{ + public interface IObservableIssueTimelineClient + { + IObservable GetAllForIssue(string owner, string repo, int number); + } +} diff --git a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs new file mode 100644 index 0000000000..bbc5593ca3 --- /dev/null +++ b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs @@ -0,0 +1,25 @@ +using System; +using Octokit.Reactive.Internal; + +namespace Octokit.Reactive +{ + public class ObservableIssueTimelineClient : IObservableIssueTimelineClient + { + readonly IConnection _connection; + + public ObservableIssueTimelineClient(IGitHubClient client) + { + Ensure.ArgumentNotNull(client, "client"); + + _connection = client.Connection; + } + + public IObservable GetAllForIssue(string owner, string repo, int number) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); + + return _connection.GetAndFlattenAllPages(ApiUrls.IssueTimeline(owner, repo, number), ApiOptions.None); + } + } +} diff --git a/Octokit.Reactive/Clients/ObservableIssuesClient.cs b/Octokit.Reactive/Clients/ObservableIssuesClient.cs index 5156fe68b7..fb0e240bb1 100644 --- a/Octokit.Reactive/Clients/ObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssuesClient.cs @@ -43,6 +43,8 @@ public class ObservableIssuesClient : IObservableIssuesClient /// public IObservableMilestonesClient Milestone { get; private set; } + public IObservableIssueTimelineClient Timeline { get; private set; } + public ObservableIssuesClient(IGitHubClient client) { Ensure.ArgumentNotNull(client, "client"); @@ -54,6 +56,7 @@ public ObservableIssuesClient(IGitHubClient client) Labels = new ObservableIssuesLabelsClient(client); Milestone = new ObservableMilestonesClient(client); Comment = new ObservableIssueCommentsClient(client); + Timeline = new ObservableIssueTimelineClient(client); } /// diff --git a/Octokit.Reactive/Octokit.Reactive-Mono.csproj b/Octokit.Reactive/Octokit.Reactive-Mono.csproj index 0964b1ffa7..c96ce61bfb 100644 --- a/Octokit.Reactive/Octokit.Reactive-Mono.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Mono.csproj @@ -192,6 +192,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj index 6a5e5193d4..2b555cc715 100644 --- a/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj +++ b/Octokit.Reactive/Octokit.Reactive-MonoAndroid.csproj @@ -208,6 +208,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj index dcef4ea5f3..0431a56579 100644 --- a/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj +++ b/Octokit.Reactive/Octokit.Reactive-Monotouch.csproj @@ -204,6 +204,8 @@ + + diff --git a/Octokit.Reactive/Octokit.Reactive.csproj b/Octokit.Reactive/Octokit.Reactive.csproj index cf8b2786ac..ae1cdaf97e 100644 --- a/Octokit.Reactive/Octokit.Reactive.csproj +++ b/Octokit.Reactive/Octokit.Reactive.csproj @@ -93,6 +93,7 @@ + @@ -109,6 +110,7 @@ + diff --git a/Octokit/Clients/IIssuesClient.cs b/Octokit/Clients/IIssuesClient.cs index 5dbfb1d948..81f8843b09 100644 --- a/Octokit/Clients/IIssuesClient.cs +++ b/Octokit/Clients/IIssuesClient.cs @@ -39,6 +39,8 @@ public interface IIssuesClient /// IIssueCommentsClient Comment { get; } + IIssueTimelineClient Timeline { get; } + /// /// Gets a single Issue by number. /// diff --git a/Octokit/Clients/IssuesClient.cs b/Octokit/Clients/IssuesClient.cs index 7387527892..ce41b8151b 100644 --- a/Octokit/Clients/IssuesClient.cs +++ b/Octokit/Clients/IssuesClient.cs @@ -22,6 +22,7 @@ public IssuesClient(IApiConnection apiConnection) : base(apiConnection) Labels = new IssuesLabelsClient(apiConnection); Milestone = new MilestonesClient(apiConnection); Comment = new IssueCommentsClient(apiConnection); + Timeline = new IssueTimelineClient(apiConnection); } /// @@ -51,6 +52,8 @@ public IssuesClient(IApiConnection apiConnection) : base(apiConnection) /// public IIssueCommentsClient Comment { get; private set; } + public IIssueTimelineClient Timeline { get; private set; } + /// /// Gets a single Issue by number. /// From d15f8f8e14ab03c7b43fdd31e2c50fc0f6484563 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Thu, 4 Aug 2016 00:11:49 +1000 Subject: [PATCH 05/22] Add that missing property thing --- Octokit.Reactive/Clients/IObservableIssuesClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Octokit.Reactive/Clients/IObservableIssuesClient.cs b/Octokit.Reactive/Clients/IObservableIssuesClient.cs index 9637d9b68e..07d9b12f34 100644 --- a/Octokit.Reactive/Clients/IObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssuesClient.cs @@ -39,6 +39,8 @@ public interface IObservableIssuesClient /// IObservableIssueCommentsClient Comment { get; } + IObservableIssueTimelineClient Timeline { get; } + /// /// Gets a single Issue by number. /// From 8c17e187c1ffb74297a2046c2ec6fbb5ef7343f8 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Thu, 4 Aug 2016 13:31:23 +1000 Subject: [PATCH 06/22] Add teh comments --- .../Clients/IObservableIssueTimelineClient.cs | 15 +++++++++++++++ .../Clients/IObservableIssuesClient.cs | 3 +++ .../Clients/ObservableIssueTimelineClient.cs | 15 +++++++++++++++ .../Clients/ObservableIssuesClient.cs | 3 +++ Octokit/Clients/IIssueTimelineClient.cs | 15 +++++++++++++++ Octokit/Clients/IssueTimelineClient.cs | 15 +++++++++++++++ Octokit/Clients/IssuesClient.cs | 3 +++ 7 files changed, 69 insertions(+) diff --git a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs index 79b8ba573f..384875f2cc 100644 --- a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs @@ -2,8 +2,23 @@ namespace Octokit.Reactive { + /// + /// A client for GitHub's Issue Timeline API. + /// + /// + /// See the Issue Timeline API documentation for more information. + /// public interface IObservableIssueTimelineClient { + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number IObservable GetAllForIssue(string owner, string repo, int number); } } diff --git a/Octokit.Reactive/Clients/IObservableIssuesClient.cs b/Octokit.Reactive/Clients/IObservableIssuesClient.cs index 07d9b12f34..0e9bd4478d 100644 --- a/Octokit.Reactive/Clients/IObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssuesClient.cs @@ -39,6 +39,9 @@ public interface IObservableIssuesClient /// IObservableIssueCommentsClient Comment { get; } + /// + /// Client for reading the timeline of events for an issue + /// IObservableIssueTimelineClient Timeline { get; } /// diff --git a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs index bbc5593ca3..71fddfe4ac 100644 --- a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs @@ -3,6 +3,12 @@ namespace Octokit.Reactive { + /// + /// A client for GitHub's Issue Timeline API. + /// + /// + /// See the Issue Timeline API documentation for more information. + /// public class ObservableIssueTimelineClient : IObservableIssueTimelineClient { readonly IConnection _connection; @@ -14,6 +20,15 @@ public ObservableIssueTimelineClient(IGitHubClient client) _connection = client.Connection; } + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number public IObservable GetAllForIssue(string owner, string repo, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); diff --git a/Octokit.Reactive/Clients/ObservableIssuesClient.cs b/Octokit.Reactive/Clients/ObservableIssuesClient.cs index fb0e240bb1..4e715ad53c 100644 --- a/Octokit.Reactive/Clients/ObservableIssuesClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssuesClient.cs @@ -43,6 +43,9 @@ public class ObservableIssuesClient : IObservableIssuesClient /// public IObservableMilestonesClient Milestone { get; private set; } + /// + /// Client for reading the timeline of events for an issue + /// public IObservableIssueTimelineClient Timeline { get; private set; } public ObservableIssuesClient(IGitHubClient client) diff --git a/Octokit/Clients/IIssueTimelineClient.cs b/Octokit/Clients/IIssueTimelineClient.cs index 31c001aa6f..f4d3721fe4 100644 --- a/Octokit/Clients/IIssueTimelineClient.cs +++ b/Octokit/Clients/IIssueTimelineClient.cs @@ -5,8 +5,23 @@ namespace Octokit { + /// + /// A client for GitHub's Issue Timeline API. + /// + /// + /// See the Issue Timeline API documentation for more information. + /// public interface IIssueTimelineClient { + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number Task> GetAllForIssue(string owner, string repo, int number); } } diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs index 5e63f434e6..283bcda1c1 100644 --- a/Octokit/Clients/IssueTimelineClient.cs +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -5,12 +5,27 @@ namespace Octokit { + /// + /// A client for GitHub's Issue Timeline API. + /// + /// + /// See the Issue Timeline API documentation for more information. + /// public class IssueTimelineClient: ApiClient, IIssueTimelineClient { public IssueTimelineClient(IApiConnection apiConnection) : base(apiConnection) { } + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number public Task> GetAllForIssue(string owner, string repo, int number) { Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); diff --git a/Octokit/Clients/IssuesClient.cs b/Octokit/Clients/IssuesClient.cs index ce41b8151b..3542da4e4b 100644 --- a/Octokit/Clients/IssuesClient.cs +++ b/Octokit/Clients/IssuesClient.cs @@ -52,6 +52,9 @@ public IssuesClient(IApiConnection apiConnection) : base(apiConnection) /// public IIssueCommentsClient Comment { get; private set; } + /// + /// Client for reading the timeline of events for an issue + /// public IIssueTimelineClient Timeline { get; private set; } /// From 6a67f2a328538e46efed1ec77c2a27f4d92310d8 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Thu, 4 Aug 2016 15:42:31 +1000 Subject: [PATCH 07/22] Added unit tests --- .../Clients/IssueTimelineClientTests.cs | 53 +++++++++++++++ Octokit.Tests/Octokit.Tests.csproj | 2 + .../ObservableIssueTimelineClientTests.cs | 65 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 Octokit.Tests/Clients/IssueTimelineClientTests.cs create mode 100644 Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs diff --git a/Octokit.Tests/Clients/IssueTimelineClientTests.cs b/Octokit.Tests/Clients/IssueTimelineClientTests.cs new file mode 100644 index 0000000000..dadcadc1ab --- /dev/null +++ b/Octokit.Tests/Clients/IssueTimelineClientTests.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NSubstitute; +using Xunit; + +namespace Octokit.Tests.Clients +{ + public class IssueTimelineClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArgument() + { + Assert.Throws( + () => new IssueTimelineClient(null)); + } + } + + public class TheGetAllForIssueMethod + { + [Fact] + public async Task RequestsTheCorrectUrl() + { + var connection = Substitute.For(); + var client = new IssueTimelineClient(connection); + + await client.GetAllForIssue("fake", "repo", 42); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), + Args.ApiOptions + ); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new IssueTimelineClient(Substitute.For()); + + await Assert.ThrowsAsync(() => client.GetAllForIssue(null, "repo", 42)); + await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", null, 42)); + + await Assert.ThrowsAsync(() => client.GetAllForIssue("", "repo", 42)); + await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", "", 42)); + + } + } + } +} diff --git a/Octokit.Tests/Octokit.Tests.csproj b/Octokit.Tests/Octokit.Tests.csproj index a05b3f8092..673961abfb 100644 --- a/Octokit.Tests/Octokit.Tests.csproj +++ b/Octokit.Tests/Octokit.Tests.csproj @@ -88,6 +88,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs new file mode 100644 index 0000000000..934b339b41 --- /dev/null +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; +using NSubstitute; +using Octokit.Internal; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Reactive +{ + public class ObservableIssueTimelineClientTests + { + public class TheCtor + { + [Fact] + public void EnsuresNonNullArgument() + { + Assert.Throws( + () => new ObservableIssueTimelineClient(null)); + } + } + + public class TheGetAllForIssueMethod + { + [Fact] + public async Task RequestsCorrectUrl() + { + var result = new List { new TimelineEventInfo() }; + + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableIssueTimelineClient(gitHubClient); + + IApiResponse> response = new ApiResponse>( + new Response + { + ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), + }, result); + gitHubClient.Connection.Get>(Args.Uri, Args.EmptyDictionary, null) + .Returns(Task.FromResult(response)); + + var timelineEvents = await client.GetAllForIssue("fake", "repo", 42).ToList(); + + connection.Received().Get>(Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), Args.EmptyDictionary, null); + Assert.Equal(1, timelineEvents.Count); + } + + [Fact] + public async Task EnsuresNonNullArguments() + { + var client = new ObservableIssueTimelineClient(Substitute.For()); + + Assert.Throws(() => client.GetAllForIssue(null, "repo", 42)); + Assert.Throws(() => client.GetAllForIssue("owner", null, 42)); + + Assert.Throws(() => client.GetAllForIssue("", "repo", 42)); + Assert.Throws(() => client.GetAllForIssue("owner", "", 42)); + + } + } + } +} From 169ed1a4b9730486854cf60ff1b8805fc28f11f0 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Thu, 4 Aug 2016 17:35:06 +1000 Subject: [PATCH 08/22] Fix method names --- Octokit.Tests/Clients/IssueTimelineClientTests.cs | 2 +- Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Octokit.Tests/Clients/IssueTimelineClientTests.cs b/Octokit.Tests/Clients/IssueTimelineClientTests.cs index dadcadc1ab..36cefb3514 100644 --- a/Octokit.Tests/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests/Clients/IssueTimelineClientTests.cs @@ -13,7 +13,7 @@ public class IssueTimelineClientTests public class TheCtor { [Fact] - public void EnsuresNonNullArgument() + public void EnsuresNonNullArguments() { Assert.Throws( () => new IssueTimelineClient(null)); diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs index 934b339b41..40d3e6dbbc 100644 --- a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -16,7 +16,7 @@ public class ObservableIssueTimelineClientTests public class TheCtor { [Fact] - public void EnsuresNonNullArgument() + public void EnsuresNonNullArguments() { Assert.Throws( () => new ObservableIssueTimelineClient(null)); From c5587e28229f9b985ac583c217191ccb09b50b39 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sat, 6 Aug 2016 16:01:37 +1000 Subject: [PATCH 09/22] Add missing event type enum and API preview accept header --- Octokit/Clients/IssueTimelineClient.cs | 2 +- Octokit/Helpers/AcceptHeaders.cs | 2 ++ Octokit/Models/Response/EventInfo.cs | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs index 283bcda1c1..652702ff1a 100644 --- a/Octokit/Clients/IssueTimelineClient.cs +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -31,7 +31,7 @@ public Task> GetAllForIssue(string owner, strin Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); - return ApiConnection.GetAll(ApiUrls.IssueTimeline(owner, repo, number), ApiOptions.None); + return ApiConnection.GetAll(ApiUrls.IssueTimeline(owner, repo, number), null, AcceptHeaders.IssueTimelineApiPreview); } } } diff --git a/Octokit/Helpers/AcceptHeaders.cs b/Octokit/Helpers/AcceptHeaders.cs index 6082aa3f21..f85ebc322f 100644 --- a/Octokit/Helpers/AcceptHeaders.cs +++ b/Octokit/Helpers/AcceptHeaders.cs @@ -38,5 +38,7 @@ public static class AcceptHeaders public const string InvitationsApiPreview = "application/vnd.github.swamp-thing-preview+json"; public const string PagesApiPreview = "application/vnd.github.mister-fantastic-preview+json"; + + public const string IssueTimelineApiPreview = "application/vnd.github.mockingbird-preview"; } } diff --git a/Octokit/Models/Response/EventInfo.cs b/Octokit/Models/Response/EventInfo.cs index a659994104..2ab2979467 100644 --- a/Octokit/Models/Response/EventInfo.cs +++ b/Octokit/Models/Response/EventInfo.cs @@ -165,6 +165,9 @@ public enum EventInfoState /// /// The actor unsubscribed from notifications for an issue. /// - Unsubscribed + Unsubscribed, + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crossreferenced")] + Crossreferenced } } \ No newline at end of file From fa739a53400370a13819c694dfa5c6d07831434b Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sat, 6 Aug 2016 16:02:43 +1000 Subject: [PATCH 10/22] Add integration tests for async client --- .../Clients/IssueTimelineClientTests.cs | 78 +++++++++++++++++++ .../Octokit.Tests.Integration.csproj | 1 + 2 files changed, 79 insertions(+) create mode 100644 Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs diff --git a/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs b/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs new file mode 100644 index 0000000000..8cc25d5dcf --- /dev/null +++ b/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs @@ -0,0 +1,78 @@ +using System; +using System.Threading.Tasks; +using Octokit.Tests.Integration.Helpers; +using Xunit; + +namespace Octokit.Tests.Integration.Clients +{ + public class IssueTimelineClientTests :IDisposable + { + private readonly IIssueTimelineClient _issueTimelineClient; + private readonly IIssuesClient _issuesClient; + private readonly RepositoryContext _context; + + public IssueTimelineClientTests() + { + var github = Helper.GetAuthenticatedClient(); + + _issueTimelineClient = github.Issue.Timeline; + _issuesClient = github.Issue; + + var repoName = Helper.MakeNameWithTimestamp("public-repo"); + + _context = github.CreateRepositoryContext(new NewRepository(repoName)).Result; + } + + [IntegrationTest] + public async Task CanRetrieveTimelineForIssue() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue); + + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + Assert.Empty(timelineEventInfos); + + var closed = await _issuesClient.Update(_context.RepositoryOwner, _context.RepositoryName, issue.Number, new IssueUpdate() { State = ItemState.Closed }); + Assert.NotNull(closed); + + timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(EventInfoState.Closed, timelineEventInfos[0].Event); + } + + [IntegrationTest] + public async Task CanDeserializeRenameEvent() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue); + + var renamed = await _issuesClient.Update(_context.Repository.Id, issue.Number, new IssueUpdate { Title = "A test issue" }); + Assert.NotNull(renamed); + Assert.Equal("A test issue", renamed.Title); + + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal("a test issue", timelineEventInfos[0].Rename.From); + Assert.Equal("A test issue", timelineEventInfos[0].Rename.To); + } + + [IntegrationTest] + public async Task CanDeserializeCrossReferenceEvent() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.RepositoryOwner, _context.RepositoryName, newIssue); + + newIssue = new NewIssue("another test issue") { Body = "Another new unassigned issue referencing the first new issue in #" + issue.Number }; + var anotherNewIssue = await _issuesClient.Create(_context.Repository.Id, newIssue); + + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(anotherNewIssue.Id, timelineEventInfos[0].Source.Id); + } + + public void Dispose() + { + _context.Dispose(); + } + } +} diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 4681be2473..c903d9fa3f 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -87,6 +87,7 @@ + From 3d99b2ee5b04a47db1a1ede5a02e3c306c7f139e Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sat, 6 Aug 2016 16:29:13 +1000 Subject: [PATCH 11/22] Pass in API preview header --- Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs index 71fddfe4ac..4389b7c35d 100644 --- a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs @@ -34,7 +34,7 @@ public IObservable GetAllForIssue(string owner, string repo, Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); - return _connection.GetAndFlattenAllPages(ApiUrls.IssueTimeline(owner, repo, number), ApiOptions.None); + return _connection.GetAndFlattenAllPages(ApiUrls.IssueTimeline(owner, repo, number), null, AcceptHeaders.IssueTimelineApiPreview); } } } From ba3b6851410077a4beb19183b76f3a7c86993f35 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sat, 6 Aug 2016 16:29:47 +1000 Subject: [PATCH 12/22] Add observable integration tests --- .../Octokit.Tests.Integration.csproj | 1 + .../ObservableIssueTimelineClientTests.cs | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index c903d9fa3f..62a1cd4090 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -154,6 +154,7 @@ + diff --git a/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs new file mode 100644 index 0000000000..1d9f1145d1 --- /dev/null +++ b/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs @@ -0,0 +1,81 @@ +using Octokit.Tests.Integration.Helpers; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Octokit.Reactive; +using Xunit; + +namespace Octokit.Tests.Integration.Reactive +{ + public class ObservableIssueTimelineClientTests + { + private readonly RepositoryContext _context; + private readonly ObservableGitHubClient _client; + + public ObservableIssueTimelineClientTests() + { + var github = Helper.GetAuthenticatedClient(); + + _client = new ObservableGitHubClient(github); + + var reponame = Helper.MakeNameWithTimestamp("public-repo"); + _context = github.CreateRepositoryContext(new NewRepository(reponame)).Result; + } + + [IntegrationTest] + public async Task CanRetrieveTimelineForIssue() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var issue = await observable; + + var observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.Empty(timelineEventInfos); + + observable = _client.Issue.Update(_context.Repository.Id, issue.Number, new IssueUpdate { State = ItemState.Closed }); + var closed = await observable; + Assert.NotNull(closed); + + observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + timelineEventInfos = await observableTimeline.ToList(); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(EventInfoState.Closed, timelineEventInfos[0].Event); + } + + [IntegrationTest] + public async Task CanDeserializeRenameEvent() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var issue = await observable; + + observable = _client.Issue.Update(_context.Repository.Id, issue.Number, new IssueUpdate { Title = "A test issue" }); + var renamed = await observable; + Assert.NotNull(renamed); + Assert.Equal("A test issue", renamed.Title); + + var observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal("a test issue", timelineEventInfos[0].Rename.From); + Assert.Equal("A test issue", timelineEventInfos[0].Rename.To); + } + + [IntegrationTest] + public async Task CanDeserializeCrossReferenceEvent() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var issue = await observable; + + newIssue = new NewIssue("another test issue") { Body = "Another new unassigned issue referencing the first new issue in #" + issue.Number }; + observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var anotherNewIssue = await observable; + + var observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.RepositoryOwner, _context.RepositoryName, issue.Number); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(anotherNewIssue.Id, timelineEventInfos[0].Source.Id); + } + } +} From c236e7e6796b595f883ed23bcb7ceb809c4723ab Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sat, 6 Aug 2016 17:10:19 +1000 Subject: [PATCH 13/22] Unbreak the broken tests... --- Octokit.Tests/Clients/IssueTimelineClientTests.cs | 3 ++- .../Reactive/ObservableIssueTimelineClientTests.cs | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Octokit.Tests/Clients/IssueTimelineClientTests.cs b/Octokit.Tests/Clients/IssueTimelineClientTests.cs index 36cefb3514..0504044516 100644 --- a/Octokit.Tests/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests/Clients/IssueTimelineClientTests.cs @@ -32,7 +32,8 @@ public async Task RequestsTheCorrectUrl() connection.Received().GetAll( Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), - Args.ApiOptions + Arg.Any>(), + "application/vnd.github.mockingbird-preview" ); } diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs index 40d3e6dbbc..ab3e7be097 100644 --- a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -39,12 +39,15 @@ public async Task RequestsCorrectUrl() { ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), }, result); - gitHubClient.Connection.Get>(Args.Uri, Args.EmptyDictionary, null) + gitHubClient.Connection.Get>(Args.Uri, null, "application/vnd.github.mockingbird-preview") .Returns(Task.FromResult(response)); var timelineEvents = await client.GetAllForIssue("fake", "repo", 42).ToList(); - connection.Received().Get>(Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), Args.EmptyDictionary, null); + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), + null, + "application/vnd.github.mockingbird-preview"); Assert.Equal(1, timelineEvents.Count); } From 9cedeec9cdd4851f7ccb009ab352b97fe4142130 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sat, 6 Aug 2016 17:44:18 +1000 Subject: [PATCH 14/22] Remove unnecessary usings --- Octokit.Tests/Clients/IssueTimelineClientTests.cs | 2 -- Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/Octokit.Tests/Clients/IssueTimelineClientTests.cs b/Octokit.Tests/Clients/IssueTimelineClientTests.cs index 0504044516..5177b4b9a2 100644 --- a/Octokit.Tests/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests/Clients/IssueTimelineClientTests.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using NSubstitute; using Xunit; diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs index ab3e7be097..47b1befec5 100644 --- a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reactive.Linq; -using System.Text; using System.Threading.Tasks; using NSubstitute; using Octokit.Internal; From c4e3c6b6eb53f293f7e52c9697b8b3522be55943 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 09:10:15 +1000 Subject: [PATCH 15/22] Add missing events --- Octokit/Models/Response/EventInfo.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Octokit/Models/Response/EventInfo.cs b/Octokit/Models/Response/EventInfo.cs index 2ab2979467..cd4529570e 100644 --- a/Octokit/Models/Response/EventInfo.cs +++ b/Octokit/Models/Response/EventInfo.cs @@ -167,6 +167,22 @@ public enum EventInfoState /// Unsubscribed, + /// + /// A comment was added to the issue. + /// + Commented, + + /// + /// A commit was added to the pull request's HEAD branch. + /// Only provided for pull requests. + /// + Committed, + + /// + /// The issue was referenced from another issue. + /// The source attribute contains the id, actor, and + /// url of the reference's source. + /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Crossreferenced")] Crossreferenced } From ec56ecfe55aad541dd46ae3b0acefcfdafd81c46 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 09:11:31 +1000 Subject: [PATCH 16/22] Fix API URLs --- Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs | 2 +- Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs | 2 +- Octokit/Clients/IIssueTimelineClient.cs | 2 +- Octokit/Clients/IssueTimelineClient.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs index 384875f2cc..89631b7a58 100644 --- a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs @@ -6,7 +6,7 @@ namespace Octokit.Reactive /// A client for GitHub's Issue Timeline API. /// /// - /// See the Issue Timeline API documentation for more information. + /// See the Issue Timeline API documentation for more information. /// public interface IObservableIssueTimelineClient { diff --git a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs index 4389b7c35d..942a1e5109 100644 --- a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs @@ -7,7 +7,7 @@ namespace Octokit.Reactive /// A client for GitHub's Issue Timeline API. /// /// - /// See the Issue Timeline API documentation for more information. + /// See the Issue Timeline API documentation for more information. /// public class ObservableIssueTimelineClient : IObservableIssueTimelineClient { diff --git a/Octokit/Clients/IIssueTimelineClient.cs b/Octokit/Clients/IIssueTimelineClient.cs index f4d3721fe4..4c2da389b6 100644 --- a/Octokit/Clients/IIssueTimelineClient.cs +++ b/Octokit/Clients/IIssueTimelineClient.cs @@ -9,7 +9,7 @@ namespace Octokit /// A client for GitHub's Issue Timeline API. /// /// - /// See the Issue Timeline API documentation for more information. + /// See the Issue Timeline API documentation for more information. /// public interface IIssueTimelineClient { diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs index 652702ff1a..487dddd7c0 100644 --- a/Octokit/Clients/IssueTimelineClient.cs +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -9,7 +9,7 @@ namespace Octokit /// A client for GitHub's Issue Timeline API. /// /// - /// See the Issue Timeline API documentation for more information. + /// See the Issue Timeline API documentation for more information. /// public class IssueTimelineClient: ApiClient, IIssueTimelineClient { From f3fc3d0964e7b63e4dac85861ae5ce34d177d0d8 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 09:13:58 +1000 Subject: [PATCH 17/22] Add overloads for using repository Id instead of owner/repo and paging --- .../Clients/IObservableIssueTimelineClient.cs | 33 ++++++++++++ .../Clients/ObservableIssueTimelineClient.cs | 50 ++++++++++++++++++- Octokit/Clients/IIssueTimelineClient.cs | 33 ++++++++++++ Octokit/Clients/IssueTimelineClient.cs | 48 +++++++++++++++++- Octokit/Helpers/ApiUrls.cs | 15 ++++-- 5 files changed, 172 insertions(+), 7 deletions(-) diff --git a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs index 89631b7a58..251fa7c851 100644 --- a/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/IObservableIssueTimelineClient.cs @@ -20,5 +20,38 @@ public interface IObservableIssueTimelineClient /// The name of the repository /// The issue number IObservable GetAllForIssue(string owner, string repo, int number); + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number + /// Options for changing the API response + IObservable GetAllForIssue(string owner, string repo, int number, ApiOptions options); + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + IObservable GetAllForIssue(int repositoryId, int number); + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + /// Options for changing the API response + IObservable GetAllForIssue(int repositoryId, int number, ApiOptions options); } } diff --git a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs index 942a1e5109..9e53b48678 100644 --- a/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs +++ b/Octokit.Reactive/Clients/ObservableIssueTimelineClient.cs @@ -34,7 +34,55 @@ public IObservable GetAllForIssue(string owner, string repo, Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); - return _connection.GetAndFlattenAllPages(ApiUrls.IssueTimeline(owner, repo, number), null, AcceptHeaders.IssueTimelineApiPreview); + return GetAllForIssue(owner, repo, number, ApiOptions.None); + } + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number + /// Options for changing the API response + public IObservable GetAllForIssue(string owner, string repo, int number, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.IssueTimeline(owner, repo, number), null, AcceptHeaders.IssueTimelineApiPreview, options); + } + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + public IObservable GetAllForIssue(int repositoryId, int number) + { + return GetAllForIssue(repositoryId, number, ApiOptions.None); + } + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + /// Options for changing the API response + public IObservable GetAllForIssue(int repositoryId, int number, ApiOptions options) + { + Ensure.ArgumentNotNull(options, "options"); + + return _connection.GetAndFlattenAllPages(ApiUrls.IssueTimeline(repositoryId, number), null, AcceptHeaders.IssueTimelineApiPreview, options); } } } diff --git a/Octokit/Clients/IIssueTimelineClient.cs b/Octokit/Clients/IIssueTimelineClient.cs index 4c2da389b6..e6321ea638 100644 --- a/Octokit/Clients/IIssueTimelineClient.cs +++ b/Octokit/Clients/IIssueTimelineClient.cs @@ -23,5 +23,38 @@ public interface IIssueTimelineClient /// The name of the repository /// The issue number Task> GetAllForIssue(string owner, string repo, int number); + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number + /// Options for changing the API repsonse + Task> GetAllForIssue(string owner, string repo, int number, ApiOptions options); + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + Task> GetAllForIssue(int repositoryId, int number); + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + /// Options for changing the API response + Task> GetAllForIssue(int repositoryId, int number, ApiOptions options); } } diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs index 487dddd7c0..8d7fdbd46a 100644 --- a/Octokit/Clients/IssueTimelineClient.cs +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -31,7 +31,53 @@ public Task> GetAllForIssue(string owner, strin Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); - return ApiConnection.GetAll(ApiUrls.IssueTimeline(owner, repo, number), null, AcceptHeaders.IssueTimelineApiPreview); + return GetAllForIssue(owner, repo, number, ApiOptions.None); + } + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The owner of the repository + /// The name of the repository + /// The issue number + /// Options for changing the API repsonse + public Task> GetAllForIssue(string owner, string repo, int number, ApiOptions options) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(repo, "repo"); + Ensure.ArgumentNotNull(options, "options"); + + return ApiConnection.GetAll(ApiUrls.IssueTimeline(owner, repo, number), null, AcceptHeaders.IssueTimelineApiPreview, options); + } + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + public Task> GetAllForIssue(int repositoryId, int number) + { + return GetAllForIssue(repositoryId, number, ApiOptions.None); + } + + /// + /// Gets all the various events that have occurred around an issue or pull request. + /// + /// + /// https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue + /// + /// The Id of the repository + /// The issue number + /// Options for changing the API response + public Task> GetAllForIssue(int repositoryId, int number, ApiOptions options) + { + return ApiConnection.GetAll(ApiUrls.IssueTimeline(repositoryId, number), null, AcceptHeaders.IssueTimelineApiPreview, options); } } } diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 3f9eea84cb..62c6304492 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -330,11 +330,6 @@ public static Uri IssueReactions(string owner, string name, int number) return "repos/{0}/{1}/issues/{2}/reactions".FormatUri(owner, name, number); } - public static Uri IssueTimeline(string owner, string repo, int number) - { - return "repos/{0}/{1}/issues/{2}/timeline".FormatUri(owner, repo, number); - } - /// /// Returns the for the reaction of a specified issue. /// @@ -346,6 +341,16 @@ public static Uri IssueReactions(int repositoryId, int number) return "repositories/{0}/issues/{1}/reactions".FormatUri(repositoryId, number); } + public static Uri IssueTimeline(string owner, string repo, int number) + { + return "repos/{0}/{1}/issues/{2}/timeline".FormatUri(owner, repo, number); + } + + public static Uri IssueTimeline(int repositoryId, int number) + { + return "repositories/{0}/issues/{1}/timeline".FormatUri(repositoryId, number); + } + /// /// Returns the for the comments for all issues in a specific repo. /// From 594588d9bac2575d0bacb91b27fe59c7eb6fcb11 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 09:14:26 +1000 Subject: [PATCH 18/22] Add tests for repository id overloads --- .../Clients/IssueTimelineClientTests.cs | 47 +++++++++++++++ .../ObservableIssueTimelineClientTests.cs | 57 +++++++++++++++++++ .../Clients/IssueTimelineClientTests.cs | 16 +++++- .../ObservableIssueTimelineClientTests.cs | 27 ++++++++- 4 files changed, 145 insertions(+), 2 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs b/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs index 8cc25d5dcf..1ecb4da9fb 100644 --- a/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs @@ -70,6 +70,53 @@ public async Task CanDeserializeCrossReferenceEvent() Assert.Equal(anotherNewIssue.Id, timelineEventInfos[0].Source.Id); } + [IntegrationTest] + public async Task CanRetrieveTimelineForIssueByRepositoryId() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.Repository.Id, newIssue); + + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.Repository.Id, issue.Number); + Assert.Empty(timelineEventInfos); + + var closed = await _issuesClient.Update(_context.Repository.Id, issue.Number, new IssueUpdate() { State = ItemState.Closed }); + Assert.NotNull(closed); + + timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.Repository.Id, issue.Number); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(EventInfoState.Closed, timelineEventInfos[0].Event); + } + + [IntegrationTest] + public async Task CanDeserializeRenameEventByRepositoryId() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.Repository.Id, newIssue); + + var renamed = await _issuesClient.Update(_context.Repository.Id, issue.Number, new IssueUpdate { Title = "A test issue" }); + Assert.NotNull(renamed); + Assert.Equal("A test issue", renamed.Title); + + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.Repository.Id, issue.Number); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal("a test issue", timelineEventInfos[0].Rename.From); + Assert.Equal("A test issue", timelineEventInfos[0].Rename.To); + } + + [IntegrationTest] + public async Task CanDeserializeCrossReferenceEventByRepositoryId() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var issue = await _issuesClient.Create(_context.Repository.Id, newIssue); + + newIssue = new NewIssue("another test issue") { Body = "Another new unassigned issue referencing the first new issue in #" + issue.Number }; + var anotherNewIssue = await _issuesClient.Create(_context.Repository.Id, newIssue); + + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue(_context.Repository.Id, issue.Number); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(anotherNewIssue.Id, timelineEventInfos[0].Source.Id); + } + public void Dispose() { _context.Dispose(); diff --git a/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs index 1d9f1145d1..f3742e88ea 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs @@ -77,5 +77,62 @@ public async Task CanDeserializeCrossReferenceEvent() Assert.Equal(1, timelineEventInfos.Count); Assert.Equal(anotherNewIssue.Id, timelineEventInfos[0].Source.Id); } + + [IntegrationTest] + public async Task CanRetrieveTimelineForIssueByRepositoryId() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var issue = await observable; + + var observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.Repository.Id, issue.Number); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.Empty(timelineEventInfos); + + observable = _client.Issue.Update(_context.Repository.Id, issue.Number, new IssueUpdate { State = ItemState.Closed }); + var closed = await observable; + Assert.NotNull(closed); + + observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.Repository.Id, issue.Number); + timelineEventInfos = await observableTimeline.ToList(); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(EventInfoState.Closed, timelineEventInfos[0].Event); + } + + [IntegrationTest] + public async Task CanDeserializeRenameEventByRepositoryId() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var issue = await observable; + + observable = _client.Issue.Update(_context.Repository.Id, issue.Number, new IssueUpdate { Title = "A test issue" }); + var renamed = await observable; + Assert.NotNull(renamed); + Assert.Equal("A test issue", renamed.Title); + + var observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.Repository.Id, issue.Number); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal("a test issue", timelineEventInfos[0].Rename.From); + Assert.Equal("A test issue", timelineEventInfos[0].Rename.To); + } + + [IntegrationTest] + public async Task CanDeserializeCrossReferenceEventByRepositoryId() + { + var newIssue = new NewIssue("a test issue") { Body = "A new unassigned issue" }; + var observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var issue = await observable; + + newIssue = new NewIssue("another test issue") { Body = "Another new unassigned issue referencing the first new issue in #" + issue.Number }; + observable = _client.Issue.Create(_context.Repository.Id, newIssue); + var anotherNewIssue = await observable; + + var observableTimeline = _client.Issue.Timeline.GetAllForIssue(_context.Repository.Id, issue.Number); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.Equal(1, timelineEventInfos.Count); + Assert.Equal(anotherNewIssue.Id, timelineEventInfos[0].Source.Id); + } } } diff --git a/Octokit.Tests/Clients/IssueTimelineClientTests.cs b/Octokit.Tests/Clients/IssueTimelineClientTests.cs index 5177b4b9a2..dd572352fd 100644 --- a/Octokit.Tests/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests/Clients/IssueTimelineClientTests.cs @@ -35,6 +35,20 @@ public async Task RequestsTheCorrectUrl() ); } + [Fact] + public async Task RequestsTheCorrectUrlWithRepositoryId() + { + var connection = Substitute.For(); + var client = new IssueTimelineClient(connection); + + await client.GetAllForIssue(1, 42); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/1/issues/42/timeline"), + Arg.Any>(), + "application/vnd.github.mockingbird-preview"); + } + [Fact] public async Task EnsuresNonNullArguments() { @@ -42,7 +56,7 @@ public async Task EnsuresNonNullArguments() await Assert.ThrowsAsync(() => client.GetAllForIssue(null, "repo", 42)); await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", null, 42)); - + await Assert.ThrowsAsync(() => client.GetAllForIssue("", "repo", 42)); await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", "", 42)); diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs index 47b1befec5..7102c873eb 100644 --- a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -44,7 +44,32 @@ public async Task RequestsCorrectUrl() connection.Received().Get>( Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), - null, + Arg.Any>(), + "application/vnd.github.mockingbird-preview"); + Assert.Equal(1, timelineEvents.Count); + } + + [Fact] + public async Task RequestCorrectUrlWithRepositoryId() + { + var result = new List { new TimelineEventInfo() }; + var connection = Substitute.For(); + var githubClient = new GitHubClient(connection); + var client = new ObservableIssueTimelineClient(githubClient); + + IApiResponse> response = new ApiResponse>( + new Response + { + ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), + }, result); + githubClient.Connection.Get>(Args.Uri, null, "application/vnd.github.mockingbird-preview") + .Returns(Task.FromResult(response)); + + var timelineEvents = await client.GetAllForIssue(1, 42).ToList(); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repositories/1/issues/42/timeline"), + Arg.Any>(), "application/vnd.github.mockingbird-preview"); Assert.Equal(1, timelineEvents.Count); } From 9dcac8885307596651123ed5a85f698cddc03e33 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 10:39:04 +1000 Subject: [PATCH 19/22] Add paging tests --- .../Clients/IssueTimelineClientTests.cs | 19 +++++++ .../ObservableIssueTimelineClientTests.cs | 20 +++++++ .../Clients/IssueTimelineClientTests.cs | 41 ++++++++++++-- .../ObservableIssueTimelineClientTests.cs | 53 +++++++++++++++++++ 4 files changed, 129 insertions(+), 4 deletions(-) diff --git a/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs b/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs index 1ecb4da9fb..af18d72f67 100644 --- a/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests.Integration/Clients/IssueTimelineClientTests.cs @@ -40,6 +40,25 @@ public async Task CanRetrieveTimelineForIssue() Assert.Equal(EventInfoState.Closed, timelineEventInfos[0].Event); } + [IntegrationTest] + public async Task CanRetrieveTimelineForIssueWithApiOptions() + { + var timelineEventInfos = await _issueTimelineClient.GetAllForIssue("octokit", "octokit.net", 1115); + Assert.NotEmpty(timelineEventInfos); + Assert.NotEqual(1, timelineEventInfos.Count); + + var pageOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + + timelineEventInfos = await _issueTimelineClient.GetAllForIssue("octokit", "octokit.net", 1115, pageOptions); + Assert.NotEmpty(timelineEventInfos); + Assert.Equal(1, timelineEventInfos.Count); + } + [IntegrationTest] public async Task CanDeserializeRenameEvent() { diff --git a/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs index f3742e88ea..7cfb66a887 100644 --- a/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests.Integration/Reactive/ObservableIssueTimelineClientTests.cs @@ -42,6 +42,26 @@ public async Task CanRetrieveTimelineForIssue() Assert.Equal(EventInfoState.Closed, timelineEventInfos[0].Event); } + [IntegrationTest] + public async Task CanRetrieveTimelineForIssueWithApiOptions() + { + var observableTimeline = _client.Issue.Timeline.GetAllForIssue("octokit", "octokit.net", 1115); + var timelineEventInfos = await observableTimeline.ToList(); + Assert.NotEmpty(timelineEventInfos); + Assert.NotEqual(1, timelineEventInfos.Count); + + var pageOptions = new ApiOptions + { + PageSize = 1, + PageCount = 1, + StartPage = 1 + }; + observableTimeline = _client.Issue.Timeline.GetAllForIssue("octokit", "octokit.net", 1115, pageOptions); + timelineEventInfos = await observableTimeline.ToList(); + Assert.NotEmpty(timelineEventInfos); + Assert.Equal(1, timelineEventInfos.Count); + } + [IntegrationTest] public async Task CanDeserializeRenameEvent() { diff --git a/Octokit.Tests/Clients/IssueTimelineClientTests.cs b/Octokit.Tests/Clients/IssueTimelineClientTests.cs index dd572352fd..694cedd7f2 100644 --- a/Octokit.Tests/Clients/IssueTimelineClientTests.cs +++ b/Octokit.Tests/Clients/IssueTimelineClientTests.cs @@ -31,8 +31,23 @@ public async Task RequestsTheCorrectUrl() connection.Received().GetAll( Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), Arg.Any>(), - "application/vnd.github.mockingbird-preview" - ); + "application/vnd.github.mockingbird-preview", + Arg.Any()); + } + + [Fact] + public async Task RequestsTheCorrectUrlWithApiOptions() + { + var connection = Substitute.For(); + var client = new IssueTimelineClient(connection); + + await client.GetAllForIssue("fake", "repo", 42, new ApiOptions {PageSize = 30}); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), + Arg.Any>(), + "application/vnd.github.mockingbird-preview", + Arg.Is(ao => ao.PageSize == 30)); } [Fact] @@ -46,7 +61,23 @@ public async Task RequestsTheCorrectUrlWithRepositoryId() connection.Received().GetAll( Arg.Is(u => u.ToString() == "repositories/1/issues/42/timeline"), Arg.Any>(), - "application/vnd.github.mockingbird-preview"); + "application/vnd.github.mockingbird-preview", + Arg.Any()); + } + + [Fact] + public async Task RequestsTheCorrectUrlWithRepositoryIdAndApiOptions() + { + var connection = Substitute.For(); + var client = new IssueTimelineClient(connection); + + await client.GetAllForIssue(1, 42, new ApiOptions {PageSize = 30}); + + connection.Received().GetAll( + Arg.Is(u => u.ToString() == "repositories/1/issues/42/timeline"), + Arg.Any>(), + "application/vnd.github.mockingbird-preview", + Arg.Is(ao => ao.PageSize == 30)); } [Fact] @@ -56,7 +87,9 @@ public async Task EnsuresNonNullArguments() await Assert.ThrowsAsync(() => client.GetAllForIssue(null, "repo", 42)); await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", null, 42)); - + await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", "repo", 42, null)); + await Assert.ThrowsAsync(() => client.GetAllForIssue(1, 42, null)); + await Assert.ThrowsAsync(() => client.GetAllForIssue("", "repo", 42)); await Assert.ThrowsAsync(() => client.GetAllForIssue("owner", "", 42)); diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs index 7102c873eb..0435673950 100644 --- a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -49,6 +49,32 @@ public async Task RequestsCorrectUrl() Assert.Equal(1, timelineEvents.Count); } + [Fact] + public async Task RequestsCorrectUrlWithApiOptions() + { + var result = new List { new TimelineEventInfo() }; + + var connection = Substitute.For(); + var gitHubClient = new GitHubClient(connection); + var client = new ObservableIssueTimelineClient(gitHubClient); + + IApiResponse> response = new ApiResponse>( + new Response + { + ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), + }, result); + gitHubClient.Connection.Get>(Args.Uri, Arg.Is>(d => d.Count == 1), "application/vnd.github.mockingbird-preview") + .Returns(Task.FromResult(response)); + + var timelineEvents = await client.GetAllForIssue("fake", "repo", 42, new ApiOptions {PageSize = 30}).ToList(); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), + Arg.Is>(d => d.Count == 1), + "application/vnd.github.mockingbird-preview"); + Assert.Equal(1, timelineEvents.Count); + } + [Fact] public async Task RequestCorrectUrlWithRepositoryId() { @@ -74,6 +100,31 @@ public async Task RequestCorrectUrlWithRepositoryId() Assert.Equal(1, timelineEvents.Count); } + [Fact] + public async Task RequestCorrectUrlWithRepositoryIdAndApiOptions() + { + var result = new List { new TimelineEventInfo() }; + var connection = Substitute.For(); + var githubClient = new GitHubClient(connection); + var client = new ObservableIssueTimelineClient(githubClient); + + IApiResponse> response = new ApiResponse>( + new Response + { + ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), + }, result); + githubClient.Connection.Get>(Args.Uri, Arg.Is>(d => d.Count == 1), "application/vnd.github.mockingbird-preview") + .Returns(Task.FromResult(response)); + + var timelineEvents = await client.GetAllForIssue(1, 42, new ApiOptions {PageSize = 30}).ToList(); + + connection.Received().Get>( + Arg.Is(u => u.ToString() == "repositories/1/issues/42/timeline"), + Arg.Is>(d => d.Count == 1), + "application/vnd.github.mockingbird-preview"); + Assert.Equal(1, timelineEvents.Count); + } + [Fact] public async Task EnsuresNonNullArguments() { @@ -81,6 +132,8 @@ public async Task EnsuresNonNullArguments() Assert.Throws(() => client.GetAllForIssue(null, "repo", 42)); Assert.Throws(() => client.GetAllForIssue("owner", null, 42)); + Assert.Throws(() => client.GetAllForIssue("owner", "repo", 42, null)); + Assert.Throws(() => client.GetAllForIssue(1, 42, null)); Assert.Throws(() => client.GetAllForIssue("", "repo", 42)); Assert.Throws(() => client.GetAllForIssue("owner", "", 42)); From bc5f22669b17fb753109c11efad0e3e4d8dbc5e1 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 11:09:56 +1000 Subject: [PATCH 20/22] I'm clearly a bit rusty about this test thing here... --- .../Reactive/ObservableIssueTimelineClientTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs index 0435673950..776f84352b 100644 --- a/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs +++ b/Octokit.Tests/Reactive/ObservableIssueTimelineClientTests.cs @@ -37,7 +37,7 @@ public async Task RequestsCorrectUrl() { ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), }, result); - gitHubClient.Connection.Get>(Args.Uri, null, "application/vnd.github.mockingbird-preview") + gitHubClient.Connection.Get>(Args.Uri, Args.EmptyDictionary, "application/vnd.github.mockingbird-preview") .Returns(Task.FromResult(response)); var timelineEvents = await client.GetAllForIssue("fake", "repo", 42).ToList(); @@ -70,7 +70,7 @@ public async Task RequestsCorrectUrlWithApiOptions() connection.Received().Get>( Arg.Is(u => u.ToString() == "repos/fake/repo/issues/42/timeline"), - Arg.Is>(d => d.Count == 1), + Arg.Is>(d => d.Count == 1 && d["per_page"] == "30"), "application/vnd.github.mockingbird-preview"); Assert.Equal(1, timelineEvents.Count); } @@ -88,7 +88,7 @@ public async Task RequestCorrectUrlWithRepositoryId() { ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()), }, result); - githubClient.Connection.Get>(Args.Uri, null, "application/vnd.github.mockingbird-preview") + githubClient.Connection.Get>(Args.Uri, Args.EmptyDictionary, "application/vnd.github.mockingbird-preview") .Returns(Task.FromResult(response)); var timelineEvents = await client.GetAllForIssue(1, 42).ToList(); @@ -120,7 +120,7 @@ public async Task RequestCorrectUrlWithRepositoryIdAndApiOptions() connection.Received().Get>( Arg.Is(u => u.ToString() == "repositories/1/issues/42/timeline"), - Arg.Is>(d => d.Count == 1), + Arg.Is>(d => d.Count == 1 && d["per_page"] == "30"), "application/vnd.github.mockingbird-preview"); Assert.Equal(1, timelineEvents.Count); } From 74074e7d422d89adf03cd278d245870dabe167b2 Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Sun, 7 Aug 2016 12:20:56 +1000 Subject: [PATCH 21/22] Missed a check for null argument --- Octokit/Clients/IssueTimelineClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Octokit/Clients/IssueTimelineClient.cs b/Octokit/Clients/IssueTimelineClient.cs index 8d7fdbd46a..ec71d3f3d0 100644 --- a/Octokit/Clients/IssueTimelineClient.cs +++ b/Octokit/Clients/IssueTimelineClient.cs @@ -77,6 +77,8 @@ public Task> GetAllForIssue(int repositoryId, i /// Options for changing the API response public Task> GetAllForIssue(int repositoryId, int number, ApiOptions options) { + Ensure.ArgumentNotNull(options, "options"); + return ApiConnection.GetAll(ApiUrls.IssueTimeline(repositoryId, number), null, AcceptHeaders.IssueTimelineApiPreview, options); } } From c0a8638f3605e82f234082efb53f60985986307d Mon Sep 17 00:00:00 2001 From: Henrik Andersson Date: Mon, 8 Aug 2016 06:24:38 +1000 Subject: [PATCH 22/22] Added missing XMLDocs --- Octokit/Helpers/ApiUrls.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index 62c6304492..c4d964e849 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -341,11 +341,24 @@ public static Uri IssueReactions(int repositoryId, int number) return "repositories/{0}/issues/{1}/reactions".FormatUri(repositoryId, number); } + /// + /// Returns the for the timeline of a specified issue. + /// + /// The owner of the repository + /// The name of the repository + /// The issue number + /// public static Uri IssueTimeline(string owner, string repo, int number) { return "repos/{0}/{1}/issues/{2}/timeline".FormatUri(owner, repo, number); } + /// + /// Returns the for the timeline of a specified issue. + /// + /// The Id of the repository + /// The issue number + /// public static Uri IssueTimeline(int repositoryId, int number) { return "repositories/{0}/issues/{1}/timeline".FormatUri(repositoryId, number);