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
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ public async ValueTask InvokeAsync(IMiddlewareContext context)

foreach (var argument in _resolverArguments)
{
input.TryGetValue(argument.Name, out var value);
input.TryGetValue(argument.InputName, out var value);

var omitted = false;
if (!inputLiteral.TryGetValue(argument.Name, out var valueLiteral))
if (!inputLiteral.TryGetValue(argument.InputName, out var valueLiteral))
{
omitted = true;
valueLiteral = argument.DefaultValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,15 @@ private void TryApplyInputConvention(
return;
}

var inputType = CreateInputType(inputTypeName, mutation);
var inputFieldNameMap = CreateInputFieldNameMap(mutation);
var inputType = CreateInputType(inputTypeName, mutation, inputFieldNameMap);
RegisterType(inputType);

var resolverArguments = new List<ResolverArgument>();

foreach (var argument in mutation.Arguments)
{
var inputFieldName = inputFieldNameMap[argument.Name];
var runtimeType = argument.RuntimeType ??
argument.Parameter?.ParameterType ??
typeof(object);
Expand Down Expand Up @@ -249,7 +251,8 @@ private void TryApplyInputConvention(
resolverArguments.Add(
new ResolverArgument(
argument.Name,
new SchemaCoordinate(inputTypeName, memberName: argument.Name),
inputFieldName,
new SchemaCoordinate(inputTypeName, memberName: inputFieldName),
_completionContext.GetType<IInputType>(argument.Type!),
runtimeType,
defaultValue,
Expand Down Expand Up @@ -448,7 +451,9 @@ private void TryApplyPayloadConvention(

// lastly we will create the mutation payload and replace with it the current mutation
// result type.
payloadFieldName ??= _context.Naming.FormatFieldName(registration.Type.Name);
payloadFieldName ??= _context.Naming.GetMemberName(
registration.Type.Name,
MemberKind.ObjectField);

var type = CreatePayloadType(
payloadTypeName,
Expand All @@ -463,16 +468,33 @@ private void TryApplyPayloadConvention(
mutation.Features.Set<ErrorFieldFeature>(null);
}

private Dictionary<string, string> CreateInputFieldNameMap(
ObjectFieldConfiguration fieldDef)
{
var inputFieldNameMap = new Dictionary<string, string>(StringComparer.Ordinal);

foreach (var argumentDef in fieldDef.Arguments)
{
inputFieldNameMap.Add(
argumentDef.Name,
_context.Naming.GetMemberName(argumentDef.Name, MemberKind.InputObjectField));
}

return inputFieldNameMap;
}

private static InputObjectType CreateInputType(
string typeName,
ObjectFieldConfiguration fieldDef)
ObjectFieldConfiguration fieldDef,
IReadOnlyDictionary<string, string> inputFieldNameMap)
{
var inputObjectDef = new InputObjectTypeConfiguration(typeName);

foreach (var argumentDef in fieldDef.Arguments)
{
var inputFieldDef = new InputFieldConfiguration();
argumentDef.CopyTo(inputFieldDef);
inputFieldDef.Name = inputFieldNameMap[argumentDef.Name];

inputFieldDef.RuntimeType =
argumentDef.RuntimeType ??
Expand Down Expand Up @@ -744,17 +766,47 @@ private readonly ref struct Options(
public string FormatInputTypeName(string mutationName)
=> InputTypeNamePattern.Replace(
$"{{{MutationConventionOptionDefaults.MutationName}}}",
char.ToUpper(mutationName[0]) + mutationName[1..]);
FormatMutationName(mutationName));

public string FormatPayloadTypeName(string mutationName)
=> PayloadTypeNamePattern.Replace(
$"{{{MutationConventionOptionDefaults.MutationName}}}",
char.ToUpper(mutationName[0]) + mutationName[1..]);
FormatMutationName(mutationName));

public string FormatErrorTypeName(string mutationName)
=> PayloadErrorTypeNamePattern.Replace(
$"{{{MutationConventionOptionDefaults.MutationName}}}",
char.ToUpper(mutationName[0]) + mutationName[1..]);
FormatMutationName(mutationName));

private static string FormatMutationName(string mutationName)
{
if (string.IsNullOrEmpty(mutationName))
{
return mutationName;
}

if (mutationName.IndexOf('_', StringComparison.Ordinal) < 0)
{
return char.ToUpperInvariant(mutationName[0]) + mutationName[1..];
}

var builder = new System.Text.StringBuilder(mutationName.Length);
var upperNext = true;

foreach (var c in mutationName)
{
if (c == '_')
{
upperNext = true;
continue;
}

builder.Append(upperNext ? char.ToUpperInvariant(c) : c);
upperNext = false;
}

return builder.ToString();
}
}

private readonly struct FieldDef(string name, TypeReference type)
Expand Down
3 changes: 3 additions & 0 deletions src/HotChocolate/Core/src/Types.Mutations/ResolverArgument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace HotChocolate.Types;

internal sealed class ResolverArgument(
string name,
string inputName,
SchemaCoordinate coordinate,
IInputType type,
Type runtimeType,
Expand All @@ -11,6 +12,8 @@ internal sealed class ResolverArgument(
{
public string Name { get; } = name;

public string InputName { get; } = inputName;

public SchemaCoordinate Coordinate { get; } = coordinate;

public IInputType Type { get; } = type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1266,11 +1266,41 @@ public async Task Mutation_With_MutationConventionsAndNamingConventions()
"name_Named": "coco"
}
}
}
}
}
""");
}

[Fact]
public async Task MutationConvention_With_SnakeCase_ObjectField_NamingConvention_Uses_PascalCase_TypeNames()
{
var schema =
await new ServiceCollection()
.AddGraphQL()
.AddMutationType<Issue4803Mutation>()
.AddConvention<INamingConventions, Issue4803NamingConvention>()
.AddMutationConventions(
new MutationConventionOptions { ApplyToAllMutations = true })
.ModifyOptions(o => o.StrictValidation = false)
.BuildSchemaAsync();

schema.MatchSnapshot();

var schemaText = schema.ToString();

Assert.Matches(
@"type Issue4803Mutation \{[\s\S]*do_something\(input: DoSomethingInput!\): DoSomethingPayload!",
schemaText);
Assert.Contains("input DoSomethingInput {", schemaText);
Assert.Contains("type DoSomethingPayload {", schemaText);
Assert.Contains("issue4803_result: Issue4803Result", schemaText);
Assert.Contains("user_name: String!", schemaText);
Assert.DoesNotContain("Do_somethingInput", schemaText);
Assert.DoesNotContain("Do_somethingPayload", schemaText);
Assert.DoesNotContain("issue4803Result: Issue4803Result", schemaText);
Assert.DoesNotContain("userName: String!", schemaText);
}

[Fact]
public async Task Mutation_With_ErrorAnnotatedAndCustomInterface_LateAndEarlyRegistration()
{
Expand Down Expand Up @@ -1933,4 +1963,45 @@ public override string GetTypeDescription(Type type, TypeKind kind)
return "GetTypeDescription";
}
}

public class Issue4803Mutation
{
public Issue4803Result DoSomething(string userName)
=> new() { UserName = userName };
}

public class Issue4803Result
{
public string UserName { get; set; } = null!;
}

public class Issue4803NamingConvention : DefaultNamingConventions
{
public override string GetMemberName(MemberInfo member, MemberKind kind)
{
if (kind is MemberKind.ObjectField or MemberKind.InputObjectField)
{
return ToSnakeCase(member.Name);
}

return base.GetMemberName(member, kind);
}

public override string GetMemberName(string originalMemberName, MemberKind kind)
{
if (kind is MemberKind.ObjectField or MemberKind.InputObjectField)
{
return ToSnakeCase(originalMemberName);
}

return base.GetMemberName(originalMemberName, kind);
}

private static string ToSnakeCase(string memberName)
{
var pattern = new System.Text.RegularExpressions.Regex(
@"[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+");
return string.Join("_", pattern.Matches(memberName)).ToLowerInvariant();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
schema {
mutation: Issue4803Mutation
}

type DoSomethingPayload {
issue4803_result: Issue4803Result
}

type Issue4803Mutation {
do_something(input: DoSomethingInput!): DoSomethingPayload!
}

type Issue4803Result {
user_name: String!
}

input DoSomethingInput {
user_name: String!
}
Loading