Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Snapshot markdown files are generated and compared byte-for-byte in tests.
# Prettier rewrites JSON-in-markdown formatting and can break snapshot tests.
src/**/__snapshots__/*.md
src/**/__snapshots__/**/*.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,19 +131,48 @@ protected virtual void WriteResolverBindings(IOutputTypeInfo type, ILocalTypeLoo
Writer.WriteLine();
Writer.WriteIndentedLine("var naming = descriptor.Extend().Context.Naming;");

if (type.Resolvers.Any(t => t.Bindings.Length > 0))
var hasFieldBindings =
type.Resolvers.Any(t => t.Bindings.Any(b => b.Kind is MemberBindingKind.Field));
var hasPropertyBindings =
type.Resolvers.Any(t => t.Bindings.Any(b => b.Kind is MemberBindingKind.Property));

if (hasFieldBindings)
{
Writer.WriteIndentedLine("var ignoredFields = new global::System.Collections.Generic.HashSet<string>();");
Writer.WriteIndentedLine("var boundFields = new global::System.Collections.Generic.HashSet<string>();");

foreach (var binding in type.Resolvers.SelectMany(t => t.Bindings))
{
if (binding.Kind is MemberBindingKind.Field)
{
Writer.WriteIndentedLine(
"ignoredFields.Add(\"{0}\");",
"boundFields.Add(\"{0}\");",
binding.Name);
}
else if (binding.Kind is MemberBindingKind.Property)
}

Writer.WriteLine();
Writer.WriteIndentedLine("foreach(string fieldName in boundFields)");
Writer.WriteIndentedLine("{");
using (Writer.IncreaseIndent())
{
Writer.WriteIndentedLine("descriptor.Field(fieldName);");
}

Writer.WriteIndentedLine("}");
}

if (hasPropertyBindings)
{
if (hasFieldBindings)
{
Writer.WriteLine();
}

Writer.WriteIndentedLine("var ignoredFields = new global::System.Collections.Generic.HashSet<string>();");

foreach (var binding in type.Resolvers.SelectMany(t => t.Bindings))
{
if (binding.Kind is MemberBindingKind.Property)
{
Writer.WriteIndentedLine(
"ignoredFields.Add(naming.GetMemberName(\"{0}\", global::{1}.ObjectField));",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ namespace HotChocolate.Types;
/// <summary>
/// Binds a member of a type extension to a field of the actual type.
/// </summary>
[AttributeUsage(
AttributeTargets.Property | AttributeTargets.Method,
Inherited = true,
AllowMultiple = false)]
public sealed class BindFieldAttribute : ObjectFieldDescriptorAttribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ namespace HotChocolate.Types;
/// <summary>
/// Binds a member of a type extension to a member of the actual type.
/// </summary>
[AttributeUsage(
AttributeTargets.Property | AttributeTargets.Method,
Inherited = true,
AllowMultiple = true)]
public sealed class BindMemberAttribute : ObjectFieldDescriptorAttribute
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,58 @@ internal static partial class BookNode
""").MatchMarkdownAsync();
}

[Fact]
public async Task BindField_BoundProperty_MatchesSnapshot()
{
await TestHelper.GetGeneratedSourceSnapshot(
"""
using HotChocolate;
using HotChocolate.Types;

namespace TestNamespace;

public class Query
{
public string Greeting { get; set; }
}

[ObjectType<Query>]
internal static partial class QueryType
{
[BindField("greeting")]
public static string GetGreeting([Parent] Query query)
=> query.Greeting + " world";
}
""").MatchMarkdownAsync();
}

[Fact]
public async Task BindField_MultipleAttributes_RaisesError()
{
await TestHelper.GetGeneratedSourceSnapshot(
"""
using HotChocolate;
using HotChocolate.Types;

namespace TestNamespace;

public class Query
{
public string Greeting { get; set; }
public string Salutation { get; set; }
}

[ObjectType<Query>]
internal static partial class QueryType
{
[BindField("greeting")]
[BindField("salutation")]
public static string GetGreeting([Parent] Query query)
=> query.Greeting + query.Salutation;
}
""").MatchMarkdownAsync();
}

[Fact]
public async Task Argument_With_DefaultValue()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# BindField_BoundProperty_MatchesSnapshot

## HotChocolateTypeModule.735550c.g.cs

```csharp
// <auto-generated/>

#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<global::TestNamespace.Query>(
"Tests::TestNamespace.QueryType",
() => global::TestNamespace.QueryType.Initialize));
builder.AddType<ObjectType<global::TestNamespace.Query>>();
return builder;
}
}
}

```

## QueryType.WaAdMHmlGJHjtEI4nqY7WA.hc.g.cs

```csharp
// <auto-generated/>

#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 QueryType
{
internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor<global::TestNamespace.Query> descriptor)
{
var extension = descriptor.Extend();
var configuration = extension.Configuration;
var thisType = typeof(global::TestNamespace.QueryType);
var bindingResolver = extension.Context.ParameterBindingResolver;
var resolvers = new __Resolvers();

var naming = descriptor.Extend().Context.Naming;
var boundFields = new global::System.Collections.Generic.HashSet<string>();
boundFields.Add("greeting");

foreach(string fieldName in boundFields)
{
descriptor.Field(fieldName);
}

descriptor
.Field(naming.GetMemberName("Greeting", 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(string), HotChocolate.Types.TypeContext.Output),
new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNode("string")));
configuration.ResultType = typeof(string);

configuration.SetSourceGeneratorFlags();

configuration.Member = context.ThisType.GetMethod(
"GetGreeting",
global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags,
new global::System.Type[]
{
typeof(global::TestNamespace.Query)
})!;

var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Context, configuration);
HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
field.Context,
fieldDescriptor,
configuration.Member,
new global::HotChocolate.Types.BindFieldAttribute("greeting"));
configuration.ConfigurationsAreApplied = true;
fieldDescriptor.CreateConfiguration();

configuration.Resolvers = context.Resolvers.GetGreeting();
},
(Resolvers: resolvers, ThisType: thisType));

Configure(descriptor);
}

static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor<global::TestNamespace.Query> descriptor);

private sealed class __Resolvers
{
public HotChocolate.Resolvers.FieldResolverDelegates GetGreeting()
{
return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: GetGreeting);
}

private global::System.Object? GetGreeting(global::HotChocolate.Resolvers.IResolverContext context)
{
var args0 = context.Parent<global::TestNamespace.Query>();
var result = global::TestNamespace.QueryType.GetGreeting(args0);
return result;
}
}
}
}


```
Loading
Loading