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

Update graphql.net to 4.6.1 #10782

Merged
merged 15 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
Expand Down Expand Up @@ -28,19 +29,21 @@ public class GraphQLMiddleware
private readonly RequestDelegate _next;
private readonly GraphQLSettings _settings;
private readonly IDocumentExecuter _executer;

private readonly IDocumentWriter _writer;
carlwoodhouse marked this conversation as resolved.
Show resolved Hide resolved
internal static readonly Encoding _utf8Encoding = new UTF8Encoding(false);
private readonly static MediaType _jsonMediaType = new MediaType("application/json");
private readonly static MediaType _graphQlMediaType = new MediaType("application/graphql");

public GraphQLMiddleware(
RequestDelegate next,
GraphQLSettings settings,
IDocumentExecuter executer)
IDocumentExecuter executer,
IDocumentWriter writer)
{
_next = next;
_settings = settings;
_executer = executer;
_writer = writer;
}

public async Task Invoke(HttpContext context, IAuthorizationService authorizationService, IAuthenticationService authenticationService, ISchemaFactory schemaService, IDocumentWriter documentWriter)
Expand Down Expand Up @@ -149,9 +152,8 @@ private async Task ExecuteAsync(HttpContext context, ISchemaFactory schemaServic
_.Schema = schema;
_.Query = queryToExecute;
_.OperationName = request.OperationName;
_.Inputs = request.Variables.ToInputs();
_.Inputs = ToInputs(request.Variables);
carlwoodhouse marked this conversation as resolved.
Show resolved Hide resolved
_.UserContext = _settings.BuildUserContext?.Invoke(context);
_.RequestServices = context.RequestServices;
_.ValidationRules = DocumentValidator.CoreRules
.Concat(context.RequestServices.GetServices<IValidationRule>());
_.ComplexityConfiguration = new ComplexityConfiguration
Expand All @@ -161,16 +163,21 @@ private async Task ExecuteAsync(HttpContext context, ISchemaFactory schemaServic
FieldImpact = _settings.FieldImpact
};
_.Listeners.Add(dataLoaderDocumentListener);
_.RequestServices = context.RequestServices;
_.ThrowOnUnhandledException = true;
carlwoodhouse marked this conversation as resolved.
Show resolved Hide resolved
});

context.Response.StatusCode = (int)(result.Errors == null || result.Errors.Count == 0
? HttpStatusCode.OK
: result.Errors.Any(x => x.Code == RequiresPermissionValidationRule.ErrorCode)
: result.Errors.Any(x => (x is ValidationError && (x as ValidationError).Number == RequiresPermissionValidationRule.ErrorCode))
MikeKry marked this conversation as resolved.
Show resolved Hide resolved
? HttpStatusCode.Unauthorized
: HttpStatusCode.BadRequest);

context.Response.ContentType = MediaTypeNames.Application.Json;
await documentWriter.WriteAsync(context.Response.Body, result);

// changed in V4
var encodedBytes = _utf8Encoding.GetBytes(await _writer.WriteToStringAsync(result));
await context.Response.Body.WriteAsync(encodedBytes, 0, encodedBytes.Length); // documentWriter causes problems when querying _schema
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you elaborate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentWriter.WriteAsync throws error when grahpql tries to get schema
"System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead."

I did not find the cause and using workaround with AllowSynchronousIO does not seem ok to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does ring a bell ... is this an issue in the ag/graphql4 branch currently then ? i have no idea why i changed it .... yours looks closer to the original tbh

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs more thoughts, will buffer all responses, and the big ones could be an issue in terms of perf. Maybe at least use an ArrayPool?

}

private static GraphQLRequest CreateRequestFromQueryString(HttpContext context, bool validateQueryKey = false)
Expand Down Expand Up @@ -202,5 +209,62 @@ private static GraphQLRequest CreateRequestFromQueryString(HttpContext context,

return request;
}

/// <summary>
/// Copied from GraphQL.NET 2.4
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static Inputs ToInputs(JObject obj)
{
var variables = GetValue(obj) as Dictionary<string, object>
?? new Dictionary<string, object>();
return new Inputs(variables);
}

public static object GetValue(object value)
{
if (value is JObject objectValue)
{
var output = new Dictionary<string, object>();
foreach (var kvp in objectValue)
{
output.Add(kvp.Key, GetValue(kvp.Value));
}
return output;
}

if (value is JProperty propertyValue)
{
return new Dictionary<string, object>
{
{ propertyValue.Name, GetValue(propertyValue.Value) }
};
}

if (value is JArray arrayValue)
{
return arrayValue.Children().Aggregate(new List<object>(), (list, token) =>
{
list.Add(GetValue(token));
return list;
});
}

if (value is JValue rawValue)
{
var val = rawValue.Value;
if (val is long l)
{
if (l >= int.MinValue && l <= int.MaxValue)
{
return (int)l;
}
}
return val;
}

return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ public class OrchardFieldNameConverter : INameConverter
{
private readonly INameConverter _defaultConverter = new CamelCaseNameConverter();

public string NameForArgument(string fieldName, IComplexGraphType parentGraphType, FieldType field)
// todo: custom argument name?
public string NameForArgument(string argumentName, IComplexGraphType parentGraphType, FieldType field)
{
return _defaultConverter.NameForArgument(fieldName, parentGraphType, field);
return _defaultConverter.NameForArgument(argumentName, parentGraphType, field);
}

// TODO: check functionality
public string NameForField(string fieldName, IComplexGraphType parentGraphType)
{
var attributes = parentGraphType?.GetType().GetCustomAttributes(typeof(GraphQLFieldNameAttribute), true);
Expand All @@ -22,7 +24,6 @@ public string NameForField(string fieldName, IComplexGraphType parentGraphType)
{
foreach (GraphQLFieldNameAttribute attribute in attributes)
{

if (attribute.Field == fieldName)
{
return attribute.Mapped;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ namespace OrchardCore.Apis.GraphQL.Services
public class SchemaService : ISchemaFactory
{
private readonly IEnumerable<ISchemaBuilder> _schemaBuilders;
private readonly IServiceProvider _serviceProvider;
private readonly SemaphoreSlim _schemaGenerationSemaphore = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<ISchemaBuilder, string> _identifiers = new ConcurrentDictionary<ISchemaBuilder, string>();

private ISchema _schema;

public SchemaService(IEnumerable<ISchemaBuilder> schemaBuilders)
public SchemaService(IEnumerable<ISchemaBuilder> schemaBuilders, IServiceProvider serviceProvider)
{
_schemaBuilders = schemaBuilders;
_serviceProvider = serviceProvider;
}

public async Task<ISchema> GetSchemaAsync()
Expand Down Expand Up @@ -63,7 +65,7 @@ public async Task<ISchema> GetSchemaAsync()

var serviceProvider = ShellScope.Services;

var schema = new Schema(new SelfActivatingServiceProvider(serviceProvider))
var schema = new Schema(new SelfActivatingServiceProvider(_serviceProvider))
{
Query = new ObjectGraphType { Name = "Query" },
Mutation = new ObjectGraphType { Name = "Mutation" },
Expand Down
12 changes: 7 additions & 5 deletions src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using GraphQL.DataLoader;
using GraphQL.Execution;
using GraphQL.NewtonsoftJson;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
Expand Down Expand Up @@ -36,20 +37,21 @@ public Startup(IOptions<AdminOptions> adminOptions, IHostEnvironment hostingEnvi

public override void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
services.AddSingleton<IDocumentWriter, DocumentWriter>();
services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
carlwoodhouse marked this conversation as resolved.
Show resolved Hide resolved
services.AddSingleton<IDataLoaderContextAccessor, DataLoaderContextAccessor>();
services.AddSingleton<IDocumentExecutionListener, DataLoaderDocumentListener>();

services.AddSingleton<ISchemaFactory, SchemaService>();
services.AddScoped<IValidationRule, MaxNumberOfResultsValidationRule>();
services.AddScoped<IValidationRule, RequiresPermissionValidationRule>();

services.AddSingleton<IErrorInfoProvider>(services =>
{
var settings = services.GetRequiredService<IOptions<GraphQLSettings>>();
return new ErrorInfoProvider(new ErrorInfoProviderOptions { ExposeExceptionStackTrace = settings.Value.ExposeExceptions });
});

services.AddSingleton<ISchemaFactory, SchemaService>();
services.AddScoped<IValidationRule, MaxNumberOfResultsValidationRule>();
services.AddScoped<IValidationRule, RequiresPermissionValidationRule>();

services.AddScoped<IPermissionProvider, Permissions>();
services.AddTransient<INavigationProvider, AdminMenu>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,67 +12,61 @@ public class MaxNumberOfResultsValidationRule : IValidationRule
{
private readonly int _maxNumberOfResults;
private readonly MaxNumberOfResultsValidationMode _maxNumberOfResultsValidationMode;
private readonly IStringLocalizer<MaxNumberOfResultsValidationRule> _localizer;
private readonly ILogger<MaxNumberOfResultsValidationRule> _logger;

public MaxNumberOfResultsValidationRule(IOptions<GraphQLSettings> options)
public MaxNumberOfResultsValidationRule(IOptions<GraphQLSettings> options, IStringLocalizer<MaxNumberOfResultsValidationRule> localizer, ILogger<MaxNumberOfResultsValidationRule> logger)
{
var settings = options.Value;
_maxNumberOfResults = settings.MaxNumberOfResults;
_maxNumberOfResultsValidationMode = settings.MaxNumberOfResultsValidationMode;
_localizer = localizer;
_logger = logger;
}

public Task<INodeVisitor> ValidateAsync(ValidationContext validationContext)
{
// Todo: EnterLeaveListener has been removed and the signatures of INodeVisitor.Enter and INodeVisitor.Leave have changed. NodeVisitors class has been added in its place.
// https://graphql-dotnet.github.io/docs/migrations/migration4/
// Ex: https://github.com/graphql-dotnet/graphql-dotnet/issues/2406
INodeVisitor result = new NodeVisitors();
return Task.FromResult(result);
return Task.FromResult((INodeVisitor)new NodeVisitors(
new MatchingNodeVisitor<Argument>((arg, visitorContext) =>
{
if ((arg.Name == "first" || arg.Name == "last") && arg.Value != null)
{
var context = (GraphQLUserContext)validationContext.UserContext;

//return new EnterLeaveListener(_ =>
//{
// _.Match<Argument>(arg =>
// {
// if ((arg.Name == "first" || arg.Name == "last") && arg.Value != null)
// {
// var context = (GraphQLUserContext)validationContext.UserContext;
int? value = null;

// int? value = null;
if (arg.Value is IntValue)
{
value = ((IntValue)arg.Value)?.Value;
}
else
{
if (validationContext.Inputs.TryGetValue(arg.Value.ToString(), out var input))
{
value = (int?)input;
}
}

// if (arg.Value is IntValue)
// {
// value = ((IntValue)arg.Value)?.Value;
// }
// else
// {
// if (validationContext.Inputs.TryGetValue(arg.Value.ToString(), out var input))
// {
// value = (int?)input;
// }
// }
if (value.HasValue && value > _maxNumberOfResults)
{
var errorMessage = _localizer["'{0}' exceeds the maximum number of results for '{1}' ({2})", value.Value, arg.Name, _maxNumberOfResults];

// if (value.HasValue && value > _maxNumberOfResults)
// {
// var localizer = context.ServiceProvider.GetService<IStringLocalizer<MaxNumberOfResultsValidationRule>>();
// var errorMessage = localizer["'{0}' exceeds the maximum number of results for '{1}' ({2})", value.Value, arg.Name, _maxNumberOfResults];

// if (_maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Enabled)
// {
// validationContext.ReportError(new ValidationError(
// validationContext.OriginalQuery,
// "ArgumentInputError",
// errorMessage,
// arg));
// }
// else
// {
// var logger = context.ServiceProvider.GetService<ILogger<MaxNumberOfResultsValidationMode>>();
// logger.LogInformation(errorMessage);
// arg.Value = new IntValue(_maxNumberOfResults); // if disabled mode we just log info and override the arg to be maxvalue
// }
// }
// }
// });
//});
if (_maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Enabled)
{
validationContext.ReportError(new ValidationError(
validationContext.Document.OriginalQuery,
"ArgumentInputError",
errorMessage,
arg));
}
else
{
_logger.LogInformation(errorMessage);
arg = new Argument(arg.NameNode, new IntValue(_maxNumberOfResults)); // if disabled mode we just log info and override the arg to be maxvalue
}
}
}
})));
}
}
}
Loading