diff --git a/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/DataLoaderWithEFCore.csproj b/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/DataLoaderWithEFCore.csproj index 2521394..d6489bd 100644 --- a/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/DataLoaderWithEFCore.csproj +++ b/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/DataLoaderWithEFCore.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -15,8 +15,8 @@ - - + + diff --git a/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Actor.cs b/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Actor.cs index af30656..da64ab6 100644 --- a/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Actor.cs +++ b/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Actor.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using AutoMapper; using DataLoaderWithEFCore.Data.Repositories; +using GraphQL; using GraphQL.Conventions; using GraphQL.DataLoader; using Models = DataLoaderWithEFCore.Data.Models; diff --git a/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Movie.cs b/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Movie.cs index 8009f02..d42b82b 100644 --- a/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Movie.cs +++ b/samples/DataLoaderWithEFCore/DataLoaderWithEFCore/GraphApi/Schema/OutputTypes/Movie.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using AutoMapper; using DataLoaderWithEFCore.Data.Repositories; +using GraphQL; using GraphQL.Conventions; using GraphQL.DataLoader; using Models = DataLoaderWithEFCore.Data.Models; diff --git a/samples/SubscriptionsGraphQLServer/SubscriptionExample/SubscriptionExample/SubscriptionExample.csproj b/samples/SubscriptionsGraphQLServer/SubscriptionExample/SubscriptionExample/SubscriptionExample.csproj index 528a10d..9f8258d 100644 --- a/samples/SubscriptionsGraphQLServer/SubscriptionExample/SubscriptionExample/SubscriptionExample.csproj +++ b/samples/SubscriptionsGraphQLServer/SubscriptionExample/SubscriptionExample/SubscriptionExample.csproj @@ -9,10 +9,10 @@ - - - - + + + + diff --git a/src/GraphQL.Conventions/Adapters/Engine/GraphQLEngine.cs b/src/GraphQL.Conventions/Adapters/Engine/GraphQLEngine.cs index 06094ee..450cf88 100644 --- a/src/GraphQL.Conventions/Adapters/Engine/GraphQLEngine.cs +++ b/src/GraphQL.Conventions/Adapters/Engine/GraphQLEngine.cs @@ -18,7 +18,6 @@ using GraphQL.Validation; using GraphQL.Validation.Complexity; using GraphQL.Validation.Rules.Custom; -using GraphQLParser.AST; // ReSharper disable once CheckNamespace namespace GraphQL.Conventions @@ -39,7 +38,7 @@ public class GraphQLEngine private readonly GraphQLSerializer _documentSerializer = new GraphQLSerializer(); - private SchemaPrinter _schemaPrinter; + private PrintOptions _printOptions; private ISchema _schema; @@ -56,24 +55,8 @@ public class GraphQLEngine private bool _includeFieldDeprecationReasons; - private class NoopValidationRule : IValidationRule + private class NoopValidationRule : ValidationRuleBase { - public ValueTask ValidateAsync(ValidationContext context) => new(new NoopNodeVisitor()); - } - - private class NoopNodeVisitor : INodeVisitor - { - public ValueTask EnterAsync(ASTNode node, ValidationContext context) - { - /* Noop */ - return default; - } - - public ValueTask LeaveAsync(ASTNode node, ValidationContext context) - { - /* Noop */ - return default; - } } private class WrappedDependencyInjector : IDependencyInjector @@ -234,7 +217,7 @@ public GraphQLEngine BuildSchema(params Type[] types) return BuildSchema(null, types); } - public GraphQLEngine BuildSchema(SchemaPrinterOptions options, params Type[] types) + public GraphQLEngine BuildSchema(PrintOptions options, params Type[] types) { if (_schema != null) return this; @@ -244,20 +227,19 @@ public GraphQLEngine BuildSchema(SchemaPrinterOptions options, params Type[] typ } lock (_schemaLock) _schema = _constructor.Build(_schemaTypes.ToArray()); - _schemaPrinter = new SchemaPrinter(_schema, options ?? new SchemaPrinterOptions + _printOptions = options ?? new PrintOptions() { IncludeDescriptions = _includeFieldDescriptions, IncludeDeprecationReasons = _includeFieldDeprecationReasons, - }); + StringComparison = StringComparison.InvariantCultureIgnoreCase, + }; return this; } - public string Describe(Func ctor = null) + public string Describe(PrintOptions printOptions = null) { BuildSchema(); // Ensure that the schema has been constructed - if (ctor != null) - _schemaPrinter = ctor(_schema); - return _schemaPrinter.Print(); + return _schema.Print(_printOptions = printOptions ?? _printOptions); } public ISchema GetSchema() @@ -288,7 +270,8 @@ internal async Task ExecuteAsync( Inputs variables, IUserContext userContext, IDependencyInjector dependencyInjector, - ComplexityConfiguration complexityConfiguration, + LegacyComplexityConfiguration complexityConfiguration, + ComplexityOptions complexityOptions, bool enableValidation = true, bool enableProfiling = false, IEnumerable rules = null, @@ -309,7 +292,11 @@ internal async Task ExecuteAsync( if (complexityConfiguration != null) { - rules = rules.Append(new ComplexityValidationRule(complexityConfiguration)); + rules = rules.Append(new LegacyComplexityValidationRule(complexityConfiguration)); + } + if (complexityOptions != null) + { + rules = rules.Append(new ComplexityValidationRule(complexityOptions)); } var configuration = new ExecutionOptions @@ -370,7 +357,7 @@ internal async Task ValidateAsync(string queryString) Document = document }); - return result.validationResult; + return result; } private object CreateInstance(Type type) diff --git a/src/GraphQL.Conventions/Adapters/Engine/GraphQLExecutor.cs b/src/GraphQL.Conventions/Adapters/Engine/GraphQLExecutor.cs index 4b1be4d..bb271ec 100644 --- a/src/GraphQL.Conventions/Adapters/Engine/GraphQLExecutor.cs +++ b/src/GraphQL.Conventions/Adapters/Engine/GraphQLExecutor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -34,7 +35,8 @@ public class GraphQLExecutor : IGraphQLExecutor private IEnumerable _validationRules; - private ComplexityConfiguration _complexityConfiguration; + private LegacyComplexityConfiguration _complexityConfiguration; + private ComplexityOptions _complexityOptions; private IEnumerable _documentExecutionListeners; @@ -117,12 +119,19 @@ public IGraphQLExecutor EnableValidation(bool enableValidation return this; } - public IGraphQLExecutor WithComplexityConfiguration(ComplexityConfiguration complexityConfiguration) + [Obsolete("Please use the WithComplexityOptions method instead.")] + public IGraphQLExecutor WithComplexityConfiguration(LegacyComplexityConfiguration complexityConfiguration) { _complexityConfiguration = complexityConfiguration; return this; } + public IGraphQLExecutor WithComplexityOptions(ComplexityOptions complexityOptions) + { + _complexityOptions = complexityOptions; + return this; + } + public IGraphQLExecutor DisableValidation() { return EnableValidation(false); @@ -146,6 +155,7 @@ public Task ExecuteAsync() enableProfiling: _enableProfiling, rules: _validationRules, complexityConfiguration: _complexityConfiguration, + complexityOptions: _complexityOptions, cancellationToken: _cancellationToken, listeners: _documentExecutionListeners); diff --git a/src/GraphQL.Conventions/Adapters/Engine/IGraphQLExecutor.cs b/src/GraphQL.Conventions/Adapters/Engine/IGraphQLExecutor.cs index 3f40d12..9b0db0b 100644 --- a/src/GraphQL.Conventions/Adapters/Engine/IGraphQLExecutor.cs +++ b/src/GraphQL.Conventions/Adapters/Engine/IGraphQLExecutor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -30,7 +31,10 @@ public interface IGraphQLExecutor IGraphQLExecutor WithValidationRules(IEnumerable rules); - IGraphQLExecutor WithComplexityConfiguration(ComplexityConfiguration complexityConfiguration); + [Obsolete("Please use the WithComplexityOptions method instead.")] + IGraphQLExecutor WithComplexityConfiguration(LegacyComplexityConfiguration complexityConfiguration); + + IGraphQLExecutor WithComplexityOptions(ComplexityOptions complexityOptions); IGraphQLExecutor WithListeners(params IDocumentExecutionListener[] listeners); diff --git a/src/GraphQL.Conventions/Adapters/Types/IdGraphType.cs b/src/GraphQL.Conventions/Adapters/Types/IdGraphType.cs index 6757733..9643551 100644 --- a/src/GraphQL.Conventions/Adapters/Types/IdGraphType.cs +++ b/src/GraphQL.Conventions/Adapters/Types/IdGraphType.cs @@ -11,5 +11,8 @@ public override object ParseValue(object value) } return new Id(value.ToString()); } + + public override object Serialize(object value) + => value is Id idValue ? idValue.ToString() : base.Serialize(value); } } diff --git a/src/GraphQL.Conventions/GraphQL.Conventions.csproj b/src/GraphQL.Conventions/GraphQL.Conventions.csproj index 37e9307..3dbd958 100644 --- a/src/GraphQL.Conventions/GraphQL.Conventions.csproj +++ b/src/GraphQL.Conventions/GraphQL.Conventions.csproj @@ -27,8 +27,9 @@ - - + + + diff --git a/src/GraphQL.Conventions/Web/RequestHandler.cs b/src/GraphQL.Conventions/Web/RequestHandler.cs index c2b1224..04562b6 100644 --- a/src/GraphQL.Conventions/Web/RequestHandler.cs +++ b/src/GraphQL.Conventions/Web/RequestHandler.cs @@ -31,7 +31,8 @@ public class RequestHandlerBuilder : IDependencyInjector private bool _useValidation = true; private bool _useProfiling; private FieldResolutionStrategy _fieldResolutionStrategy = FieldResolutionStrategy.Normal; - private ComplexityConfiguration _complexityConfiguration; + private LegacyComplexityConfiguration _complexityConfiguration; + private ComplexityOptions _complexityOptions; internal RequestHandlerBuilder() { @@ -139,12 +140,19 @@ public RequestHandlerBuilder WithFieldResolutionStrategy(FieldResolutionStrategy return this; } - public RequestHandlerBuilder WithComplexityConfiguration(ComplexityConfiguration complexityConfiguration) + [Obsolete("Please use the WithComplexityOptions method instead.")] + public RequestHandlerBuilder WithComplexityConfiguration(LegacyComplexityConfiguration complexityConfiguration) { _complexityConfiguration = complexityConfiguration; return this; } + public RequestHandlerBuilder WithComplexityOptions(ComplexityOptions complexityOptions) + { + _complexityOptions = complexityOptions; + return this; + } + public RequestHandlerBuilder WithMiddleware() { _middleware.Add(typeof(T)); @@ -168,6 +176,7 @@ public IRequestHandler Generate() _useProfiling, _fieldResolutionStrategy, _complexityConfiguration, + _complexityOptions, _middleware, _typeResolver); } @@ -185,7 +194,8 @@ private class RequestHandlerImpl : IRequestHandler private readonly List _exceptionsTreatedAsWarnings = new List(); private readonly bool _useValidation; private readonly bool _useProfiling; - private readonly ComplexityConfiguration _complexityConfiguration; + private readonly LegacyComplexityConfiguration _complexityConfiguration; + private readonly ComplexityOptions _complexityOptions; internal RequestHandlerImpl( IDependencyInjector dependencyInjector, @@ -195,7 +205,8 @@ internal RequestHandlerImpl( bool useValidation, bool useProfiling, FieldResolutionStrategy fieldResolutionStrategy, - ComplexityConfiguration complexityConfiguration, + LegacyComplexityConfiguration complexityConfiguration, + ComplexityOptions complexityOptions, IEnumerable middleware, ITypeResolver typeResolver) { @@ -208,6 +219,7 @@ internal RequestHandlerImpl( _engine.WithFieldResolutionStrategy(fieldResolutionStrategy); _engine.BuildSchema(schemaTypes.ToArray()); _complexityConfiguration = complexityConfiguration; + _complexityOptions = complexityOptions; foreach (var type in middleware) { @@ -227,6 +239,7 @@ public async Task ProcessRequestAsync(Request request, IUserContext us .WithDependencyInjector(dependencyInjector ?? _dependencyInjector) .WithUserContext(userContext) .WithComplexityConfiguration(_complexityConfiguration) + .WithComplexityOptions(_complexityOptions) .EnableValidation(_useValidation) .EnableProfiling(_useProfiling) .ExecuteAsync() diff --git a/test/GraphQL.Conventions.Tests/Adapters/Engine/GraphQLEngineTests.cs b/test/GraphQL.Conventions.Tests/Adapters/Engine/GraphQLEngineTests.cs index eb6c232..8491399 100644 --- a/test/GraphQL.Conventions.Tests/Adapters/Engine/GraphQLEngineTests.cs +++ b/test/GraphQL.Conventions.Tests/Adapters/Engine/GraphQLEngineTests.cs @@ -61,36 +61,44 @@ public void Can_Construct_And_Describe_Polymorphic_Schema() var engine = GraphQLEngine.New(); var schema = engine.Describe(); schema.ShouldEqualWhenReformatted(@" - type Actor { - dateOfBirth: DateTime - firstName: String - lastName: String! - } - type ExtendedVersion implements ISemanticVersion { - branchName: String! - majorVersion: Int! - minorVersion: Int! - revision: Int! - } - interface ISemanticVersion { - majorVersion: Int! - minorVersion: Int! - revision: Int! - } - type Movie { - actors: [Actor] - releaseDate: DateTime - title: String! - } - type Query { - search(searchFor: String!): [SearchResult] - version(branchName: String): ISemanticVersion - } - type SearchResult { - node: SearchResultItem - score: Float! - } - union SearchResultItem = Movie | Actor + type Actor { + dateOfBirth: DateTime + firstName: String + lastName: String! + } + + scalar DateTime + + type ExtendedVersion implements ISemanticVersion { + branchName: String! + majorVersion: Int! + minorVersion: Int! + revision: Int! + } + + interface ISemanticVersion { + majorVersion: Int! + minorVersion: Int! + revision: Int! + } + + type Movie { + actors: [Actor] + releaseDate: DateTime + title: String! + } + + type Query { + search(searchFor: String!): [SearchResult] + version(branchName: String): ISemanticVersion + } + + type SearchResult { + node: SearchResultItem + score: Float! + } + + union SearchResultItem = Actor | Movie "); } @@ -204,14 +212,14 @@ public async Task Can_Register_And_Use_Custom_Json_Scalar_Types() } [Test] - public async Task Can_Run_Simple_Query_Using_ComplexityConfiguration() + public async Task Can_Run_Simple_Query_Using_ComplexityOptions() { var executor = GraphQLEngine .New() .NewExecutor(); var result = await executor - .WithComplexityConfiguration(new ComplexityConfiguration { MaxDepth = 0 }) + .WithComplexityOptions(new ComplexityOptions { MaxDepth = 1 }) .WithQueryString(@" { title @@ -230,7 +238,7 @@ public async Task Cannot_Run_Too_Complex_Query_Using_ComplexityConfiguration() .NewExecutor(); var result = await executor - .WithComplexityConfiguration(new ComplexityConfiguration { MaxDepth = 0 }) + .WithComplexityOptions(new ComplexityOptions { MaxDepth = 1 }) .WithQueryString(@" { title @@ -244,7 +252,7 @@ public async Task Cannot_Run_Too_Complex_Query_Using_ComplexityConfiguration() result.ShouldHaveErrors(1); var error = result.Errors.First().InnerException?.ToString(); - error.ShouldContainWhenReformatted("Query is too nested to execute. Depth is 1 levels, maximum allowed on this endpoint is 0."); + error.ShouldContainWhenReformatted("Query is too nested to execute. Maximum depth is 2 levels; maximum allowed on this endpoint is 1."); } private class BasicQuery diff --git a/test/GraphQL.Conventions.Tests/Attributes/MetaData/FieldTypeMetaDataAttributeTests.cs b/test/GraphQL.Conventions.Tests/Attributes/MetaData/FieldTypeMetaDataAttributeTests.cs index af7f3a1..8a6f9ca 100644 --- a/test/GraphQL.Conventions.Tests/Attributes/MetaData/FieldTypeMetaDataAttributeTests.cs +++ b/test/GraphQL.Conventions.Tests/Attributes/MetaData/FieldTypeMetaDataAttributeTests.cs @@ -136,9 +136,9 @@ public TestCustomAttribute(string permission) public override object Value() => Permission; } - public class TestValidation : IValidationRule + public class TestValidation : ValidationRuleBase { - public ValueTask ValidateAsync(ValidationContext context) + public override ValueTask GetPreNodeVisitorAsync(ValidationContext context) { return new ValueTask(new TestValidationNodeVisitor(context.GetUserContext() as TestUserContext)); } diff --git a/test/GraphQL.Conventions.Tests/Attributes/MetaData/Relay/ImplementViewerAttributeTests.cs b/test/GraphQL.Conventions.Tests/Attributes/MetaData/Relay/ImplementViewerAttributeTests.cs index 93ce09a..41dc014 100644 --- a/test/GraphQL.Conventions.Tests/Attributes/MetaData/Relay/ImplementViewerAttributeTests.cs +++ b/test/GraphQL.Conventions.Tests/Attributes/MetaData/Relay/ImplementViewerAttributeTests.cs @@ -85,24 +85,27 @@ public async Task Can_Use_The_Viewer_Node_For_Multiple_Schemas() public void Can_Generate_Viewers_For_Multiple_Operations() { GetSchemaDefinition(true, true).ShouldEqualWhenReformatted(@" - type Mutation { - doSomething(value: Boolean): Boolean - doSomethingElse(value: Boolean): Boolean - viewer: MutationViewer - } - type MutationViewer { - doSomething(value: Boolean): Boolean - doSomethingElse(value: Boolean): Boolean - } - type Query { - floatToString(value: Float!): String - intToString(value: Int!): String - viewer: QueryViewer - } - type QueryViewer { - floatToString(value: Float!): String - intToString(value: Int!): String - } + type Mutation { + doSomething(value: Boolean): Boolean + doSomethingElse(value: Boolean): Boolean + viewer: MutationViewer + } + + type MutationViewer { + doSomething(value: Boolean): Boolean + doSomethingElse(value: Boolean): Boolean + } + + type Query { + floatToString(value: Float!): String + intToString(value: Int!): String + viewer: QueryViewer + } + + type QueryViewer { + floatToString(value: Float!): String + intToString(value: Int!): String + } "); } diff --git a/test/GraphQL.Conventions.Tests/GraphQL.Conventions.Tests.csproj b/test/GraphQL.Conventions.Tests/GraphQL.Conventions.Tests.csproj index c216351..bdc84f7 100755 --- a/test/GraphQL.Conventions.Tests/GraphQL.Conventions.Tests.csproj +++ b/test/GraphQL.Conventions.Tests/GraphQL.Conventions.Tests.csproj @@ -1,4 +1,4 @@ - + Exe @@ -12,7 +12,7 @@ - + diff --git a/test/GraphQL.Conventions.Tests/Templates/Extensions/TestExtensions.cs b/test/GraphQL.Conventions.Tests/Templates/Extensions/TestExtensions.cs index c6aa85e..a3c678f 100644 --- a/test/GraphQL.Conventions.Tests/Templates/Extensions/TestExtensions.cs +++ b/test/GraphQL.Conventions.Tests/Templates/Extensions/TestExtensions.cs @@ -200,10 +200,20 @@ public static void ShouldHaveFieldWithValue(this object result, params object[] if (k is int intValue) { var node = obj as ArrayExecutionNode; - node.Items.ShouldNotBeNull(); - node.Items.Count.ShouldBeGreaterThan(intValue); - obj = node.Items[intValue]; - obj.ShouldNotBeNull(); + if (node.SerializedResult != null) + { + node.SerializedResult.ShouldNotBeNull(); + node.SerializedResult.Cast().Count().ShouldBeGreaterThan(intValue); + node.SerializedResult.Cast().Skip(intValue).First().ShouldEqual(value); + return; + } + else + { + node.Items.ShouldNotBeNull(); + node.Items.Count.ShouldBeGreaterThan(intValue); + obj = node.Items[intValue]; + obj.ShouldNotBeNull(); + } } else { diff --git a/test/GraphQL.Conventions.Tests/Web/RequestHandlerTests.cs b/test/GraphQL.Conventions.Tests/Web/RequestHandlerTests.cs index f1fa739..fc42557 100644 --- a/test/GraphQL.Conventions.Tests/Web/RequestHandlerTests.cs +++ b/test/GraphQL.Conventions.Tests/Web/RequestHandlerTests.cs @@ -32,13 +32,13 @@ public async Task Can_Run_Query() } [Test] - public async Task Can_Run_Simple_Query_Using_ComplexityConfiguration() + public async Task Can_Run_Simple_Query_Using_ComplexityOptions() { var request = Request.New("{ \"query\": \"{ hello }\" }"); var response = await RequestHandler .New() .WithQuery() - .WithComplexityConfiguration(new ComplexityConfiguration { MaxDepth = 2 }) + .WithComplexityOptions(new ComplexityOptions { MaxDepth = 2 }) .Generate() .ProcessRequestAsync(request, null); @@ -51,18 +51,18 @@ public async Task Can_Run_Simple_Query_Using_ComplexityConfiguration() } [Test] - public async Task Cannot_Run_Too_Complex_Query_Using_ComplexityConfiguration() + public async Task Cannot_Run_Too_Complex_Query_Using_ComplexityOptions() { var request = Request.New("{ \"query\": \"{ sub { sub { end } } }\" }"); var response = await RequestHandler .New() .WithQuery() - .WithComplexityConfiguration(new ComplexityConfiguration { MaxDepth = 1 }) + .WithComplexityOptions(new ComplexityOptions { MaxDepth = 2 }) .Generate() .ProcessRequestAsync(request, null); response.Errors.Count.ShouldEqual(1); - response.Errors[0].Message.ShouldEqual("Query is too nested to execute. Depth is 2 levels, maximum allowed on this endpoint is 1."); + response.Errors[0].Message.ShouldEqual("Query is too nested to execute. Maximum depth is 3 levels; maximum allowed on this endpoint is 2."); } [Test]