From b5c62b2bcd95b92607399b29946fb32684826bef Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 6 Aug 2019 21:17:52 +0200 Subject: [PATCH] Added support for middleware on introspection fields --- .../Execution/IntrospectionTests.cs | 32 +++++++++++++++++++ ...reHasAnEffectOnIntrospectIfSwitchedOn.snap | 9 ++++++ .../Contracts/FieldMiddlewareApplication.cs | 21 ++++++++++++ .../Contracts/IReadOnlySchemaOptions.cs | 5 +++ .../Configuration/Contracts/ISchemaOptions.cs | 2 ++ .../Configuration/ReadOnlySchemaOptions.cs | 3 ++ src/Core/Types/Configuration/SchemaOptions.cs | 9 +++++- .../Types/Helpers/FieldMiddlewareCompiler.cs | 4 +-- src/Core/Types/Types/ObjectField.cs | 9 +++++- 9 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/Core/Core.Tests/Execution/__snapshots__/IntrospectionTests.FieldMiddlewareHasAnEffectOnIntrospectIfSwitchedOn.snap create mode 100644 src/Core/Types/Configuration/Contracts/FieldMiddlewareApplication.cs diff --git a/src/Core/Core.Tests/Execution/IntrospectionTests.cs b/src/Core/Core.Tests/Execution/IntrospectionTests.cs index 7dba87730cf..d881843eaa2 100644 --- a/src/Core/Core.Tests/Execution/IntrospectionTests.cs +++ b/src/Core/Core.Tests/Execution/IntrospectionTests.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using ChilliCream.Testing; +using HotChocolate.Configuration; using HotChocolate.Types; using Snapshooter.Xunit; using Xunit; @@ -117,6 +118,37 @@ public async Task FieldMiddlewareDoesNotHaveAnEffectOnIntrospection() result.MatchSnapshot(); } + [Fact] + public async Task FieldMiddlewareHasAnEffectOnIntrospectIfSwitchedOn() + { + // arrange + string query = "{ __typename a }"; + + ISchema schema = SchemaBuilder.New() + .AddQueryType() + .Use(next => async context => + { + await next.Invoke(context); + + if (context.Result is string s) + { + context.Result = s.ToUpperInvariant(); + } + }) + .ModifyOptions(o => + o.FieldMiddleware = FieldMiddlewareApplication.AllFields) + .Create(); + + IQueryExecutor executor = schema.MakeExecutable(); + + // act + IExecutionResult result = await executor.ExecuteAsync(query); + + // assert + Assert.Empty(result.Errors); + result.MatchSnapshot(); + } + [Fact] public async Task DirectiveMiddlewareDoesWorkOnIntrospection() { diff --git a/src/Core/Core.Tests/Execution/__snapshots__/IntrospectionTests.FieldMiddlewareHasAnEffectOnIntrospectIfSwitchedOn.snap b/src/Core/Core.Tests/Execution/__snapshots__/IntrospectionTests.FieldMiddlewareHasAnEffectOnIntrospectIfSwitchedOn.snap new file mode 100644 index 00000000000..bd336a4341e --- /dev/null +++ b/src/Core/Core.Tests/Execution/__snapshots__/IntrospectionTests.FieldMiddlewareHasAnEffectOnIntrospectIfSwitchedOn.snap @@ -0,0 +1,9 @@ +{ + "Data": { + "__typename": "QUERY", + "a": "A" + }, + "Extensions": {}, + "Errors": [], + "ContextData": {} +} diff --git a/src/Core/Types/Configuration/Contracts/FieldMiddlewareApplication.cs b/src/Core/Types/Configuration/Contracts/FieldMiddlewareApplication.cs new file mode 100644 index 00000000000..5e0ee4a51e2 --- /dev/null +++ b/src/Core/Types/Configuration/Contracts/FieldMiddlewareApplication.cs @@ -0,0 +1,21 @@ +namespace HotChocolate.Configuration +{ + /// + /// This enum specified on which fields custom field + /// middleware is applied to. + /// + public enum FieldMiddlewareApplication : byte + { + /// + /// Custom field middleware is only applied to + /// user-defined fields and not to introspection fields. + /// + UserDefinedFields = 0, + + /// + /// Custom field middleware is applied to all fields + /// (user-defined fields and introspection fields). + /// + AllFields = 1 + } +} diff --git a/src/Core/Types/Configuration/Contracts/IReadOnlySchemaOptions.cs b/src/Core/Types/Configuration/Contracts/IReadOnlySchemaOptions.cs index 8e42eae987a..02b76e5a4eb 100644 --- a/src/Core/Types/Configuration/Contracts/IReadOnlySchemaOptions.cs +++ b/src/Core/Types/Configuration/Contracts/IReadOnlySchemaOptions.cs @@ -11,5 +11,10 @@ public interface IReadOnlySchemaOptions bool StrictValidation { get; } bool UseXmlDocumentation { get; } + + /// + /// Defines on which fields a middleware pipeline can be applied on. + /// + FieldMiddlewareApplication FieldMiddleware { get; } } } diff --git a/src/Core/Types/Configuration/Contracts/ISchemaOptions.cs b/src/Core/Types/Configuration/Contracts/ISchemaOptions.cs index 9ad7fe44089..a3a7ab7ae9c 100644 --- a/src/Core/Types/Configuration/Contracts/ISchemaOptions.cs +++ b/src/Core/Types/Configuration/Contracts/ISchemaOptions.cs @@ -12,5 +12,7 @@ public interface ISchemaOptions new bool StrictValidation { get; set; } new bool UseXmlDocumentation { get; set; } + + new FieldMiddlewareApplication FieldMiddleware { get; set; } } } diff --git a/src/Core/Types/Configuration/ReadOnlySchemaOptions.cs b/src/Core/Types/Configuration/ReadOnlySchemaOptions.cs index 3432e64f9c3..9bd795780b1 100644 --- a/src/Core/Types/Configuration/ReadOnlySchemaOptions.cs +++ b/src/Core/Types/Configuration/ReadOnlySchemaOptions.cs @@ -20,6 +20,7 @@ public ReadOnlySchemaOptions(IReadOnlySchemaOptions options) ?? "Subscription"; StrictValidation = options.StrictValidation; UseXmlDocumentation = options.UseXmlDocumentation; + FieldMiddleware = options.FieldMiddleware; } public string QueryTypeName { get; } @@ -31,5 +32,7 @@ public ReadOnlySchemaOptions(IReadOnlySchemaOptions options) public bool StrictValidation { get; } public bool UseXmlDocumentation { get; } + + public FieldMiddlewareApplication FieldMiddleware { get; } } } diff --git a/src/Core/Types/Configuration/SchemaOptions.cs b/src/Core/Types/Configuration/SchemaOptions.cs index 0733ba31b19..a7f48fff156 100644 --- a/src/Core/Types/Configuration/SchemaOptions.cs +++ b/src/Core/Types/Configuration/SchemaOptions.cs @@ -13,6 +13,12 @@ public class SchemaOptions public bool UseXmlDocumentation { get; set; } = true; + public FieldMiddlewareApplication FieldMiddleware + { + get; + set; + } = FieldMiddlewareApplication.UserDefinedFields; + public static SchemaOptions FromOptions(IReadOnlySchemaOptions options) { return new SchemaOptions @@ -21,7 +27,8 @@ public static SchemaOptions FromOptions(IReadOnlySchemaOptions options) MutationTypeName = options.MutationTypeName, SubscriptionTypeName = options.SubscriptionTypeName, StrictValidation = options.StrictValidation, - UseXmlDocumentation = options.UseXmlDocumentation + UseXmlDocumentation = options.UseXmlDocumentation, + FieldMiddleware = options.FieldMiddleware }; } } diff --git a/src/Core/Types/Types/Helpers/FieldMiddlewareCompiler.cs b/src/Core/Types/Types/Helpers/FieldMiddlewareCompiler.cs index 1651666f740..84d39c74dbd 100644 --- a/src/Core/Types/Types/Helpers/FieldMiddlewareCompiler.cs +++ b/src/Core/Types/Types/Helpers/FieldMiddlewareCompiler.cs @@ -11,7 +11,7 @@ public static FieldDelegate Compile( IReadOnlyList globalComponents, IReadOnlyList fieldComponents, FieldResolverDelegate fieldResolver, - bool isIntrospection) + bool skipMiddleware) { if (globalComponents == null) { @@ -23,7 +23,7 @@ public static FieldDelegate Compile( throw new ArgumentNullException(nameof(fieldComponents)); } - if (isIntrospection + if (skipMiddleware || (globalComponents.Count == 0 && fieldComponents.Count == 0)) { diff --git a/src/Core/Types/Types/ObjectField.cs b/src/Core/Types/Types/ObjectField.cs index f216ff42be8..c687c49dca0 100644 --- a/src/Core/Types/Types/ObjectField.cs +++ b/src/Core/Types/Types/ObjectField.cs @@ -110,11 +110,18 @@ private void CompleteResolver( context.Type.Name, Resolver, resolver); } + IReadOnlySchemaOptions options = context.DescriptorContext.Options; + + bool skipMiddleware = + options.FieldMiddleware == FieldMiddlewareApplication.AllFields + ? false + : isIntrospectionField; + Middleware = FieldMiddlewareCompiler.Compile( context.GlobalComponents, definition.MiddlewareComponents.ToArray(), Resolver, - isIntrospectionField); + skipMiddleware); if (Resolver == null && Middleware == null) {