From 2701be5e79a47a88ec075ee2df24e905a770142a Mon Sep 17 00:00:00 2001 From: Chris Simpson Date: Tue, 20 Sep 2022 21:15:19 +0100 Subject: [PATCH] Fix deserializing of Emoji types (#2577) --- .../Clients/MiscellaneousClientTests.cs | 12 +++--- Octokit/Clients/MiscellaneousClient.cs | 11 +++-- Octokit/Http/AssignableExtensions.cs | 40 +++++++++++++++++++ Octokit/Http/JsonHttpPipeline.cs | 2 +- 4 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 Octokit/Http/AssignableExtensions.cs diff --git a/Octokit.Tests/Clients/MiscellaneousClientTests.cs b/Octokit.Tests/Clients/MiscellaneousClientTests.cs index 059316e354..1c44aec35e 100644 --- a/Octokit.Tests/Clients/MiscellaneousClientTests.cs +++ b/Octokit.Tests/Clients/MiscellaneousClientTests.cs @@ -65,15 +65,15 @@ public class TheGetEmojisMethod [Fact] public async Task RequestsTheEmojiEndpoint() { - IReadOnlyList response = new List + IDictionary response = new Dictionary { - { new Emoji("foo", "http://example.com/foo.gif") }, - { new Emoji("bar", "http://example.com/bar.gif") } + { "foo", "http://example.com/foo.gif" }, + { "bar", "http://example.com/bar.gif" } }; var apiConnection = Substitute.For(); - apiConnection.GetAll(Args.Uri) - .Returns(Task.FromResult(response)); + apiConnection.Get>(Args.Uri) + .Returns(Task.FromResult(response)); var client = new MiscellaneousClient(apiConnection); @@ -82,7 +82,7 @@ public async Task RequestsTheEmojiEndpoint() Assert.Equal(2, emojis.Count); Assert.Equal("foo", emojis[0].Name); apiConnection.Received() - .GetAll(Arg.Is(u => u.ToString() == "emojis")); + .Get>(Arg.Is(u => u.ToString() == "emojis")); } } diff --git a/Octokit/Clients/MiscellaneousClient.cs b/Octokit/Clients/MiscellaneousClient.cs index 872753c393..ab64a8ee16 100644 --- a/Octokit/Clients/MiscellaneousClient.cs +++ b/Octokit/Clients/MiscellaneousClient.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading.Tasks; namespace Octokit @@ -39,12 +41,13 @@ public MiscellaneousClient(IApiConnection apiConnection) /// Gets all the emojis available to use on GitHub. /// /// Thrown when a general API error occurs. - /// An of emoji and their URI. + /// An of emoji and their URI. [ManualRoute("GET", "/emojis")] - [Obsolete("This client is being deprecated and will be removed in the future. Use EmojisClient.GetAllEmojis instead.")] - public Task> GetAllEmojis() + public async Task> GetAllEmojis() { - return _emojisClient.GetAllEmojis(); + var result = await ApiConnection.Get>(ApiUrls.Emojis()); + + return result.Select(x => new Emoji(x.Key, x.Value)).ToList(); } /// diff --git a/Octokit/Http/AssignableExtensions.cs b/Octokit/Http/AssignableExtensions.cs new file mode 100644 index 0000000000..6fd2de8fb5 --- /dev/null +++ b/Octokit/Http/AssignableExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Linq; + +namespace Octokit +{ + public static class AssignableExtensions + { + /// + /// Determines whether the is assignable from + /// taking into account generic definitions + /// + public static bool IsAssignableToGenericType(this Type givenType, Type genericType) + { + if (givenType == null || genericType == null) + { + return false; + } + + return givenType == genericType + || givenType.MapsToGenericTypeDefinition(genericType) + || givenType.HasInterfaceThatMapsToGenericTypeDefinition(genericType) + || givenType.BaseType.IsAssignableToGenericType(genericType); + } + + private static bool HasInterfaceThatMapsToGenericTypeDefinition(this Type givenType, Type genericType) + { + return givenType + .GetInterfaces() + .Where(it => it.IsGenericType) + .Any(it => it.GetGenericTypeDefinition() == genericType); + } + + private static bool MapsToGenericTypeDefinition(this Type givenType, Type genericType) + { + return genericType.IsGenericTypeDefinition + && givenType.IsGenericType + && givenType.GetGenericTypeDefinition() == genericType; + } + } +} diff --git a/Octokit/Http/JsonHttpPipeline.cs b/Octokit/Http/JsonHttpPipeline.cs index 7296cd378a..e40a347d1d 100644 --- a/Octokit/Http/JsonHttpPipeline.cs +++ b/Octokit/Http/JsonHttpPipeline.cs @@ -49,7 +49,7 @@ public IApiResponse DeserializeResponse(IResponse response) // simple json does not support the root node being empty. Will submit a pr but in the mean time.... if (!string.IsNullOrEmpty(body) && body != "{}") { - var typeIsDictionary = typeof(IDictionary).IsAssignableFrom(typeof(T)); + var typeIsDictionary = typeof(IDictionary).IsAssignableFrom(typeof(T)) || typeof(T).IsAssignableToGenericType(typeof(System.Collections.Generic.IDictionary<,>)); var typeIsEnumerable = typeof(IEnumerable).IsAssignableFrom(typeof(T)); var responseIsObject = body.StartsWith("{", StringComparison.Ordinal);