Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scope the projection selection properly when using the mutation conventions. #6444

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ public static class WellKnownContextData
/// </summary>
public const string MutationQueryField = "HotChocolate.Relay.Mutations.QueryField";

/// <summary>
/// The key to the name of the data field when using the mutation convention.
/// </summary>
public const string MutationConventionDataField = "HotChocolate.Types.Mutations.Conventions.DataField";

/// <summary>
/// The key to get the Cache-Control header value from the context data.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ private static ObjectType CreatePayloadType(

return parent;
});
objectDef.ContextData.Add(MutationConventionDataField, dataFieldDef.Name);
objectDef.Fields.Add(dataFieldDef);

// if the mutation has domain errors we will add the errors
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Threading;
Expand Down Expand Up @@ -205,10 +206,27 @@ value is ObjectType objectType &&
context = new MiddlewareContextProxy(context, selection, objectType);
}

if (context.Operation.Definition.Operation == OperationType.Mutation
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
&& context.Selection.Field.Type.NamedType() is ObjectType mutationPayloadType
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
&& mutationPayloadType.ContextData.GetValueOrDefault(MutationConventionDataField, null) is string dataFieldName)
{
var dataField = mutationPayloadType.Fields[dataFieldName];
var selection = UnwrapMutationPayloadSelect(context, dataField);
context = new MiddlewareContextProxy(context, selection, dataField.DeclaringType);
}
return executor.Invoke(next).Invoke(context);
};
}

private static ISelection UnwrapMutationPayloadSelect(IMiddlewareContext context, IObjectField field)
{
var selectionVariant =
context.Operation.SelectionVariants.First(sv => sv.GetPossibleTypes().Contains(field.DeclaringType));
var unwrap = selectionVariant.GetSelectionSet(field.DeclaringType).Selections
.First(s => s.Field.Name == field.Name);
return unwrap;
}

private sealed class MiddlewareContextProxy : IMiddlewareContext
{
private readonly IMiddlewareContext _context;
Expand Down Expand Up @@ -329,7 +347,7 @@ public ArgumentValue ReplaceArgument(string argumentName, ArgumentValue newArgum
IResolverContext IResolverContext.Clone() => _context.Clone();
}

private static Selection CreateProxySelection(ISelection selection, NodeFieldProxy field)
private static Selection CreateProxySelection(ISelection selection, IObjectField field)
{
var includeConditionsSource = ((Selection)selection).IncludeConditions;
var includeConditions = new long[includeConditionsSource.Length];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,56 @@ ... on Bar { fieldOfBar }

result.MatchSnapshot();
}

[Fact]
public async Task Mutation_Convention_Select()
{
var executor = await new ServiceCollection()
.AddGraphQL()
.AddQueryType<Query>() //error thrown without query, it's not needed for the test though
.AddMutationType<Mutation>()
.AddProjections()
.AddMutationConventions()
.BuildRequestExecutorAsync();

var result = await executor.ExecuteAsync(
"""
mutation {
modify {
foo {
bar
}
}
}
""");

result.MatchSnapshot();
}

[Fact]
public async Task Mutation_Convention_Select_With_SingleOrDefault()
{
var executor = await new ServiceCollection()
.AddGraphQL()
.AddQueryType<Query>() //error thrown without query, it's not needed for the test though
.AddMutationType<Mutation>()
.AddProjections()
.AddMutationConventions()
.BuildRequestExecutorAsync();

var result = await executor.ExecuteAsync(
"""
mutation {
modifySingleOrDefault {
foo {
bar
}
}
}
""");

result.MatchSnapshot();
}
}

public class Query
Expand All @@ -289,6 +339,24 @@ public IQueryable<Foo> Foos
=> new Foo[] { new() { Bar = "A" }, new() { Bar = "B" } }.AsQueryable();
}

public class Mutation
{
[UseMutationConvention]
[UseProjection]
public IQueryable<Foo> Modify()
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
{
return new Foo[] { new() { Bar = "A" }, new() { Bar = "B" } }.AsQueryable();
}

[UseMutationConvention]
[UseSingleOrDefault]
[UseProjection]
public IQueryable<Foo> ModifySingleOrDefault()
{
return new Foo[] { new() { Bar = "A" } }.AsQueryable();
}
}

[ExtendObjectType(typeof(Foo))]
public class FooExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"data": {
"modify": {
"foo": [
{
"bar": "A"
},
{
"bar": "B"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"data": {
"modifySingleOrDefault": {
"foo": {
"bar": "A"
}
}
}
}