diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeFileBuilder.cs index f07f5dd607b..e0748e42a5f 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeFileBuilder.cs @@ -78,6 +78,8 @@ public override void WriteResolverFields(IOutputTypeInfo type) { WriteResolverField(objectType.NodeResolver); } + + WriteIsSelectedFields(objectType.NodeResolver); } } @@ -93,7 +95,9 @@ public override void WriteResolverConstructor(IOutputTypeInfo type, ILocalTypeLo objectType, typeLookup, type.Resolvers.Any(t => t.RequiresParameterBindings) - || (objectType.NodeResolver?.RequiresParameterBindings ?? false)); + || (objectType.NodeResolver?.RequiresParameterBindings ?? false), + type.Resolvers.Any(HasIsSelectedFields) + || HasIsSelectedFields(objectType.NodeResolver)); } protected override void WriteResolversBindingInitialization(IOutputTypeInfo type, ILocalTypeLookup typeLookup) @@ -112,6 +116,8 @@ protected override void WriteResolversBindingInitialization(IOutputTypeInfo type { WriteResolverBindingInitialization(objectType.NodeResolver, typeLookup); } + + WriteIsSelectedInitialization(objectType.NodeResolver); } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs index 7172b8cab96..35b6e9b8f6a 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/TypeFileBuilderBase.cs @@ -666,6 +666,8 @@ public virtual void WriteResolverFields(IOutputTypeInfo type) { WriteResolverField(resolver); } + + WriteIsSelectedFields(resolver); } } @@ -692,23 +694,34 @@ public virtual void WriteResolverConstructor(IOutputTypeInfo type, ILocalTypeLoo WriteResolverConstructor( type, typeLookup, - type.Resolvers.Any(t => t.RequiresParameterBindings)); + type.Resolvers.Any(t => t.RequiresParameterBindings), + type.Resolvers.Any(HasIsSelectedFields)); } protected void WriteResolverConstructor( IOutputTypeInfo type, ILocalTypeLookup typeLookup, - bool requiresParameterBindings) + bool requiresParameterBindings, + bool requiresIsSelectedInit = false) { - if (!requiresParameterBindings) + if (!requiresParameterBindings && !requiresIsSelectedInit) { return; } Writer.WriteLine(); - Writer.WriteIndentedLine( - "public __Resolvers(global::{0} bindingResolver)", - WellKnownTypes.ParameterBindingResolver); + + if (requiresParameterBindings) + { + Writer.WriteIndentedLine( + "public __Resolvers(global::{0} bindingResolver)", + WellKnownTypes.ParameterBindingResolver); + } + else + { + Writer.WriteIndentedLine("public __Resolvers()"); + } + Writer.WriteIndentedLine("{"); using (Writer.IncreaseIndent()) @@ -725,6 +738,7 @@ protected virtual void WriteResolversBindingInitialization(IOutputTypeInfo type, foreach (var resolver in type.Resolvers) { WriteResolverBindingInitialization(resolver, typeLookup); + WriteIsSelectedInitialization(resolver); } } @@ -1700,6 +1714,63 @@ private void WriteResolverArguments(Resolver resolver, IMethodSymbol resolverMet i); break; + case ResolverParameterKind.IsSelected: + var (variant, fieldNames, _) = GetIsSelectedInfo(parameter); + + switch (variant) + { + case IsSelectedVariant.SingleField: + Writer.WriteIndentedLine( + "var args{0} = context.Select().IsSelected(\"{1}\");", + i, + fieldNames[0]); + break; + + case IsSelectedVariant.MultipleFields: + var sb = new StringBuilder(); + for (var j = 0; j < fieldNames.Length; j++) + { + if (j > 0) + { + sb.Append(", "); + } + + sb.Append('"'); + sb.Append(fieldNames[j]); + sb.Append('"'); + } + + Writer.WriteIndentedLine( + "var args{0} = context.Select().IsSelected({1});", + i, + sb.ToString()); + break; + + case IsSelectedVariant.FieldSet: + Writer.WriteIndentedLine( + "var args{0} = context.Select().IsSelected(_isSelected_{1}_{2});", + i, + resolver.Member.Name, + parameter.Name); + break; + + case IsSelectedVariant.Pattern: + Writer.WriteIndentedLine( + "var args{0}_selectionContext = new global::HotChocolate.Resolvers.IsSelectedContext(context.Schema, context.Select());", + i); + Writer.WriteIndentedLine( + "global::HotChocolate.Resolvers.IsSelectedVisitor.Instance.Visit(_isSelected_{0}_{1}, args{2}_selectionContext);", + resolver.Member.Name, + parameter.Name, + i); + Writer.WriteIndentedLine( + "var args{0} = args{0}_selectionContext.AllSelected;", + i); + break; + } + + break; + case ResolverParameterKind.Unknown: Writer.WriteIndentedLine( "var args{0} = _binding_{1}_{2}.Execute<{3}>(context);", @@ -1966,6 +2037,165 @@ private static string EscapeChar(char c) }; } + protected void WriteIsSelectedFields(Resolver resolver) + { + foreach (var parameter in resolver.Parameters) + { + if (parameter.Kind != ResolverParameterKind.IsSelected) + { + continue; + } + + var (variant, _, _) = GetIsSelectedInfo(parameter); + + switch (variant) + { + case IsSelectedVariant.FieldSet: + Writer.WriteIndentedLine( + "private readonly global::System.Collections.Generic.HashSet _isSelected_{0}_{1};", + resolver.Member.Name, + parameter.Name); + break; + + case IsSelectedVariant.Pattern: + Writer.WriteIndentedLine( + "private readonly global::HotChocolate.Language.SelectionSetNode _isSelected_{0}_{1};", + resolver.Member.Name, + parameter.Name); + break; + } + } + } + + protected void WriteIsSelectedInitialization(Resolver resolver) + { + foreach (var parameter in resolver.Parameters) + { + if (parameter.Kind != ResolverParameterKind.IsSelected) + { + continue; + } + + var (variant, fieldNames, patternString) = GetIsSelectedInfo(parameter); + + switch (variant) + { + case IsSelectedVariant.FieldSet: + var sb = new StringBuilder(); + for (var i = 0; i < fieldNames.Length; i++) + { + if (i > 0) + { + sb.Append(", "); + } + + sb.Append('"'); + sb.Append(fieldNames[i]); + sb.Append('"'); + } + + Writer.WriteIndentedLine( + "_isSelected_{0}_{1} = new global::System.Collections.Generic.HashSet([{2}]);", + resolver.Member.Name, + parameter.Name, + sb.ToString()); + break; + + case IsSelectedVariant.Pattern: + Writer.WriteIndentedLine( + $"_isSelected_{resolver.Member.Name}_{parameter.Name} = global::HotChocolate.Language.Utf8GraphQLParser.Syntax.ParseSelectionSet(\"{{ {patternString} }}\");"); + break; + } + } + } + + protected static bool HasIsSelectedFields(Resolver? resolver) + { + if (resolver is null) + { + return false; + } + + foreach (var parameter in resolver.Parameters) + { + if (parameter.Kind != ResolverParameterKind.IsSelected) + { + continue; + } + + var (variant, _, _) = GetIsSelectedInfo(parameter); + if (variant is IsSelectedVariant.FieldSet or IsSelectedVariant.Pattern) + { + return true; + } + } + + return false; + } + + private static (IsSelectedVariant Variant, string[] FieldNames, string? PatternString) GetIsSelectedInfo( + ResolverParameter parameter) + { + AttributeData? attr = null; + foreach (var a in parameter.Attributes) + { + if (a.AttributeClass?.ToDisplayString() == WellKnownAttributes.IsSelectedAttribute) + { + attr = a; + break; + } + } + + if (attr is null) + { + throw new InvalidOperationException("The parameter does not have an IsSelectedAttribute."); + } + + var args = attr.ConstructorArguments; + + // params string[] constructor (4+ fields) + if (args.Length == 1 && args[0].Kind == TypedConstantKind.Array) + { + var names = new string[args[0].Values.Length]; + for (var i = 0; i < names.Length; i++) + { + names[i] = (string)args[0].Values[i].Value!; + } + + return (IsSelectedVariant.FieldSet, names, null); + } + + // Single string constructor + if (args.Length == 1) + { + var fieldName = (string)args[0].Value!; + + if (fieldName.IndexOf(' ') < 0 && fieldName.IndexOf('{') < 0) + { + return (IsSelectedVariant.SingleField, [fieldName], null); + } + + return (IsSelectedVariant.Pattern, [], fieldName); + } + + // 2 or 3 explicit string args + var fieldNames = new string[args.Length]; + for (var i = 0; i < fieldNames.Length; i++) + { + fieldNames[i] = (string)args[i].Value!; + } + + return (IsSelectedVariant.MultipleFields, fieldNames, null); + } + + private enum IsSelectedVariant + { + SingleField, + MultipleFields, + FieldSet, + Pattern + } + public void Flush() => Writer.Flush(); private static string ToFullyQualifiedString( diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/CompilationExtensions.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/CompilationExtensions.cs index 57d4bb3e2a3..980c5923465 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/CompilationExtensions.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/CompilationExtensions.cs @@ -417,6 +417,11 @@ public static ResolverParameterKind GetParameterKind( return ResolverParameterKind.ConnectionFlags; } + if (parameter.IsIsSelected()) + { + return ResolverParameterKind.IsSelected; + } + if (parameter.IsSelection()) { return ResolverParameterKind.Selection; diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs index 0cc8ac01132..4d643df9fb5 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs @@ -787,6 +787,19 @@ public static bool IsPagingArguments(this IParameterSymbol parameter) public static bool IsSelection(this IParameterSymbol parameter) => parameter.Type.ToDisplayString() == WellKnownTypes.ISelection; + public static bool IsIsSelected(this IParameterSymbol parameter) + { + foreach (var attribute in parameter.GetAttributes()) + { + if (attribute.AttributeClass?.ToDisplayString() == WellKnownAttributes.IsSelectedAttribute) + { + return true; + } + } + + return false; + } + public static bool IsGlobalState( this IParameterSymbol parameter, Compilation compilation, diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs index 96415bea0c0..46b55f7b9f9 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs @@ -89,7 +89,8 @@ ResolverParameterKind.FieldNode or ResolverParameterKind.OutputField or ResolverParameterKind.ClaimsPrincipal or ResolverParameterKind.ConnectionFlags or - ResolverParameterKind.Selection; + ResolverParameterKind.Selection or + ResolverParameterKind.IsSelected; public bool RequiresBinding => Kind == ResolverParameterKind.Unknown; diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs index daa5641882a..df1e94aad42 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs @@ -24,5 +24,6 @@ public enum ResolverParameterKind QueryContext, PagingArguments, ConnectionFlags, - Selection + Selection, + IsSelected } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownAttributes.cs b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownAttributes.cs index cd7c538c5e8..d62a1805693 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownAttributes.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownAttributes.cs @@ -39,6 +39,7 @@ public static class WellKnownAttributes public const string ObsoleteAttribute = "System.ObsoleteAttribute"; public const string BatchResolverAttribute = "HotChocolate.Types.BatchResolverAttribute"; public const string GraphQLTypeAttribute = "HotChocolate.GraphQLTypeAttribute"; + public const string IsSelectedAttribute = "HotChocolate.Types.IsSelectedAttribute"; public static HashSet BindAttributes { get; } = [ diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/IsSelectedTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/IsSelectedTests.cs new file mode 100644 index 00000000000..1ad8224d0ea --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/IsSelectedTests.cs @@ -0,0 +1,126 @@ +using HotChocolate.Execution; +using HotChocolate.Tests; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.Types; + +public class IsSelectedTests +{ + [Fact] + public async Task IsSelected_Should_ReturnTrue_When_FieldIsSelected() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQLServer() + .AddIntegrationTestTypes() + .AddPagingArguments() + .AddGlobalObjectIdentification() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync("{ isSelectedTest { name wasNameSelected } }"); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "isSelectedTest": { + "name": "test", + "wasNameSelected": true + } + } + } + """); + } + + [Fact] + public async Task IsSelected_Should_ReturnFalse_When_FieldIsNotSelected() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQLServer() + .AddIntegrationTestTypes() + .AddPagingArguments() + .AddGlobalObjectIdentification() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync("{ isSelectedTest { description wasNameSelected } }"); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "isSelectedTest": { + "description": "desc", + "wasNameSelected": false + } + } + } + """); + } + + [Fact] + public async Task IsSelected_NodeResolver_Should_ReturnTrue_When_FieldIsSelected() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQLServer() + .AddIntegrationTestTypes() + .AddPagingArguments() + .AddGlobalObjectIdentification() + .BuildRequestExecutorAsync(); + + var id = Convert.ToBase64String("IsSelectedNode:1"u8); + + // act + var result = await executor.ExecuteAsync( + $$"""{ node(id: "{{id}}") { ... on IsSelectedNode { name wasNameSelected } } }"""); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "node": { + "name": "test", + "wasNameSelected": true + } + } + } + """); + } + + [Fact] + public async Task IsSelected_NodeResolver_Should_ReturnFalse_When_FieldIsNotSelected() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQLServer() + .AddIntegrationTestTypes() + .AddPagingArguments() + .AddGlobalObjectIdentification() + .BuildRequestExecutorAsync(); + + var id = Convert.ToBase64String("IsSelectedNode:1"u8); + + // act + var result = await executor.ExecuteAsync( + $$"""{ node(id: "{{id}}") { ... on IsSelectedNode { description wasNameSelected } } }"""); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "node": { + "description": "desc", + "wasNameSelected": false + } + } + } + """); + } +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/Product.cs b/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/Product.cs index 129e5cd8750..08c3e7cc4cb 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/Product.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/Product.cs @@ -5,6 +5,7 @@ using HotChocolate.Language; using HotChocolate.Text.Json; using HotChocolate.Types.Descriptors; +using HotChocolate.Types.Relay; namespace HotChocolate.Types; @@ -47,6 +48,11 @@ public static partial class Query [GraphQLIgnore] public static PagingArguments PagingArguments { get; private set; } + public static IsSelectedNode GetIsSelectedTest([IsSelected("name")] bool isSelected) + { + return new IsSelectedNode { WasNameSelected = isSelected }; + } + /// /// Gets the product. /// @@ -99,6 +105,27 @@ public static string NullableListNullableElementArgumentRef(List? items => throw new Exception(); } +public class IsSelectedNode +{ + public int Id { get; set; } + + public string Name { get; set; } = "test"; + + public string Description { get; set; } = "desc"; + + public bool WasNameSelected { get; set; } +} + +[ObjectType] +public static partial class IsSelectedNodeType +{ + [NodeResolver] + public static IsSelectedNode? GetIsSelectedNodeById( + int id, + [IsSelected("name")] bool isSelected) + => new() { Id = id, WasNameSelected = isSelected }; +} + public class VersionType : ScalarType { public VersionType() : base("Version", BindingBehavior.Explicit) diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/__snapshots__/IntegrationTests.Schema_Snapshot.snap b/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/__snapshots__/IntegrationTests.Schema_Snapshot.snap index f68e38f005b..1a1918f142e 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/__snapshots__/IntegrationTests.Schema_Snapshot.snap +++ b/src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/__snapshots__/IntegrationTests.Schema_Snapshot.snap @@ -36,6 +36,13 @@ type IntsEdge { node: Int! } +type IsSelectedNode implements Node { + id: ID! + name: String! + description: String! + wasNameSelected: Boolean! +} + type Issue8057Entity implements Node { id: ID! } @@ -71,6 +78,7 @@ type ProductsEdge { } type Query { + isSelectedTest: IsSelectedNode! """ Gets the product. diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs index e916d01d931..0c5c0bf92c5 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs @@ -553,4 +553,104 @@ internal static partial class Query """ ]).MatchMarkdownAsync(); } + + [Fact] + public async Task GenerateSource_ResolverWithIsSelectedSingleField_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([IsSelected("email")] bool isSelected) + { + return isSelected ? 1 : 0; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithIsSelectedMultipleFields_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([IsSelected("email", "password", "name", "address")] bool isSelected) + { + return isSelected ? 1 : 0; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithIsSelectedPattern_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([IsSelected("email category { name }")] bool isSelected) + { + return isSelected ? 1 : 0; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_NodeResolverWithIsSelectedPattern_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + using HotChocolate.Types.Relay; + using System.Threading.Tasks; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + [NodeResolver] + internal static Task GetTestByIdAsync( + int id, + [IsSelected("name address { city }")] bool isSelected) + => Task.FromResult(null); + } + + internal class Test + { + public int Id { get; set; } + + public string Name { get; set; } + } + """).MatchMarkdownAsync(); + } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs index 72d4621bea0..d9a444bb10f 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs @@ -15,6 +15,7 @@ using HotChocolate.Execution.Processing; using HotChocolate.Features; using HotChocolate.Language; +using HotChocolate.Language.Visitors; using HotChocolate.Types.Analyzers; using HotChocolate.Types.Pagination; using Microsoft.AspNetCore.Builder; @@ -79,6 +80,12 @@ public static Snapshot GetGeneratedSourceSnapshot( // HotChocolate.Language MetadataReference.CreateFromFile(typeof(OperationType).Assembly.Location), + // HotChocolate.Language.Utf8 + MetadataReference.CreateFromFile(typeof(ParserOptions).Assembly.Location), + + // HotChocolate.Language.Visitors + MetadataReference.CreateFromFile(typeof(SyntaxVisitor).Assembly.Location), + // HotChocolate.Abstractions MetadataReference.CreateFromFile(typeof(ParentAttribute).Assembly.Location), diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_NodeResolverWithIsSelectedPattern_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_NodeResolverWithIsSelectedPattern_MatchesSnapshot.md new file mode 100644 index 00000000000..d5fb375b13d --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_NodeResolverWithIsSelectedPattern_MatchesSnapshot.md @@ -0,0 +1,97 @@ +# GenerateSource_NodeResolverWithIsSelectedPattern_MatchesSnapshot + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## TestType.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static partial class TestType + { + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + var extension = descriptor.Extend(); + var configuration = extension.Configuration; + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = extension.Context.ParameterBindingResolver; + var resolvers = new __Resolvers(); + + descriptor + .ImplementsNode() + .ResolveNode(resolvers.GetTestByIdAsync().Resolver!); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Language.SelectionSetNode _isSelected_GetTestByIdAsync_isSelected; + + public __Resolvers() + { + _isSelected_GetTestByIdAsync_isSelected = global::HotChocolate.Language.Utf8GraphQLParser.Syntax.ParseSelectionSet("{ name address { city } }"); + } + + public HotChocolate.Resolvers.FieldResolverDelegates GetTestByIdAsync() + => new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: GetTestByIdAsync); + + private async global::System.Threading.Tasks.ValueTask GetTestByIdAsync(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.GetLocalState(global::HotChocolate.WellKnownContextData.InternalId); + var args1_selectionContext = new global::HotChocolate.Resolvers.IsSelectedContext(context.Schema, context.Select()); + global::HotChocolate.Resolvers.IsSelectedVisitor.Instance.Visit(_isSelected_GetTestByIdAsync_isSelected, args1_selectionContext); + var args1 = args1_selectionContext.AllSelected; + var result = await global::TestNamespace.TestType.GetTestByIdAsync(args0, args1); + return result; + } + } + } +} + + +``` diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedMultipleFields_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedMultipleFields_MatchesSnapshot.md new file mode 100644 index 00000000000..89ecb3b722e --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedMultipleFields_MatchesSnapshot.md @@ -0,0 +1,114 @@ +# GenerateSource_ResolverWithIsSelectedMultipleFields_MatchesSnapshot + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## TestType.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static partial class TestType + { + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + var extension = descriptor.Extend(); + var configuration = extension.Configuration; + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = extension.Context.ParameterBindingResolver; + var resolvers = new __Resolvers(); + + var naming = descriptor.Extend().Context.Naming; + + descriptor + .Field(naming.GetMemberName("Test", global::HotChocolate.Types.MemberKind.ObjectField)) + .ExtendWith(static (field, context) => + { + var configuration = field.Configuration; + var typeInspector = field.Context.TypeInspector; + var bindingResolver = field.Context.ParameterBindingResolver; + var naming = field.Context.Naming; + + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.GetTest(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::System.Collections.Generic.HashSet _isSelected_GetTest_isSelected; + + public __Resolvers() + { + _isSelected_GetTest_isSelected = new global::System.Collections.Generic.HashSet(["email", "password", "name", "address"]); + } + + public HotChocolate.Resolvers.FieldResolverDelegates GetTest() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: GetTest); + } + + private global::System.Object? GetTest(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.Select().IsSelected(_isSelected_GetTest_isSelected); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } + } +} + + +``` diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedPattern_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedPattern_MatchesSnapshot.md new file mode 100644 index 00000000000..42cf6cf7dbb --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedPattern_MatchesSnapshot.md @@ -0,0 +1,116 @@ +# GenerateSource_ResolverWithIsSelectedPattern_MatchesSnapshot + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## TestType.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static partial class TestType + { + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + var extension = descriptor.Extend(); + var configuration = extension.Configuration; + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = extension.Context.ParameterBindingResolver; + var resolvers = new __Resolvers(); + + var naming = descriptor.Extend().Context.Naming; + + descriptor + .Field(naming.GetMemberName("Test", global::HotChocolate.Types.MemberKind.ObjectField)) + .ExtendWith(static (field, context) => + { + var configuration = field.Configuration; + var typeInspector = field.Context.TypeInspector; + var bindingResolver = field.Context.ParameterBindingResolver; + var naming = field.Context.Naming; + + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.GetTest(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + private readonly global::HotChocolate.Language.SelectionSetNode _isSelected_GetTest_isSelected; + + public __Resolvers() + { + _isSelected_GetTest_isSelected = global::HotChocolate.Language.Utf8GraphQLParser.Syntax.ParseSelectionSet("{ email category { name } }"); + } + + public HotChocolate.Resolvers.FieldResolverDelegates GetTest() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: GetTest); + } + + private global::System.Object? GetTest(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0_selectionContext = new global::HotChocolate.Resolvers.IsSelectedContext(context.Schema, context.Select()); + global::HotChocolate.Resolvers.IsSelectedVisitor.Instance.Visit(_isSelected_GetTest_isSelected, args0_selectionContext); + var args0 = args0_selectionContext.AllSelected; + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } + } +} + + +``` diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedSingleField_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedSingleField_MatchesSnapshot.md new file mode 100644 index 00000000000..ef165feda08 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithIsSelectedSingleField_MatchesSnapshot.md @@ -0,0 +1,107 @@ +# GenerateSource_ResolverWithIsSelectedSingleField_MatchesSnapshot + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## TestType.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static partial class TestType + { + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + var extension = descriptor.Extend(); + var configuration = extension.Configuration; + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = extension.Context.ParameterBindingResolver; + var resolvers = new __Resolvers(); + + var naming = descriptor.Extend().Context.Naming; + + descriptor + .Field(naming.GetMemberName("Test", global::HotChocolate.Types.MemberKind.ObjectField)) + .ExtendWith(static (field, context) => + { + var configuration = field.Configuration; + var typeInspector = field.Context.TypeInspector; + var bindingResolver = field.Context.ParameterBindingResolver; + var naming = field.Context.Naming; + + configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create( + typeInspector.GetTypeRef(typeof(int), HotChocolate.Types.TypeContext.Output), + new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("int"))); + configuration.ResultType = typeof(int); + + configuration.SetSourceGeneratorFlags(); + + configuration.Resolvers = context.Resolvers.GetTest(); + }, + (Resolvers: resolvers, ThisType: thisType)); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); + + private sealed class __Resolvers + { + public HotChocolate.Resolvers.FieldResolverDelegates GetTest() + { + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: GetTest); + } + + private global::System.Object? GetTest(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.Select().IsSelected("email"); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } + } +} + + +```