From f35d133e610a800f747789cad2e113572bab8717 Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Thu, 22 Feb 2024 14:39:48 +0800 Subject: [PATCH 01/14] Add UserPickerField --- .../GraphQL/Startup.cs | 3 +- .../Types/UserPickerFieldQueryObjectType.cs | 71 +++++++++++++++++++ .../OrchardCore.ContentFields.csproj | 1 + .../OrchardCore.Users/GraphQL/Startup.cs | 2 +- 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs index 7baaf362ede..c13376033b8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs @@ -8,7 +8,7 @@ namespace OrchardCore.ContentFields.GraphQL { - [RequireFeatures("OrchardCore.Apis.GraphQL")] + [RequireFeatures("OrchardCore.Apis.GraphQL","OrchardCore.Users")] public class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) @@ -19,6 +19,7 @@ public override void ConfigureServices(IServiceCollection services) services.AddObjectGraphType(); services.AddObjectGraphType(); services.AddObjectGraphType(); + services.AddObjectGraphType(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs new file mode 100644 index 00000000000..fec5a347625 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using GraphQL; +using GraphQL.DataLoader; +using GraphQL.Types; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Apis.GraphQL; +using OrchardCore.ContentFields.Fields; +using OrchardCore.ContentManagement; +using OrchardCore.Users.GraphQL; +using OrchardCore.Users.Indexes; +using OrchardCore.Users.Models; +using YesSql; +using YesSql.Services; + +namespace OrchardCore.ContentFields.GraphQL +{ + public class UserPickerFieldQueryObjectType : ObjectGraphType + { + public UserPickerFieldQueryObjectType() + { + Name = nameof(UserPickerField); + + Field, IEnumerable>("userIds") + .Description("user ids") + .PagingArguments() + .Resolve(x => + { + return x.Page(x.Source.UserIds); + }); + + Field, IEnumerable>("users") + .Description("the user items") + .PagingArguments() + .ResolveAsync(x => + { + var userLoader = GetOrAddUserProfileByIdDataLoader(x); + return userLoader.LoadAsync(x.Page(x.Source.UserIds)).Then(itemResultSet => + { + return itemResultSet.SelectMany(x => x); + }); + }); + Field("user") + .Description("the first user") + .ResolveAsync(x => + { + var userLoader = GetOrAddUserProfileByIdDataLoader(x); + return userLoader.LoadAsync(x.Source.UserIds.FirstOrDefault()).Then(itemResultSet => + { + return itemResultSet.FirstOrDefault(); + }); + }); + } + + public static IDataLoader> GetOrAddUserProfileByIdDataLoader(IResolveFieldContext context) + { + IDataLoaderContextAccessor requiredService = context.RequestServices.GetRequiredService(); + var session = context.RequestServices.GetService(); + return requiredService.Context.GetOrAddCollectionBatchLoader("GetOrAddUserByIds", async (IEnumerable userIds) => + { + if (userIds == null || !userIds.Any()) + { + return null; + } + var users = await session.Query(y => y.UserId.IsIn(userIds)).ListAsync(); + + return users.ToLookup((User k) => k.UserId, (User user) => user); + }); + } + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/OrchardCore.ContentFields.csproj b/src/OrchardCore.Modules/OrchardCore.ContentFields/OrchardCore.ContentFields.csproj index d50cedac85f..1e75bfb884f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/OrchardCore.ContentFields.csproj +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/OrchardCore.ContentFields.csproj @@ -29,6 +29,7 @@ + diff --git a/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs index 4dba4a80de3..90ff703ac1d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs @@ -10,6 +10,6 @@ public class Startup : StartupBase public override void ConfigureServices(IServiceCollection services) { services.AddSingleton(); - services.AddTransient(); + services.AddSingleton(); } } From d85bc64a50064ad9e419bbf33432b752fb8c19ff Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Fri, 23 Feb 2024 01:36:33 +0800 Subject: [PATCH 02/14] fix query --- .../Types/UserPickerFieldQueryObjectType.cs | 4 ++- .../OrchardCore.Users/GraphQL/Startup.cs | 2 +- .../OrchardCore.Users/GraphQL/UserType.cs | 36 ++++++++++--------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs index fec5a347625..07f253d4a85 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs @@ -17,7 +17,7 @@ namespace OrchardCore.ContentFields.GraphQL { public class UserPickerFieldQueryObjectType : ObjectGraphType { - public UserPickerFieldQueryObjectType() + public UserPickerFieldQueryObjectType(UserType userType) { Name = nameof(UserPickerField); @@ -30,6 +30,7 @@ public UserPickerFieldQueryObjectType() }); Field, IEnumerable>("users") + .Type(new ListGraphType(userType)) .Description("the user items") .PagingArguments() .ResolveAsync(x => @@ -41,6 +42,7 @@ public UserPickerFieldQueryObjectType() }); }); Field("user") + .Type(userType) .Description("the first user") .ResolveAsync(x => { diff --git a/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs index 90ff703ac1d..85daf5d706f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/Startup.cs @@ -10,6 +10,6 @@ public class Startup : StartupBase public override void ConfigureServices(IServiceCollection services) { services.AddSingleton(); - services.AddSingleton(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/UserType.cs b/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/UserType.cs index f5d1818a94a..c1c09a4de73 100644 --- a/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/UserType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Users/GraphQL/UserType.cs @@ -3,6 +3,8 @@ using GraphQL.Types; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; +using OrchardCore.ContentManagement; +using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Users.Models; using OrchardCore.Users.Services; @@ -30,27 +32,29 @@ public UserType(IStringLocalizer localizer) internal void AddField(ISchema schema, ContentTypeDefinition typeDefinition) { var contentItemType = schema.AdditionalTypeInstances.SingleOrDefault(t => t.Name == typeDefinition.Name); - + if (contentItemType == null) { // This error would indicate that this graph type is build too early. throw new InvalidOperationException("ContentTypeDefinition has not been registered in GraphQL"); } - var field = Field(typeDefinition.Name, contentItemType.GetType()) - .Description(S["Custom user settings of {0}.", typeDefinition.DisplayName]) - .ResolveAsync(static async context => { - // We don't want to create an empty content item if it does not exist. - if (context.Source is User user && - user.Properties.ContainsKey(context.FieldDefinition.ResolvedType.Name)) - { - var customUserSettingsService = context.RequestServices!.GetRequiredService(); - var settingsType = await customUserSettingsService.GetSettingsTypeAsync(context.FieldDefinition.ResolvedType.Name); - - return await customUserSettingsService.GetSettingsAsync(user, settingsType); - } - - return null; - }); + Field(typeDefinition.Name) + .Type(contentItemType) + .Description(S["Custom user settings of {0}.", typeDefinition.DisplayName]) + .ResolveAsync(static async context => + { + // We don't want to create an empty content item if it does not exist. + if (context.Source is User user && + user.Properties.ContainsKey(context.FieldDefinition.ResolvedType.Name)) + { + var customUserSettingsService = context.RequestServices!.GetRequiredService(); + var settingsType = await customUserSettingsService.GetSettingsTypeAsync(context.FieldDefinition.ResolvedType.Name); + + return await customUserSettingsService.GetSettingsAsync(user, settingsType); + } + + return null; + }); } } From 1418b06fce7d8e69262d6b1fb0912971cb8de60d Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Fri, 23 Feb 2024 10:04:42 +0800 Subject: [PATCH 03/14] update dependency --- .../OrchardCore.ContentFields/GraphQL/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs index c13376033b8..6eff7d9ab16 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs @@ -8,7 +8,7 @@ namespace OrchardCore.ContentFields.GraphQL { - [RequireFeatures("OrchardCore.Apis.GraphQL","OrchardCore.Users")] + [RequireFeatures("OrchardCore.Apis.GraphQL")] public class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) From 92a21cd3cc9fbcfdaffafccbf759e786c608c58e Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Sun, 17 Mar 2024 03:46:15 +0800 Subject: [PATCH 04/14] fix_graphql_api_with_parameters --- .../OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs index 44281d94b6d..73e96ef4362 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs @@ -90,18 +90,19 @@ private async Task ExecuteAsync(HttpContext context) if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType)) { + using var sr = new StreamReader(context.Request.Body); + var bodyText = await sr.ReadToEndAsync(); if (mediaType.IsSubsetOf(_graphQlMediaType)) { - using var sr = new StreamReader(context.Request.Body); request = new GraphQLNamedQueryRequest { - Query = await sr.ReadToEndAsync() + Query = bodyText }; } else { - request = await JsonSerializer.DeserializeAsync(context.Request.Body, JOptions.CamelCase); + request = JsonSerializer.Deserialize(bodyText, JOptions.CamelCase); } } else From eb6319488b1f38304e1a47316dc4e072a10d2a49 Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Sun, 17 Mar 2024 12:03:44 +0800 Subject: [PATCH 05/14] fix graphql query --- .../GraphQLMiddleware.cs | 18 +-- .../GraphQLNamedQueryRequestJsonConverter.cs | 115 ++++++++++++++++++ .../OrchardCore.Apis.GraphQL/Startup.cs | 8 +- 3 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs index 73e96ef4362..6a8da016e9f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Mime; using System.Text; -using System.Text.Json; using System.Threading.Tasks; using GraphQL; using GraphQL.Execution; @@ -28,6 +27,7 @@ namespace OrchardCore.Apis.GraphQL public class GraphQLMiddleware : IMiddleware { private readonly GraphQLSettings _settings; + private readonly IGraphQLTextSerializer _graphQLTextSerializer; private readonly IGraphQLSerializer _serializer; private readonly IDocumentExecuter _executer; internal static readonly Encoding _utf8Encoding = new UTF8Encoding(false); @@ -37,11 +37,13 @@ public class GraphQLMiddleware : IMiddleware public GraphQLMiddleware( IOptions settingsOption, IDocumentExecuter executer, - IGraphQLSerializer serializer) + IGraphQLSerializer serializer, + IGraphQLTextSerializer graphQLTextSerializer) { _settings = settingsOption.Value; _executer = executer; _serializer = serializer; + _graphQLTextSerializer = graphQLTextSerializer; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { @@ -89,12 +91,10 @@ private async Task ExecuteAsync(HttpContext context) if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType)) { - using var sr = new StreamReader(context.Request.Body); var bodyText = await sr.ReadToEndAsync(); if (mediaType.IsSubsetOf(_graphQlMediaType)) { - request = new GraphQLNamedQueryRequest { Query = bodyText @@ -102,17 +102,17 @@ private async Task ExecuteAsync(HttpContext context) } else { - request = JsonSerializer.Deserialize(bodyText, JOptions.CamelCase); + request = _graphQLTextSerializer.Deserialize(bodyText); } } else { - request = CreateRequestFromQueryString(context); + request = CreateRequestFromQueryStringAsync(context); } } else if (HttpMethods.IsGet(context.Request.Method)) { - request = CreateRequestFromQueryString(context, true); + request = CreateRequestFromQueryStringAsync(context, true); } if (request == null) @@ -172,7 +172,7 @@ private async Task ExecuteAsync(HttpContext context) await _serializer.WriteAsync(context.Response.Body, result); } - private static GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext context, bool validateQueryKey = false) + private GraphQLNamedQueryRequest CreateRequestFromQueryStringAsync(HttpContext context, bool validateQueryKey = false) { if (!context.Request.Query.ContainsKey("query")) { @@ -191,7 +191,7 @@ private static GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext if (context.Request.Query.ContainsKey("variables")) { - request.Variables = JsonSerializer.Deserialize(context.Request.Query["variables"], JOptions.CamelCase); + request.Variables = _graphQLTextSerializer.Deserialize(context.Request.Query["variables"]); } if (context.Request.Query.ContainsKey("operationName")) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs new file mode 100644 index 00000000000..3adfb5eaa94 --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs @@ -0,0 +1,115 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using GraphQL; + +namespace OrchardCore.Apis.GraphQL.Json; +public class GraphQLNamedQueryRequestJsonConverter : JsonConverter +{ + public static GraphQLNamedQueryRequestJsonConverter Instance = new(); + /// + /// Name for the operation name parameter. + /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters + /// + private const string OPERATION_NAME_KEY = "operationName"; + + /// + /// Name for the query parameter. + /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters + /// + private const string QUERY_KEY = "query"; + + /// + /// Name for the variables parameter. + /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters + /// + private const string VARIABLES_KEY = "variables"; + + /// + /// Name for the extensions parameter. + /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters + /// + private const string EXTENSIONS_KEY = "extensions"; + + private const string NAMEDQUERY_KEY = "namedQuery"; + + + public override void Write(Utf8JsonWriter writer, GraphQLNamedQueryRequest value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + if (value.Query != null) + { + writer.WritePropertyName(QUERY_KEY); + writer.WriteStringValue(value.Query); + } + if (value.OperationName != null) + { + writer.WritePropertyName(OPERATION_NAME_KEY); + writer.WriteStringValue(value.OperationName); + } + if (value.Variables != null) + { + writer.WritePropertyName(VARIABLES_KEY); + JsonSerializer.Serialize(writer, value.Variables, options); + } + if (value.Extensions != null) + { + writer.WritePropertyName(EXTENSIONS_KEY); + JsonSerializer.Serialize(writer, value.Extensions, options); + } + if (value.NamedQuery != null) + { + writer.WritePropertyName(NAMEDQUERY_KEY); + JsonSerializer.Serialize(writer, value.NamedQuery, options); + } + writer.WriteEndObject(); + } + + public override GraphQLNamedQueryRequest Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException(); + + var request = new GraphQLNamedQueryRequest(); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + return request; + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new JsonException(); + + string key = reader.GetString()!; + + //unexpected end of data + if (!reader.Read()) + throw new JsonException(); + + switch (key) + { + case QUERY_KEY: + request.Query = reader.GetString()!; + break; + case OPERATION_NAME_KEY: + request.OperationName = reader.GetString()!; + break; + case NAMEDQUERY_KEY: + request.NamedQuery = reader.GetString(); + break; + case VARIABLES_KEY: + request.Variables = JsonSerializer.Deserialize(ref reader, options); + break; + case EXTENSIONS_KEY: + request.Extensions = JsonSerializer.Deserialize(ref reader, options); + break; + default: + reader.Skip(); + break; + } + } + + //unexpected end of data + throw new JsonException(); + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs index 8bdf71dda32..5bdc6fdfe36 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; +using OrchardCore.Apis.GraphQL.Json; using OrchardCore.Apis.GraphQL.Services; using OrchardCore.Apis.GraphQL.ValidationRules; using OrchardCore.Environment.Shell.Configuration; @@ -46,7 +47,12 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddTransient(); services.AddSingleton(); - services.AddGraphQL(builder => builder.AddSystemTextJson()); + + services.AddGraphQL(builder => builder.AddSystemTextJson(options => + { + // Common types of converters are already configured in the assembly "GraphQL.SystemTextJson". + options.Converters.Add(GraphQLNamedQueryRequestJsonConverter.Instance); + })); services.AddOptions().Configure((c, configuration) => { From 04d062a2e3c95221d1252a6f068a4ad4df207208 Mon Sep 17 00:00:00 2001 From: Tony Han Date: Tue, 19 Mar 2024 00:48:05 +0800 Subject: [PATCH 06/14] Update src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs Co-authored-by: Mike Alhayek --- .../Json/GraphQLNamedQueryRequestJsonConverter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs index 3adfb5eaa94..ce9803e61f0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs @@ -6,7 +6,8 @@ namespace OrchardCore.Apis.GraphQL.Json; public class GraphQLNamedQueryRequestJsonConverter : JsonConverter { - public static GraphQLNamedQueryRequestJsonConverter Instance = new(); + public static readonly GraphQLNamedQueryRequestJsonConverter Instance = new(); + /// /// Name for the operation name parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters From ec62d6c32250e5ccbff0a52ab304f431bcb66989 Mon Sep 17 00:00:00 2001 From: Tony Han Date: Tue, 19 Mar 2024 00:48:22 +0800 Subject: [PATCH 07/14] Update src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs Co-authored-by: Mike Alhayek --- .../Json/GraphQLNamedQueryRequestJsonConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs index ce9803e61f0..ddf2f2728b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs @@ -4,6 +4,7 @@ using GraphQL; namespace OrchardCore.Apis.GraphQL.Json; + public class GraphQLNamedQueryRequestJsonConverter : JsonConverter { public static readonly GraphQLNamedQueryRequestJsonConverter Instance = new(); From 580659fd8b0876bcb556d9b3420f2ab89268543e Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 00:59:24 +0800 Subject: [PATCH 08/14] fix code style --- .../GraphQLNamedQueryRequestJsonConverter.cs | 34 +++++++++++-------- .../OrchardCore.Apis.GraphQL/Startup.cs | 5 +++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs index ddf2f2728b5..0f1d1e580b3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs @@ -13,27 +13,27 @@ public class GraphQLNamedQueryRequestJsonConverter : JsonConverter - private const string OPERATION_NAME_KEY = "operationName"; + private const string OperationNameKey = "operationName"; /// /// Name for the query parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string QUERY_KEY = "query"; + private const string QueryKey = "query"; /// /// Name for the variables parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string VARIABLES_KEY = "variables"; + private const string VariablesKey = "variables"; /// /// Name for the extensions parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string EXTENSIONS_KEY = "extensions"; + private const string ExtensionsKey = "extensions"; - private const string NAMEDQUERY_KEY = "namedQuery"; + private const string NamedQueryKey = "namedQuery"; public override void Write(Utf8JsonWriter writer, GraphQLNamedQueryRequest value, JsonSerializerOptions options) @@ -41,27 +41,27 @@ public override void Write(Utf8JsonWriter writer, GraphQLNamedQueryRequest value writer.WriteStartObject(); if (value.Query != null) { - writer.WritePropertyName(QUERY_KEY); + writer.WritePropertyName(QueryKey); writer.WriteStringValue(value.Query); } if (value.OperationName != null) { - writer.WritePropertyName(OPERATION_NAME_KEY); + writer.WritePropertyName(OperationNameKey); writer.WriteStringValue(value.OperationName); } if (value.Variables != null) { - writer.WritePropertyName(VARIABLES_KEY); + writer.WritePropertyName(VariablesKey); JsonSerializer.Serialize(writer, value.Variables, options); } if (value.Extensions != null) { - writer.WritePropertyName(EXTENSIONS_KEY); + writer.WritePropertyName(ExtensionsKey); JsonSerializer.Serialize(writer, value.Extensions, options); } if (value.NamedQuery != null) { - writer.WritePropertyName(NAMEDQUERY_KEY); + writer.WritePropertyName(NamedQueryKey); JsonSerializer.Serialize(writer, value.NamedQuery, options); } writer.WriteEndObject(); @@ -77,10 +77,14 @@ public override GraphQLNamedQueryRequest Read(ref Utf8JsonReader reader, Type ty while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) + { return request; + } if (reader.TokenType != JsonTokenType.PropertyName) + { throw new JsonException(); + } string key = reader.GetString()!; @@ -90,19 +94,19 @@ public override GraphQLNamedQueryRequest Read(ref Utf8JsonReader reader, Type ty switch (key) { - case QUERY_KEY: + case QueryKey: request.Query = reader.GetString()!; break; - case OPERATION_NAME_KEY: + case OperationNameKey: request.OperationName = reader.GetString()!; break; - case NAMEDQUERY_KEY: + case NamedQueryKey: request.NamedQuery = reader.GetString(); break; - case VARIABLES_KEY: + case VariablesKey: request.Variables = JsonSerializer.Deserialize(ref reader, options); break; - case EXTENSIONS_KEY: + case ExtensionsKey: request.Extensions = JsonSerializer.Deserialize(ref reader, options); break; default: diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs index 5bdc6fdfe36..01f8cae3a26 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs @@ -13,6 +13,7 @@ using OrchardCore.Apis.GraphQL.Services; using OrchardCore.Apis.GraphQL.ValidationRules; using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Extensions; using OrchardCore.Modules; using OrchardCore.Navigation; using OrchardCore.Security.Permissions; @@ -47,6 +48,10 @@ public override void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddTransient(); services.AddSingleton(); + services.Configure(options => + { + + }); services.AddGraphQL(builder => builder.AddSystemTextJson(options => { From 99592bd39002a5cab8593c6132572e1451ecd6c9 Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 01:04:55 +0800 Subject: [PATCH 09/14] add contentSerializerJsonOptions to graphql serializer --- .../OrchardCore.Apis.GraphQL/Startup.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs index 01f8cae3a26..ba1b335a67a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs @@ -14,6 +14,7 @@ using OrchardCore.Apis.GraphQL.ValidationRules; using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Extensions; +using OrchardCore.Json; using OrchardCore.Modules; using OrchardCore.Navigation; using OrchardCore.Security.Permissions; @@ -53,10 +54,16 @@ public override void ConfigureServices(IServiceCollection services) }); - services.AddGraphQL(builder => builder.AddSystemTextJson(options => + services.AddGraphQL(builder => builder.AddSystemTextJson((options, sp) => { // Common types of converters are already configured in the assembly "GraphQL.SystemTextJson". options.Converters.Add(GraphQLNamedQueryRequestJsonConverter.Instance); + + var contentSerializerJsonOptions = sp.GetRequiredService(); + foreach (var converter in contentSerializerJsonOptions.SerializerOptions.Converters) + { + options.Converters.Add(converter); + } })); services.AddOptions().Configure((c, configuration) => From fdb3efb6e469df9b32c9ead54359a729428b1919 Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 01:06:16 +0800 Subject: [PATCH 10/14] fix option action --- src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs index ba1b335a67a..f44d0d99741 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs @@ -59,7 +59,7 @@ public override void ConfigureServices(IServiceCollection services) // Common types of converters are already configured in the assembly "GraphQL.SystemTextJson". options.Converters.Add(GraphQLNamedQueryRequestJsonConverter.Instance); - var contentSerializerJsonOptions = sp.GetRequiredService(); + var contentSerializerJsonOptions = sp.GetRequiredService>().Value; foreach (var converter in contentSerializerJsonOptions.SerializerOptions.Converters) { options.Converters.Add(converter); From f11752c9e9cc798b30c03efb4763cafcacf0e5dc Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 01:42:29 +0800 Subject: [PATCH 11/14] update options --- src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs index f44d0d99741..fb97d811d05 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs @@ -15,6 +15,7 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Extensions; using OrchardCore.Json; +using OrchardCore.Json.Extensions; using OrchardCore.Modules; using OrchardCore.Navigation; using OrchardCore.Security.Permissions; @@ -60,10 +61,7 @@ public override void ConfigureServices(IServiceCollection services) options.Converters.Add(GraphQLNamedQueryRequestJsonConverter.Instance); var contentSerializerJsonOptions = sp.GetRequiredService>().Value; - foreach (var converter in contentSerializerJsonOptions.SerializerOptions.Converters) - { - options.Converters.Add(converter); - } + options.Merge(contentSerializerJsonOptions.SerializerOptions); })); services.AddOptions().Configure((c, configuration) => From 9dbe55d3bf4393855499d7452e7cb5e8e8a7a05b Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 02:14:48 +0800 Subject: [PATCH 12/14] remove var bodyText --- .../OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs index 6a8da016e9f..53182c511dd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs @@ -92,17 +92,16 @@ private async Task ExecuteAsync(HttpContext context) if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType)) { using var sr = new StreamReader(context.Request.Body); - var bodyText = await sr.ReadToEndAsync(); if (mediaType.IsSubsetOf(_graphQlMediaType)) { request = new GraphQLNamedQueryRequest { - Query = bodyText + Query = await sr.ReadToEndAsync() }; } else { - request = _graphQLTextSerializer.Deserialize(bodyText); + request = _graphQLTextSerializer.Deserialize(await sr.ReadToEndAsync()); } } else From fe6bbb6b1edcdd2429d96db4828940d3f08f3371 Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 02:15:50 +0800 Subject: [PATCH 13/14] restore method name --- .../OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs index 53182c511dd..c988122c5fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs @@ -106,12 +106,12 @@ private async Task ExecuteAsync(HttpContext context) } else { - request = CreateRequestFromQueryStringAsync(context); + request = CreateRequestFromQueryString(context); } } else if (HttpMethods.IsGet(context.Request.Method)) { - request = CreateRequestFromQueryStringAsync(context, true); + request = CreateRequestFromQueryString(context, true); } if (request == null) @@ -171,7 +171,7 @@ private async Task ExecuteAsync(HttpContext context) await _serializer.WriteAsync(context.Response.Body, result); } - private GraphQLNamedQueryRequest CreateRequestFromQueryStringAsync(HttpContext context, bool validateQueryKey = false) + private GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext context, bool validateQueryKey = false) { if (!context.Request.Query.ContainsKey("query")) { From 1571457e3cd28e4f23ae5cca4515a077d7d501ae Mon Sep 17 00:00:00 2001 From: hyzx86 Date: Tue, 19 Mar 2024 09:02:03 +0800 Subject: [PATCH 14/14] update code --- .../GraphQLNamedQueryRequestJsonConverter.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs index 0f1d1e580b3..3db29857c7d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Json/GraphQLNamedQueryRequestJsonConverter.cs @@ -8,32 +8,32 @@ namespace OrchardCore.Apis.GraphQL.Json; public class GraphQLNamedQueryRequestJsonConverter : JsonConverter { public static readonly GraphQLNamedQueryRequestJsonConverter Instance = new(); - + /// /// Name for the operation name parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string OperationNameKey = "operationName"; + private const string _operationNameKey = "operationName"; /// /// Name for the query parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string QueryKey = "query"; + private const string _queryKey = "query"; /// /// Name for the variables parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string VariablesKey = "variables"; + private const string _variablesKey = "variables"; /// /// Name for the extensions parameter. /// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters /// - private const string ExtensionsKey = "extensions"; + private const string _extensionsKey = "extensions"; - private const string NamedQueryKey = "namedQuery"; + private const string _namedQueryKey = "namedQuery"; public override void Write(Utf8JsonWriter writer, GraphQLNamedQueryRequest value, JsonSerializerOptions options) @@ -41,27 +41,27 @@ public override void Write(Utf8JsonWriter writer, GraphQLNamedQueryRequest value writer.WriteStartObject(); if (value.Query != null) { - writer.WritePropertyName(QueryKey); + writer.WritePropertyName(_queryKey); writer.WriteStringValue(value.Query); } if (value.OperationName != null) { - writer.WritePropertyName(OperationNameKey); + writer.WritePropertyName(_operationNameKey); writer.WriteStringValue(value.OperationName); } if (value.Variables != null) { - writer.WritePropertyName(VariablesKey); + writer.WritePropertyName(_variablesKey); JsonSerializer.Serialize(writer, value.Variables, options); } if (value.Extensions != null) { - writer.WritePropertyName(ExtensionsKey); + writer.WritePropertyName(_extensionsKey); JsonSerializer.Serialize(writer, value.Extensions, options); } if (value.NamedQuery != null) { - writer.WritePropertyName(NamedQueryKey); + writer.WritePropertyName(_namedQueryKey); JsonSerializer.Serialize(writer, value.NamedQuery, options); } writer.WriteEndObject(); @@ -94,19 +94,19 @@ public override GraphQLNamedQueryRequest Read(ref Utf8JsonReader reader, Type ty switch (key) { - case QueryKey: + case _queryKey: request.Query = reader.GetString()!; break; - case OperationNameKey: + case _operationNameKey: request.OperationName = reader.GetString()!; break; - case NamedQueryKey: + case _namedQueryKey: request.NamedQuery = reader.GetString(); break; - case VariablesKey: + case _variablesKey: request.Variables = JsonSerializer.Deserialize(ref reader, options); break; - case ExtensionsKey: + case _extensionsKey: request.Extensions = JsonSerializer.Deserialize(ref reader, options); break; default: