Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix graphql api with parameters #1

Merged
merged 12 commits into from
Mar 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -37,11 +37,13 @@ public class GraphQLMiddleware : IMiddleware
public GraphQLMiddleware(
IOptions<GraphQLSettings> 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)
{
Expand Down Expand Up @@ -89,19 +91,17 @@ private async Task ExecuteAsync(HttpContext context)

if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType))
{

using var sr = new StreamReader(context.Request.Body);
if (mediaType.IsSubsetOf(_graphQlMediaType))
{
using var sr = new StreamReader(context.Request.Body);

request = new GraphQLNamedQueryRequest
{
Query = await sr.ReadToEndAsync()
};
}
else
{
request = await JsonSerializer.DeserializeAsync<GraphQLNamedQueryRequest>(context.Request.Body, JOptions.CamelCase);
request = _graphQLTextSerializer.Deserialize<GraphQLNamedQueryRequest>(await sr.ReadToEndAsync());
}
}
else
Expand Down Expand Up @@ -171,7 +171,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 CreateRequestFromQueryString(HttpContext context, bool validateQueryKey = false)
{
if (!context.Request.Query.ContainsKey("query"))
{
Expand All @@ -190,7 +190,7 @@ private static GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext

if (context.Request.Query.ContainsKey("variables"))
{
request.Variables = JsonSerializer.Deserialize<Inputs>(context.Request.Query["variables"], JOptions.CamelCase);
request.Variables = _graphQLTextSerializer.Deserialize<Inputs>(context.Request.Query["variables"]);
}

if (context.Request.Query.ContainsKey("operationName"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using GraphQL;

namespace OrchardCore.Apis.GraphQL.Json;

public class GraphQLNamedQueryRequestJsonConverter : JsonConverter<GraphQLNamedQueryRequest>
{
public static readonly GraphQLNamedQueryRequestJsonConverter Instance = new();

/// <summary>
/// Name for the operation name parameter.
/// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters
/// </summary>
private const string _operationNameKey = "operationName";

/// <summary>
/// Name for the query parameter.
/// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters
/// </summary>
private const string _queryKey = "query";

/// <summary>
/// Name for the variables parameter.
/// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters
/// </summary>
private const string _variablesKey = "variables";

/// <summary>
/// Name for the extensions parameter.
/// See https://github.com/graphql/graphql-over-http/blob/master/spec/GraphQLOverHTTP.md#request-parameters
/// </summary>
private const string _extensionsKey = "extensions";

private const string _namedQueryKey = "namedQuery";


public override void Write(Utf8JsonWriter writer, GraphQLNamedQueryRequest value, JsonSerializerOptions options)
{
writer.WriteStartObject();
if (value.Query != null)
{
writer.WritePropertyName(_queryKey);
writer.WriteStringValue(value.Query);
}
if (value.OperationName != null)
{
writer.WritePropertyName(_operationNameKey);
writer.WriteStringValue(value.OperationName);
}
if (value.Variables != null)
{
writer.WritePropertyName(_variablesKey);
JsonSerializer.Serialize(writer, value.Variables, options);
}
if (value.Extensions != null)
{
writer.WritePropertyName(_extensionsKey);
JsonSerializer.Serialize(writer, value.Extensions, options);
}
if (value.NamedQuery != null)
{
writer.WritePropertyName(_namedQueryKey);
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 _queryKey:
request.Query = reader.GetString()!;
break;
case _operationNameKey:
request.OperationName = reader.GetString()!;
break;
case _namedQueryKey:
request.NamedQuery = reader.GetString();
break;
case _variablesKey:
request.Variables = JsonSerializer.Deserialize<Inputs>(ref reader, options);
break;
case _extensionsKey:
request.Extensions = JsonSerializer.Deserialize<Inputs>(ref reader, options);
break;
default:
reader.Skip();
break;
}
}

//unexpected end of data
throw new JsonException();
}
}
18 changes: 17 additions & 1 deletion src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
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;
using OrchardCore.Extensions;
using OrchardCore.Json;
using OrchardCore.Json.Extensions;
using OrchardCore.Modules;
using OrchardCore.Navigation;
using OrchardCore.Security.Permissions;
Expand Down Expand Up @@ -46,7 +50,19 @@ public override void ConfigureServices(IServiceCollection services)
services.AddScoped<IPermissionProvider, Permissions>();
services.AddTransient<INavigationProvider, AdminMenu>();
services.AddSingleton<GraphQLMiddleware>();
services.AddGraphQL(builder => builder.AddSystemTextJson());
services.Configure<ContentSerializerJsonOptionsConfiguration>(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<IOptions<ContentSerializerJsonOptions>>().Value;
options.Merge(contentSerializerJsonOptions.SerializerOptions);
}));

services.AddOptions<GraphQLSettings>().Configure<IShellConfiguration>((c, configuration) =>
{
Expand Down
Loading