From a4b6c26eb410b215678aa190ca144bdacb290618 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sat, 24 Sep 2022 20:49:44 +0200 Subject: [PATCH] Fixed schema error when using static properties as resolver --- .../Properties/TypeResources.Designer.cs | 4 +- .../src/Types/Properties/TypeResources.resx | 2 +- .../Resolvers/DefaultResolverCompiler.cs | 8 +- .../test/Types.Tests/Types/ObjectTypeTests.cs | 73 ++++++++++++++++++- ...ests.Static_Field_Inference_3_Execute.snap | 5 ++ ...ests.Static_Field_Inference_4_Execute.snap | 6 ++ ...TypeTests.Static_Field_Inference_5.graphql | 19 +++++ 7 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_3_Execute.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_4_Execute.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_5.graphql diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs index ba1a29940a5..11c8edf37bf 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs @@ -1263,9 +1263,9 @@ internal static string EventMessageParameterExpressionBuilder_MessageNotFound { } } - internal static string DefaultResolverCompilerService_CreateResolver_ArgumentValudationError { + internal static string DefaultResolverCompilerService_CreateResolver_ArgumentValidationError { get { - return ResourceManager.GetString("DefaultResolverCompilerService_CreateResolver_ArgumentValudationError", resourceCulture); + return ResourceManager.GetString("DefaultResolverCompilerService_CreateResolver_ArgumentValidationError", resourceCulture); } } diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx index 4842acfdefb..eca5de651f3 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx @@ -737,7 +737,7 @@ Type: `{0}` The event message parameter can only be used in a subscription context. - + The public method should already have ensured that we do not have members other than method or property at this point. diff --git a/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs b/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs index b7571b0a9d1..ad9ec7f3c28 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/DefaultResolverCompiler.cs @@ -199,6 +199,10 @@ public FieldResolverDelegates CompileResolve( { resolver = CompileStaticResolver(method, parameterExpressionBuilders ?? _empty); } + else if (member is PropertyInfo { GetMethod: { IsStatic: true } getMethod }) + { + resolver = CompileStaticResolver(getMethod, parameterExpressionBuilders ?? _empty); + } else { resolver = CreateResolver( @@ -334,7 +338,7 @@ private FieldResolverDelegate CreateResolver( } throw new NotSupportedException( - DefaultResolverCompilerService_CreateResolver_ArgumentValudationError); + DefaultResolverCompilerService_CreateResolver_ArgumentValidationError); } private PureFieldDelegate? TryCompilePureResolver( @@ -528,7 +532,7 @@ private IParameterExpressionBuilder GetParameterExpressionBuilder( #if NETSTANDARD _cache[parameter] = builder; #else - _cache.TryAdd(parameter, builder); + _cache.TryAdd(parameter, builder); #endif return builder; } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs index aaf604371ef..90aca1844b3 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeTests.cs @@ -5,17 +5,14 @@ using System.Linq.Expressions; using System.Threading.Tasks; using HotChocolate.Execution; +using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Tests; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Relay; using Microsoft.Extensions.DependencyInjection; using Moq; -#if NETCOREAPP2_1 -using Snapshooter; -#endif using Snapshooter.Xunit; -using Xunit; using static HotChocolate.Types.FieldBindingFlags; using static HotChocolate.WellKnownContextData; using SnapshotExtensions = CookieCrumble.SnapshotExtensions; @@ -2019,6 +2016,22 @@ public async Task Static_Field_Inference_3() SnapshotExtensions.MatchSnapshot(schema); } + [Fact] + public async Task Static_Field_Inference_3_Execute() + { + // arrange + // act + var result = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .ModifyOptions(o => o.DefaultBindingBehavior = BindingBehavior.Explicit) + .ExecuteRequestAsync("{ hello }"); + + // assert + SnapshotExtensions.MatchSnapshot(result); + } + [Fact] public async Task Static_Field_Inference_4() { @@ -2039,6 +2052,42 @@ public async Task Static_Field_Inference_4() SnapshotExtensions.MatchSnapshot(schema); } + [Fact] + public async Task Static_Field_Inference_4_Execute() + { + // arrange + // act + var result = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .ModifyOptions(o => + { + o.DefaultBindingBehavior = BindingBehavior.Explicit; + o.DefaultFieldBindingFlags = Instance | Static; + }) + .ExecuteRequestAsync("{ hello staticHello }"); + + // assert + SnapshotExtensions.MatchSnapshot(result); + } + + [Fact] + public async Task Static_Field_Inference_5() + { + // arrange + // act + var schema = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(BookQuery)) + .ModifyOptions(o => o.DefaultFieldBindingFlags = InstanceAndStatic) + .BuildSchemaAsync(); + + // assert + SnapshotExtensions.MatchSnapshot(schema); + } public class GenericFoo { @@ -2338,4 +2387,20 @@ public class WithStaticField2 public string Hello() => "hello"; } + + [ExtendObjectType(OperationType.Query)] + public static class BookQuery + { + public static Book GetBook() + => new Book(); + } + + public class Book + { + public int Id { get; } + + public string Title { get; set; } + + public static bool IsComic => true; + } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_3_Execute.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_3_Execute.snap new file mode 100644 index 00000000000..db42c9e5210 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_3_Execute.snap @@ -0,0 +1,5 @@ +{ + "data": { + "hello": "hello" + } +} \ No newline at end of file diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_4_Execute.snap b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_4_Execute.snap new file mode 100644 index 00000000000..d1ad4e5f898 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_4_Execute.snap @@ -0,0 +1,6 @@ +{ + "data": { + "hello": "hello", + "staticHello": "hello" + } +} \ No newline at end of file diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_5.graphql b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_5.graphql new file mode 100644 index 00000000000..3a7f5b9f94f --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/__snapshots__/ObjectTypeTests.Static_Field_Inference_5.graphql @@ -0,0 +1,19 @@ +schema { + query: Query +} + +type Book { + id: Int! + title: String! + isComic: Boolean! +} + +type Query { + book: Book! +} + +"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." +directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT + +"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." +directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD \ No newline at end of file