diff --git a/CustomDictionary.xml b/CustomDictionary.xml index a9c1edd1..8cd1d26f 100644 --- a/CustomDictionary.xml +++ b/CustomDictionary.xml @@ -21,6 +21,7 @@ Mergeable Symlink Submodule + Forkee diff --git a/Octokit.Tests.Integration/Clients/EventsClientTests.cs b/Octokit.Tests.Integration/Clients/EventsClientTests.cs index e503da2f..f1d57979 100644 --- a/Octokit.Tests.Integration/Clients/EventsClientTests.cs +++ b/Octokit.Tests.Integration/Clients/EventsClientTests.cs @@ -1,4 +1,7 @@ -using System.Threading.Tasks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Xunit; namespace Octokit.Tests.Integration.Clients @@ -7,13 +10,89 @@ namespace Octokit.Tests.Integration.Clients { public class TheGetUserPerformedMethod { - [Fact] + [IntegrationTest] public async Task ReturnsACollection() { var github = Helper.GetAuthenticatedClient(); var events = await github.Activity.Events.GetUserPerformed("shiftkey"); Assert.NotEmpty(events); - } + } + } + + public class EventPayloads + { + readonly IEnumerable _events; + public EventPayloads() + { + var github = Helper.GetAuthenticatedClient(); + _events = github.Activity.Events.GetUserPerformed("shiftkey").Result; + } + + [IntegrationTest] + public void AllEventsHavePayloads() + { + Assert.True(_events.All(e => e.Payload != null)); + } + + [IntegrationTest] + public void IssueCommentPayloadEventDeserializesCorrectly() + { + var commentEvent = _events.FirstOrDefault(e => e.Id == "2628548686"); + Assert.NotNull(commentEvent); + Assert.Equal("IssueCommentEvent", commentEvent.Type); + var commentPayload = commentEvent.Payload as IssueCommentPayload; + Assert.NotNull(commentPayload); + Assert.Equal("created", commentPayload.Action); + Assert.NotNull(commentPayload.Comment); + Assert.Equal("@joshvera just going to give this a once-over to ensure it matches up with our other conventions before merging", commentPayload.Comment.Body); + Assert.NotNull(commentPayload.Issue); + Assert.Equal(742, commentPayload.Issue.Number); + } + + [IntegrationTest] + public void PushEventPayloadDeserializesCorrectly() + { + var pushEvent = _events.FirstOrDefault(e => e.Id == "2628858765"); + Assert.NotNull(pushEvent); + Assert.Equal("PushEvent", pushEvent.Type); + var pushPayload = pushEvent.Payload as PushEventPayload; + Assert.NotNull(pushPayload); + Assert.NotNull(pushPayload.Commits); + Assert.Equal(1, pushPayload.Commits.Count); + Assert.Equal("3cdcba0ccbea0e6d13ae94249fbb294d71648321", pushPayload.Commits.FirstOrDefault().Sha); + Assert.Equal("3cdcba0ccbea0e6d13ae94249fbb294d71648321", pushPayload.Head); + Assert.Equal("refs/heads/release-candidate", pushPayload.Ref); + Assert.Equal(1, pushPayload.Size); + } + + [IntegrationTest] + public void PREventPayloadDeserializesCorrectly() + { + var prEvent = _events.FirstOrDefault(e => e.Id == "2628718313"); + Assert.NotNull(prEvent); + Assert.Equal("PullRequestEvent", prEvent.Type); + var prPayload = prEvent.Payload as PullRequestEventPayload; + Assert.NotNull(prPayload); + Assert.Equal("opened", prPayload.Action); + Assert.Equal(743, prPayload.Number); + Assert.NotNull(prPayload.PullRequest); + Assert.Equal(743, prPayload.PullRequest.Number); + } + + [IntegrationTest] + public void PRReviewCommentEventPayloadDeserializesCorrectly() + { + var prrcEvent = _events.First(e => e.Id == "2623246246"); + Assert.NotNull(prrcEvent); + Assert.Equal("PullRequestReviewCommentEvent", prrcEvent.Type); + var prrcPayload = prrcEvent.Payload as PullRequestCommentPayload; + Assert.NotNull(prrcPayload); + Assert.Equal("created", prrcPayload.Action); + Assert.NotNull(prrcPayload.Comment); + Assert.Equal("Suuuuuuuuure :P", prrcPayload.Comment.Body); + Assert.NotNull(prrcPayload.PullRequest); + Assert.Equal(737, prrcPayload.PullRequest.Number); + } } } } diff --git a/Octokit.Tests/Clients/EventsClientTests.cs b/Octokit.Tests/Clients/EventsClientTests.cs index cbe84e0c..c94c375f 100644 --- a/Octokit.Tests/Clients/EventsClientTests.cs +++ b/Octokit.Tests/Clients/EventsClientTests.cs @@ -1,6 +1,12 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; using System.Threading.Tasks; using NSubstitute; +using Octokit.Internal; +using Octokit; using Octokit.Tests.Helpers; using Xunit; @@ -219,5 +225,296 @@ namespace Octokit.Tests.Clients await AssertEx.Throws(async () => await client.GetForAnOrganization("fake", "")); } } + + private readonly Dictionary _activityTypes = new Dictionary + { + {"CommitCommentEvent", typeof(CommitCommentPayload)}, + {"ForkEvent", typeof(ForkEventPayload)}, + {"IssueCommentEvent", typeof(IssueCommentPayload)}, + {"IssuesEvent", typeof(IssueEventPayload)}, + {"PullRequestEvent", typeof(PullRequestEventPayload)}, + {"PullRequestReviewCommentEvent", typeof(PullRequestCommentPayload)}, + {"PushEvent", typeof(PushEventPayload)}, + {"WatchEvent", typeof(StarredEventPayload)}, + {"unknown", typeof(ActivityPayload)} + }; + + [Fact] + public async Task DeserializesPayloadToCorrectType() + { + _activityTypes.ToList().ForEach(async kvp => + { + var jsonObj = new JsonObject {{ "type", kvp.Key }, {"payload", new + { + repository = new + { + id = 1337 + }, + sender = new + { + id = 1337 + } + }}}; + + var client = GetTestingEventsClient(jsonObj); + + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + var activity = activities.FirstOrDefault(); + Assert.Equal(kvp.Value, activity.Payload.GetType()); + Assert.NotNull(activity.Payload.Repository); + Assert.NotNull(activity.Payload.Sender); + Assert.Equal(1337, activity.Payload.Repository.Id); + Assert.Equal(1337, activity.Payload.Sender.Id); + }); + } + + [Fact] + public async Task DeserializesCommitCommentEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "CommitCommentEvent" }, + { + "payload", new + { + comment = new + { + id = 1337 + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as CommitCommentPayload; + Assert.Equal(1337, payload.Comment.Id); + } + + [Fact] + public async Task DeserializesForkEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "ForkEvent" }, + { + "payload", new + { + forkee = new + { + id = 1337 + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as ForkEventPayload; + Assert.Equal(1337, payload.Forkee.Id); + } + + [Fact] + public async Task DeserializesIssueCommentEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "IssueCommentEvent" }, + { + "payload", new + { + action = "created", + issue = new + { + number = 1337 + }, + comment = new + { + id = 1337 + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as IssueCommentPayload; + Assert.Equal("created", payload.Action); + Assert.Equal(1337, payload.Comment.Id); + Assert.Equal(1337, payload.Issue.Number); + } + + [Fact] + public async Task DeserializesIssueEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "IssuesEvent" }, + { + "payload", new + { + action = "assigned", + issue = new + { + number = 1337 + }, + assignee = new + { + id = 1337 + }, + label = new + { + name = "bug" + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as IssueEventPayload; + Assert.Equal("assigned", payload.Action); + Assert.Equal(1337, payload.Issue.Number); + Assert.Equal(1337, payload.Assignee.Id); + Assert.Equal("bug", payload.Label.Name); + } + + [Fact] + public async Task DeserializesPullRequestEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "PullRequestEvent" }, + { + "payload", new + { + action = "assigned", + number = 1337, + pull_request = new + { + title = "PR Title" + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as PullRequestEventPayload; + Assert.Equal("assigned", payload.Action); + Assert.Equal(1337, payload.Number); + Assert.Equal("PR Title", payload.PullRequest.Title); + } + + [Fact] + public async Task DeserializesPullRequestCommentEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "PullRequestReviewCommentEvent" }, + { + "payload", new + { + action = "assigned", + pull_request = new + { + title = "PR Title" + }, + comment = new + { + id = 1337 + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as PullRequestCommentPayload; + Assert.Equal("assigned", payload.Action); + Assert.Equal("PR Title", payload.PullRequest.Title); + Assert.Equal(1337, payload.Comment.Id); + } + + [Fact] + public async Task DeserializesPushEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "PushEvent" }, + { + "payload", new + { + head = "head", + @ref = "ref", + size = 1337, + commits = new [] + { + new + { + message = "message" + } + } + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as PushEventPayload; + Assert.Equal("head", payload.Head); + Assert.Equal("ref", payload.Ref); + Assert.Equal(1337, payload.Size); + Assert.NotNull(payload.Commits); + Assert.Equal(1, payload.Commits.Count); + Assert.Equal("message", payload.Commits.FirstOrDefault().Message); + } + + [Fact] + public async Task DeserializesStarredEventCorrectly() + { + var jsonObj = new JsonObject + { + { "type", "WatchEvent" }, + { + "payload", new + { + action = "started" + } + } + }; + + var client = GetTestingEventsClient(jsonObj); + var activities = await client.GetAll(); + Assert.Equal(1, activities.Count); + + var payload = activities.FirstOrDefault().Payload as StarredEventPayload; + Assert.Equal("started", payload.Action); + } + + private EventsClient GetTestingEventsClient(JsonObject response) + { + var responseString = response.ToString(); + var httpClientMock = Substitute.For(); + httpClientMock.Send(Arg.Is((IRequest r) => r.Endpoint.ToString().Contains("events")), Arg.Any()).Returns(Task.FromResult( + new Response(HttpStatusCode.Accepted, responseString, new Dictionary(), "application/json") as IResponse)); + + return new EventsClient(new ApiConnection(new Connection(new ProductHeaderValue("mock"), httpClientMock))); + } } } diff --git a/Octokit/Http/SimpleJsonSerializer.cs b/Octokit/Http/SimpleJsonSerializer.cs index e0eb8524..bad16ccf 100644 --- a/Octokit/Http/SimpleJsonSerializer.cs +++ b/Octokit/Http/SimpleJsonSerializer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; +using Octokit; using Octokit.Reflection; namespace Octokit.Internal @@ -81,10 +82,13 @@ namespace Octokit.Internal return p.ToString().ToLowerInvariant(); } + private string _type = null; + // Overridden to handle enums. public override object DeserializeObject(object value, Type type) { var stringValue = value as string; + var jsonValue = value as JsonObject; if (stringValue != null) { stringValue = stringValue.Replace("-", ""); @@ -116,6 +120,19 @@ namespace Octokit.Internal } } } + else if (jsonValue != null) + { + if (type == typeof(Activity)) + { + _type = jsonValue["type"].ToString(); + } + } + + if (type == typeof(ActivityPayload)) + { + var payloadType = GetPayloadType(_type); + return base.DeserializeObject(value, payloadType); + } return base.DeserializeObject(value, type); } @@ -137,6 +154,30 @@ namespace Octokit.Internal p => p.JsonFieldName, p => new KeyValuePair(p.Type, p.SetDelegate)); } + + private static Type GetPayloadType(string activityType) + { + switch (activityType) + { + case "CommitCommentEvent": + return typeof(CommitCommentPayload); + case "ForkEvent": + return typeof(ForkEventPayload); + case "IssueCommentEvent": + return typeof(IssueCommentPayload); + case "IssuesEvent": + return typeof(IssueEventPayload); + case "PullRequestEvent": + return typeof(PullRequestEventPayload); + case "PullRequestReviewCommentEvent": + return typeof(PullRequestCommentPayload); + case "PushEvent": + return typeof(PushEventPayload); + case "WatchEvent": + return typeof(StarredEventPayload); + } + return typeof(ActivityPayload); + } } } } diff --git a/Octokit/Models/Response/Activity.cs b/Octokit/Models/Response/Activity.cs index b0887f63..7c3146e6 100644 --- a/Octokit/Models/Response/Activity.cs +++ b/Octokit/Models/Response/Activity.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using Octokit; namespace Octokit { @@ -60,6 +61,11 @@ namespace Octokit /// public string Id { get; protected set; } + /// + /// The payload associated with the activity event. + /// + public ActivityPayload Payload { get; protected set; } + internal string DebuggerDisplay { get diff --git a/Octokit/Models/Response/ActivityPayloads/ActivityPayload.cs b/Octokit/Models/Response/ActivityPayloads/ActivityPayload.cs new file mode 100644 index 00000000..9e2cdfd1 --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/ActivityPayload.cs @@ -0,0 +1,16 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ActivityPayload + { + public Repository Repository { get; protected set; } + public User Sender { get; protected set; } + + internal string DebuggerDisplay + { + get { return this.Repository.FullName; } + } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/CommitCommentPayload.cs b/Octokit/Models/Response/ActivityPayloads/CommitCommentPayload.cs new file mode 100644 index 00000000..9e35c994 --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/CommitCommentPayload.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class CommitCommentPayload : ActivityPayload + { + public CommitComment Comment { get; protected set; } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/ForkEventPayload.cs b/Octokit/Models/Response/ActivityPayloads/ForkEventPayload.cs new file mode 100644 index 00000000..f48d01c0 --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/ForkEventPayload.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class ForkEventPayload : ActivityPayload + { + public Repository Forkee { get; protected set; } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/IssueCommentPayload.cs b/Octokit/Models/Response/ActivityPayloads/IssueCommentPayload.cs new file mode 100644 index 00000000..581add5c --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/IssueCommentPayload.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class IssueCommentPayload : ActivityPayload + { + // should always be "created" according to github api docs + public string Action { get; protected set; } + public Issue Issue { get; protected set; } + public IssueComment Comment { get; protected set; } + + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/IssueEventPayload.cs b/Octokit/Models/Response/ActivityPayloads/IssueEventPayload.cs new file mode 100644 index 00000000..04f18177 --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/IssueEventPayload.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class IssueEventPayload : ActivityPayload + { + public string Action { get; protected set; } + public Issue Issue { get; protected set; } + public User Assignee { get; protected set; } + public Label Label { get; protected set; } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/PullRequestCommentPayload.cs b/Octokit/Models/Response/ActivityPayloads/PullRequestCommentPayload.cs new file mode 100644 index 00000000..026935e0 --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/PullRequestCommentPayload.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestCommentPayload : ActivityPayload + { + public string Action { get; protected set; } + public PullRequest PullRequest { get; protected set; } + public PullRequestReviewComment Comment { get; protected set; } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/PullRequestEventPayload.cs b/Octokit/Models/Response/ActivityPayloads/PullRequestEventPayload.cs new file mode 100644 index 00000000..ebaf48de --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/PullRequestEventPayload.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PullRequestEventPayload : ActivityPayload + { + public string Action { get; protected set; } + public int Number { get; protected set; } + + public PullRequest PullRequest { get; protected set; } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/PushEventPayload.cs b/Octokit/Models/Response/ActivityPayloads/PushEventPayload.cs new file mode 100644 index 00000000..ad6cdf73 --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/PushEventPayload.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class PushEventPayload : ActivityPayload + { + public string Head { get; protected set; } + public string Ref { get; protected set; } + public int Size { get; protected set; } + public IReadOnlyList Commits { get; protected set; } + } +} diff --git a/Octokit/Models/Response/ActivityPayloads/StarredEventPayload.cs b/Octokit/Models/Response/ActivityPayloads/StarredEventPayload.cs new file mode 100644 index 00000000..7bc43a2a --- /dev/null +++ b/Octokit/Models/Response/ActivityPayloads/StarredEventPayload.cs @@ -0,0 +1,10 @@ +using System.Diagnostics; + +namespace Octokit +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public class StarredEventPayload : ActivityPayload + { + public string Action { get; protected set; } + } +} diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index a8bf80ab..64a01234 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -120,6 +120,15 @@ + + + + + + + + + diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 56859c3a..14f5eae9 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -102,6 +102,15 @@ + + + + + + + + + diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 3535181b..2becca9b 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -99,6 +99,15 @@ + + + + + + + + + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index df8d5d62..f4092e19 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -210,6 +210,15 @@ + + + + + + + + + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index f29a903d..077ffd01 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -215,6 +215,15 @@ + + + + + + + + + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index a3bd9201..ec335c5e 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -93,6 +93,15 @@ + + + + + + + + +