From 09f52a6a79f9669b1bb3c747313fd66d6bae6731 Mon Sep 17 00:00:00 2001 From: Juanu Date: Thu, 24 Nov 2022 14:56:19 +0100 Subject: [PATCH 1/4] Use GraphQL.Client for Scaffolding Using GraphQL improves performance and interaction with GraphQL client. Specifically, this was tested using the SpaceX sample but did't work with the Lens API GraphQL. Using GraphQL.Client got rid of the issue and works on both. --- .gitignore | 1 + src/GraphQLinq.Scaffolding/MySerializer.cs | 62 +++++++++++++++++++ src/GraphQLinq.Scaffolding/Program.cs | 36 ++++++++--- .../RootSchemaObject.cs | 6 +- src/GraphQLinq.Scaffolding/Scaffolding.csproj | 10 +++ .../Properties/launchSettings.json | 5 +- src/GraphQLinq.TestServer/TestServer.csproj | 1 + 7 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 src/GraphQLinq.Scaffolding/MySerializer.cs diff --git a/.gitignore b/.gitignore index 0ca0c13..1c1ff5c 100644 --- a/.gitignore +++ b/.gitignore @@ -240,3 +240,4 @@ ModelManifest.xml # FAKE - F# Make .fake/ +**/.DS_Store diff --git a/src/GraphQLinq.Scaffolding/MySerializer.cs b/src/GraphQLinq.Scaffolding/MySerializer.cs new file mode 100644 index 0000000..dbda6e3 --- /dev/null +++ b/src/GraphQLinq.Scaffolding/MySerializer.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using GraphQL.Client.Abstractions; +using GraphQL.Client.Abstractions.Websocket; +using GraphQL.Client.Serializer.Newtonsoft; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Scaffolding; + +public class MySerializer : IGraphQLWebsocketJsonSerializer +{ + public static JsonSerializerSettings DefaultJsonSerializerSettings => new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, + MissingMemberHandling = MissingMemberHandling.Ignore, + Converters = { new ConstantCaseEnumConverter() } + }; + + public JsonSerializerSettings JsonSerializerSettings { get; } + + public MySerializer() : this(DefaultJsonSerializerSettings) { } + + public MySerializer(Action configure) : this(configure.AndReturn(DefaultJsonSerializerSettings)) { } + + public MySerializer(JsonSerializerSettings jsonSerializerSettings) + { + JsonSerializerSettings = jsonSerializerSettings; + ConfigureMandatorySerializerOptions(); + } + + // deserialize extensions to Dictionary + private void ConfigureMandatorySerializerOptions() => JsonSerializerSettings.Converters.Insert(0, new MapConverter()); + + public string SerializeToString(GraphQLRequest request) => JsonConvert.SerializeObject(request, JsonSerializerSettings); + + public byte[] SerializeToBytes(GraphQLWebSocketRequest request) + { + var json = JsonConvert.SerializeObject(request, JsonSerializerSettings); + return Encoding.UTF8.GetBytes(json); + } + + public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) => DeserializeFromUtf8Stream(stream); + + public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) => + JsonConvert.DeserializeObject>>(Encoding.UTF8.GetString(bytes), + JsonSerializerSettings); + + public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) => DeserializeFromUtf8Stream>(stream); + + private Task DeserializeFromUtf8Stream(Stream stream) + { + using var sr = new StreamReader(stream); + using JsonReader reader = new JsonTextReader(sr); + var serializer = JsonSerializer.Create(JsonSerializerSettings); + return Task.FromResult(serializer.Deserialize(reader)); + } +} \ No newline at end of file diff --git a/src/GraphQLinq.Scaffolding/Program.cs b/src/GraphQLinq.Scaffolding/Program.cs index 4a16f83..ed3d134 100644 --- a/src/GraphQLinq.Scaffolding/Program.cs +++ b/src/GraphQLinq.Scaffolding/Program.cs @@ -6,6 +6,10 @@ using System.Net.Http.Json; using System.Text.Json; using System.Threading.Tasks; +using GraphQL; +using GraphQL.Client.Http; +using GraphQL.Client.Serializer.Newtonsoft; +using Scaffolding; using Spectre.Console; namespace GraphQLinq.Scaffolding @@ -135,15 +139,31 @@ private static async Task HandleGenerate(Uri endpoint, string output, string @na AnsiConsole.MarkupLine("Scaffolding GraphQL client code for [bold]{0}[/] to [bold]{1}[/]", endpoint, outputFolder); - var schema = await AnsiConsole.Status().StartAsync("Performing introspection", async ctx => + var data = await AnsiConsole.Status().StartAsync("Performing introspection", async ctx => { - AnsiConsole.WriteLine("Running introspection query ..."); - using var httpClient = new HttpClient(); - using var responseMessage = await httpClient.PostAsJsonAsync(endpoint, new { query = IntrospectionQuery }); + try + { + + AnsiConsole.WriteLine("Running introspection query ..."); + //using var httpClient = new HttpClient(); + GraphQLHttpClient client = new GraphQLHttpClient(endpoint, new MySerializer()); + var request = new GraphQLRequest + { + Query = IntrospectionQuery + }; + var responseMessage = await client.SendQueryAsync(request); + //using var responseMessage = await httpClient.GetAsync(endpoint.ToString(), JsonContent.Create(new { query = IntrospectionQuery })); + //using var responseMessage = await httpClient.PostAsJsonAsync(endpoint, new { query = IntrospectionQuery }); - AnsiConsole.WriteLine("Reading and deserializing schema information ..."); - var schemaJson = await responseMessage.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(schemaJson, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + AnsiConsole.WriteLine("Reading and deserializing schema information ..."); + return responseMessage.Data; + //var schemaJson = await responseMessage; + //return JsonSerializer.Deserialize(schemaJson, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + } + catch (Exception ex) + { + throw ex; + } }); AnsiConsole.WriteLine(); @@ -158,7 +178,7 @@ private static async Task HandleGenerate(Uri endpoint, string output, string @na }; var graphQLClassesGenerator = new GraphQLClassesGenerator(codeGenerationOptions); - return graphQLClassesGenerator.GenerateClient(schema.Data.Schema, endpoint.AbsoluteUri); + return graphQLClassesGenerator.GenerateClient(data.Schema, endpoint.AbsoluteUri); }); AnsiConsole.WriteLine(); diff --git a/src/GraphQLinq.Scaffolding/RootSchemaObject.cs b/src/GraphQLinq.Scaffolding/RootSchemaObject.cs index 16b156d..80b6b27 100644 --- a/src/GraphQLinq.Scaffolding/RootSchemaObject.cs +++ b/src/GraphQLinq.Scaffolding/RootSchemaObject.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; -using System.Text.Json.Serialization; +using Newtonsoft.Json; namespace GraphQLinq.Scaffolding { @@ -12,7 +12,7 @@ public class RootSchemaObject public class Data { - [JsonPropertyName("__schema")] + [JsonProperty("__schema")] public Schema Schema { get; set; } } @@ -33,7 +33,7 @@ public class GraphqlType : BaseInfo public List Interfaces { get; set; } } - [JsonConverter(typeof(JsonStringEnumMemberConverter))] + //[JsonConverter(typeof(JsonStringEnumMemberConverter))] public enum TypeKind { List, diff --git a/src/GraphQLinq.Scaffolding/Scaffolding.csproj b/src/GraphQLinq.Scaffolding/Scaffolding.csproj index 0243803..5a2ebb7 100644 --- a/src/GraphQLinq.Scaffolding/Scaffolding.csproj +++ b/src/GraphQLinq.Scaffolding/Scaffolding.csproj @@ -31,6 +31,10 @@ Generate classes from GraphQL endpoint and write strongly typed queries with LINQ. GraphQLinq.Scaffolding + + 4 + GraphQLinq.Scaffolding + @@ -38,6 +42,12 @@ + + + + + + diff --git a/src/GraphQLinq.TestServer/Properties/launchSettings.json b/src/GraphQLinq.TestServer/Properties/launchSettings.json index dab3581..d8690b1 100644 --- a/src/GraphQLinq.TestServer/Properties/launchSettings.json +++ b/src/GraphQLinq.TestServer/Properties/launchSettings.json @@ -1,11 +1,10 @@ -{ +{ "profiles": { "GraphQLing.TestServer": { "commandName": "Project", - "dotnetRunMessages": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } -} +} \ No newline at end of file diff --git a/src/GraphQLinq.TestServer/TestServer.csproj b/src/GraphQLinq.TestServer/TestServer.csproj index 44fd198..e2cc575 100644 --- a/src/GraphQLinq.TestServer/TestServer.csproj +++ b/src/GraphQLinq.TestServer/TestServer.csproj @@ -6,6 +6,7 @@ enable + From ae0d870a6bf3ac4ab9dfd89b7c6f684947eca0e4 Mon Sep 17 00:00:00 2001 From: Juanu Date: Thu, 24 Nov 2022 14:57:52 +0100 Subject: [PATCH 2/4] Remove test Serializer --- src/GraphQLinq.Scaffolding/MySerializer.cs | 62 ---------------------- src/GraphQLinq.Scaffolding/Program.cs | 5 +- 2 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 src/GraphQLinq.Scaffolding/MySerializer.cs diff --git a/src/GraphQLinq.Scaffolding/MySerializer.cs b/src/GraphQLinq.Scaffolding/MySerializer.cs deleted file mode 100644 index dbda6e3..0000000 --- a/src/GraphQLinq.Scaffolding/MySerializer.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; -using GraphQL.Client.Abstractions; -using GraphQL.Client.Abstractions.Websocket; -using GraphQL.Client.Serializer.Newtonsoft; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Scaffolding; - -public class MySerializer : IGraphQLWebsocketJsonSerializer -{ - public static JsonSerializerSettings DefaultJsonSerializerSettings => new JsonSerializerSettings - { - ContractResolver = new CamelCasePropertyNamesContractResolver { IgnoreIsSpecifiedMembers = true }, - MissingMemberHandling = MissingMemberHandling.Ignore, - Converters = { new ConstantCaseEnumConverter() } - }; - - public JsonSerializerSettings JsonSerializerSettings { get; } - - public MySerializer() : this(DefaultJsonSerializerSettings) { } - - public MySerializer(Action configure) : this(configure.AndReturn(DefaultJsonSerializerSettings)) { } - - public MySerializer(JsonSerializerSettings jsonSerializerSettings) - { - JsonSerializerSettings = jsonSerializerSettings; - ConfigureMandatorySerializerOptions(); - } - - // deserialize extensions to Dictionary - private void ConfigureMandatorySerializerOptions() => JsonSerializerSettings.Converters.Insert(0, new MapConverter()); - - public string SerializeToString(GraphQLRequest request) => JsonConvert.SerializeObject(request, JsonSerializerSettings); - - public byte[] SerializeToBytes(GraphQLWebSocketRequest request) - { - var json = JsonConvert.SerializeObject(request, JsonSerializerSettings); - return Encoding.UTF8.GetBytes(json); - } - - public Task DeserializeToWebsocketResponseWrapperAsync(Stream stream) => DeserializeFromUtf8Stream(stream); - - public GraphQLWebSocketResponse> DeserializeToWebsocketResponse(byte[] bytes) => - JsonConvert.DeserializeObject>>(Encoding.UTF8.GetString(bytes), - JsonSerializerSettings); - - public Task> DeserializeFromUtf8StreamAsync(Stream stream, CancellationToken cancellationToken) => DeserializeFromUtf8Stream>(stream); - - private Task DeserializeFromUtf8Stream(Stream stream) - { - using var sr = new StreamReader(stream); - using JsonReader reader = new JsonTextReader(sr); - var serializer = JsonSerializer.Create(JsonSerializerSettings); - return Task.FromResult(serializer.Deserialize(reader)); - } -} \ No newline at end of file diff --git a/src/GraphQLinq.Scaffolding/Program.cs b/src/GraphQLinq.Scaffolding/Program.cs index ed3d134..b7ed37e 100644 --- a/src/GraphQLinq.Scaffolding/Program.cs +++ b/src/GraphQLinq.Scaffolding/Program.cs @@ -9,7 +9,6 @@ using GraphQL; using GraphQL.Client.Http; using GraphQL.Client.Serializer.Newtonsoft; -using Scaffolding; using Spectre.Console; namespace GraphQLinq.Scaffolding @@ -146,14 +145,12 @@ private static async Task HandleGenerate(Uri endpoint, string output, string @na AnsiConsole.WriteLine("Running introspection query ..."); //using var httpClient = new HttpClient(); - GraphQLHttpClient client = new GraphQLHttpClient(endpoint, new MySerializer()); + GraphQLHttpClient client = new GraphQLHttpClient(endpoint, new NewtonsoftJsonSerializer()); var request = new GraphQLRequest { Query = IntrospectionQuery }; var responseMessage = await client.SendQueryAsync(request); - //using var responseMessage = await httpClient.GetAsync(endpoint.ToString(), JsonContent.Create(new { query = IntrospectionQuery })); - //using var responseMessage = await httpClient.PostAsJsonAsync(endpoint, new { query = IntrospectionQuery }); AnsiConsole.WriteLine("Reading and deserializing schema information ..."); return responseMessage.Data; From de4763e9851d0749560f98109422c4a34d81e8cb Mon Sep 17 00:00:00 2001 From: Juanu Date: Sat, 26 Nov 2022 17:03:53 +0100 Subject: [PATCH 3/4] Fixed Demo project Output Assembly name --- src/GraphQLinq.Demo/Demo.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GraphQLinq.Demo/Demo.csproj b/src/GraphQLinq.Demo/Demo.csproj index 6d84151..2b6ae33 100644 --- a/src/GraphQLinq.Demo/Demo.csproj +++ b/src/GraphQLinq.Demo/Demo.csproj @@ -3,6 +3,9 @@ net6 Exe + + GraphQLinq.Demo + From 1aaf7f8b568ec8f79376e671f980cef89c8cde9b Mon Sep 17 00:00:00 2001 From: Juanu Date: Sat, 26 Nov 2022 17:07:02 +0100 Subject: [PATCH 4/4] System.Text Serialization & .net 7.0 upgrade EnumMember attribute is not supported out of the box in .net 6 https://stackoverflow.com/a/59061296/1561618 --- src/GraphQLinq.Scaffolding/Program.cs | 6 +++--- src/GraphQLinq.Scaffolding/RootSchemaObject.cs | 12 +++++++++--- src/GraphQLinq.Scaffolding/Scaffolding.csproj | 5 +++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/GraphQLinq.Scaffolding/Program.cs b/src/GraphQLinq.Scaffolding/Program.cs index b7ed37e..dbf7ee9 100644 --- a/src/GraphQLinq.Scaffolding/Program.cs +++ b/src/GraphQLinq.Scaffolding/Program.cs @@ -5,10 +5,11 @@ using System.Net.Http; using System.Net.Http.Json; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using GraphQL; using GraphQL.Client.Http; -using GraphQL.Client.Serializer.Newtonsoft; +using GraphQL.Client.Serializer.SystemTextJson; using Spectre.Console; namespace GraphQLinq.Scaffolding @@ -144,8 +145,7 @@ private static async Task HandleGenerate(Uri endpoint, string output, string @na { AnsiConsole.WriteLine("Running introspection query ..."); - //using var httpClient = new HttpClient(); - GraphQLHttpClient client = new GraphQLHttpClient(endpoint, new NewtonsoftJsonSerializer()); + GraphQLHttpClient client = new GraphQLHttpClient(endpoint, new SystemTextJsonSerializer()); var request = new GraphQLRequest { Query = IntrospectionQuery diff --git a/src/GraphQLinq.Scaffolding/RootSchemaObject.cs b/src/GraphQLinq.Scaffolding/RootSchemaObject.cs index 80b6b27..4ebcbbf 100644 --- a/src/GraphQLinq.Scaffolding/RootSchemaObject.cs +++ b/src/GraphQLinq.Scaffolding/RootSchemaObject.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace GraphQLinq.Scaffolding { @@ -12,7 +12,7 @@ public class RootSchemaObject public class Data { - [JsonProperty("__schema")] + [JsonPropertyName("__schema")] public Schema Schema { get; set; } } @@ -33,16 +33,22 @@ public class GraphqlType : BaseInfo public List Interfaces { get; set; } } - //[JsonConverter(typeof(JsonStringEnumMemberConverter))] + [JsonConverter(typeof(JsonStringEnumMemberConverter))] public enum TypeKind { + [EnumMember(Value = "LIST")] List, [EnumMember(Value = "NON_NULL")] NonNull, + [EnumMember(Value = "SCALAR")] Scalar, + [EnumMember(Value = "OBJECT")] Object, + [EnumMember(Value = "INTERFACE")] Interface, + [EnumMember(Value = "UNION")] Union, + [EnumMember(Value = "ENUM")] Enum, [EnumMember(Value = "INPUT_OBJECT")] InputObject diff --git a/src/GraphQLinq.Scaffolding/Scaffolding.csproj b/src/GraphQLinq.Scaffolding/Scaffolding.csproj index 5a2ebb7..e421228 100644 --- a/src/GraphQLinq.Scaffolding/Scaffolding.csproj +++ b/src/GraphQLinq.Scaffolding/Scaffolding.csproj @@ -1,6 +1,6 @@  - net6 + net7 Exe enable @@ -43,11 +43,12 @@ - + +