diff --git a/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceEqualityComparer.cs b/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeRefEqualityComparer.cs similarity index 75% rename from src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceEqualityComparer.cs rename to src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeRefEqualityComparer.cs index 840f5c2ccf3..ce3c6758b60 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceEqualityComparer.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeRefEqualityComparer.cs @@ -9,10 +9,9 @@ namespace HotChocolate.Configuration; -internal sealed class ExtendedTypeReferenceEqualityComparer - : IEqualityComparer +internal sealed class ExtendedTypeRefEqualityComparer : IEqualityComparer { - public bool Equals([AllowNull] ExtendedTypeReference x, [AllowNull] ExtendedTypeReference y) + public bool Equals(ExtendedTypeReference? x, ExtendedTypeReference? y) { if (ReferenceEquals(x, y)) { @@ -39,7 +38,7 @@ public bool Equals([AllowNull] ExtendedTypeReference x, [AllowNull] ExtendedType return Equals(x.Type, y.Type); } - private static bool Equals([AllowNull] IExtendedType x, [AllowNull] IExtendedType y) + private static bool Equals(IExtendedType? x, IExtendedType? y) { if (ReferenceEquals(x, y)) { @@ -54,7 +53,7 @@ private static bool Equals([AllowNull] IExtendedType x, [AllowNull] IExtendedTyp return ReferenceEquals(x.Type, y.Type) && x.Kind == y.Kind; } - public int GetHashCode([DisallowNull] ExtendedTypeReference obj) + public int GetHashCode(ExtendedTypeReference obj) { unchecked { @@ -69,7 +68,7 @@ public int GetHashCode([DisallowNull] ExtendedTypeReference obj) } } - private static int GetHashCode([DisallowNull] IExtendedType obj) + private static int GetHashCode(IExtendedType obj) { unchecked { @@ -78,11 +77,10 @@ private static int GetHashCode([DisallowNull] IExtendedType obj) for (var i = 0; i < obj.TypeArguments.Count; i++) { - hashCode ^= (GetHashCode(obj.TypeArguments[i]) * 397 * i); + hashCode ^= GetHashCode(obj.TypeArguments[i]) * 397 * i; } return hashCode; } } - } diff --git a/src/HotChocolate/Core/src/Types/Configuration/Handlers/ExtendedTypeReferenceHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/Handlers/ExtendedTypeReferenceHandler.cs index e1cba59b55d..e7093ef3e95 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/Handlers/ExtendedTypeReferenceHandler.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/Handlers/ExtendedTypeReferenceHandler.cs @@ -69,10 +69,7 @@ private static void TryMapToExistingRegistration( foreach (var component in typeInfo.Components) { - normalizedTypeRef = TypeReference.Create( - component.Type, - context, - scope); + normalizedTypeRef = TypeReference.Create(component.Type, context, scope); if (typeRegistrar.IsResolved(normalizedTypeRef)) { diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs index 9317345ee2c..336f43c31e1 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs @@ -174,6 +174,9 @@ private bool TryInferTypes() if (unresolvedTypeRef is ExtendedTypeReference typeRef) { + // we normalize the type context so that we can correctly lookup + // if a type is already registered. + typeRef = typeRef.WithContext(schemaTypeRef.Context); _typeRegistry.TryRegister(typeRef, schemaTypeRef); } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs index 92f4b5879b5..cd9d601d29b 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs @@ -15,7 +15,7 @@ internal sealed class TypeRegistry { private readonly Dictionary _typeRegister = new(); private readonly Dictionary _runtimeTypeRefs = - new(new ExtendedTypeReferenceEqualityComparer()); + new(new ExtendedTypeRefEqualityComparer()); private readonly Dictionary _nameRefs = new(StringComparer.Ordinal); private readonly List _types = new(); private readonly TypeInterceptor _typeRegistryInterceptor; @@ -47,9 +47,9 @@ public bool IsRegistered(TypeReference typeReference) return true; } - if (typeReference is ExtendedTypeReference clrTypeReference) + if (typeReference is ExtendedTypeReference extendedTypeRef) { - return _runtimeTypeRefs.ContainsKey(clrTypeReference); + return _runtimeTypeRefs.ContainsKey(extendedTypeRef); } return false; diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs index 463e33348f7..5fb57f57bb1 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs @@ -1946,5 +1946,17 @@ internal static string ErrorHelper_FetchedToManyNodesAtOnce { return ResourceManager.GetString("ErrorHelper_FetchedToManyNodesAtOnce", resourceCulture); } } + + internal static string ThrowHelper_InputTypeExpected_Message { + get { + return ResourceManager.GetString("ThrowHelper_InputTypeExpected_Message", resourceCulture); + } + } + + internal static string ThrowHelper_OutputTypeExpected_Message { + get { + return ResourceManager.GetString("ThrowHelper_OutputTypeExpected_Message", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx index 64177e3f8b8..26ea0160950 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx @@ -1083,4 +1083,10 @@ Type: `{0}` The maximum number of nodes that can be fetched at once is {0}. This selection tried to fetch {1} nodes that exceeded the maximum allowed amount. + + The specified type `{0}` is expected to be an input type. + + + The specified type `{0}` is expected to be an output type. + diff --git a/src/HotChocolate/Core/src/Types/Types/Argument.cs b/src/HotChocolate/Core/src/Types/Types/Argument.cs index ac22acbbb1e..f3aba673826 100644 --- a/src/HotChocolate/Core/src/Types/Types/Argument.cs +++ b/src/HotChocolate/Core/src/Types/Types/Argument.cs @@ -101,7 +101,7 @@ protected override void OnCompleteField( base.OnCompleteField(context, declaringMember, definition); - Type = context.GetType(definition.Type!); + Type = context.GetType(definition.Type!).EnsureInputType(); _runtimeType = definition.RuntimeType ?? definition.Parameter?.ParameterType!; _runtimeType = CompleteRuntimeType(Type, _runtimeType, out var isOptional); DefaultValue = CompleteDefaultValue(context, definition, Type, Coordinate); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs index 00d6bf94ce1..c76134e7dc8 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs @@ -12,6 +12,21 @@ public sealed class ExtendedTypeReference : TypeReference , IEquatable { + /// + /// Initializes a new instance of . + /// + /// + /// The extended type. + /// + /// + /// The type context. + /// + /// + /// The type scope. + /// + /// + /// is null. + /// internal ExtendedTypeReference( IExtendedType type, TypeContext context, @@ -91,17 +106,25 @@ public override bool Equals(object? obj) /// public override int GetHashCode() - { - unchecked - { - return base.GetHashCode() ^ Type.GetHashCode() * 397; - } - } + => HashCode.Combine(base.GetHashCode(), Type.GetHashCode()); /// public override string ToString() => ToString(Type); + /// + /// Creates a new and + /// replaces the with the provided . + /// + /// + /// The extended type. + /// + /// + /// Returns a new . + /// + /// + /// is null. + /// public ExtendedTypeReference WithType(IExtendedType type) { if (type is null) @@ -109,36 +132,87 @@ public ExtendedTypeReference WithType(IExtendedType type) throw new ArgumentNullException(nameof(type)); } - return Create( - type, - Context, - Scope); + if (type.Equals(Type)) + { + return this; + } + + return Create(type, Context, Scope); } + /// + /// Creates a new and replaces the + /// with the provided . + /// + /// + /// The type context. + /// + /// + /// Returns a new . + /// public ExtendedTypeReference WithContext(TypeContext context = TypeContext.None) { + if (context == Context) + { + return this; + } + return Create( Type, context, Scope); } + /// + /// Creates a new and replaces the + /// with the provided + /// . + /// + /// + /// The type scope. + /// + /// + /// Returns a new . + /// public ExtendedTypeReference WithScope(string? scope = null) { + if (string.Equals(scope, Scope, StringComparison.Ordinal)) + { + return this; + } + return Create( Type, Context, scope); } + /// + /// Creates a new and allows to replace certain aspects + /// of the original instance. + /// + /// + /// The extended type. + /// + /// + /// The type context. + /// + /// + /// The type scope. + /// + /// + /// Returns a new . + /// public ExtendedTypeReference With( IExtendedType? type = default, Optional context = default, Optional scope = default) - { - return Create( + => Create( type ?? Type, - context.HasValue ? context.Value : Context, - scope.HasValue ? scope.Value : Scope); - } + context.HasValue + ? context.Value + : Context, + scope.HasValue + ? scope.Value + : Scope); } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs index 28bd7a42465..d8ab5c5edd9 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using HotChocolate.Internal; using HotChocolate.Language; using HotChocolate.Utilities; @@ -24,15 +25,32 @@ protected TypeReference( Scope = scope; } - /// + /// + /// Gets the kind of the type reference. + /// public TypeReferenceKind Kind { get; } - /// + /// + /// Specifies in which context the type reference is used + /// (input types, output types, neutral context). + /// public TypeContext Context { get; } - /// + /// + /// Gets the scope of the type reference. + /// The scope allows us to branch the type system. + /// public string? Scope { get; } + /// + /// A helper method to compare a type reference. + /// + /// + /// The other type reference. + /// + /// + /// true if the type references are equal; otherwise, false. + /// protected bool IsEqual(TypeReference other) { if (Context != other.Context diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs index 48886225dce..1d76f8656b3 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs @@ -5,22 +5,57 @@ using HotChocolate.Properties; using HotChocolate.Utilities; using static HotChocolate.Utilities.ThrowHelper; +using ThrowHelper = HotChocolate.Utilities.ThrowHelper; #nullable enable namespace HotChocolate.Types; +/// +/// Provides utility methods to work with . +/// public static class TypeExtensions { + /// + /// Calculates the depth of a type. The depth is defined as the + /// number of wrapper types + the named type itself.. + /// + /// + /// The type. + /// + /// + /// Returns the depth of the type. + /// + /// + /// is null. + /// public static int Depth(this IType type) { + if (type is null) + { + throw new ArgumentNullException(nameof(type)); + } + if (type is INamedType) { return 1; } + return Depth(type.InnerType()) + 1; } + /// + /// Defines if a type is nullable. + /// + /// + /// The type. + /// + /// + /// Returns true if the type is nullable; otherwise, false. + /// + /// + /// is null. + /// public static bool IsNullableType(this IType type) { if (type is null) @@ -31,6 +66,18 @@ public static bool IsNullableType(this IType type) return type.Kind != TypeKind.NonNull; } + /// + /// Defines if a type is non-nullable. + /// + /// + /// The type. + /// + /// + /// Returns true if the type is non-nullable; otherwise, false. + /// + /// + /// is null. + /// public static bool IsNonNullType(this IType type) { if (type is null) @@ -41,6 +88,18 @@ public static bool IsNonNullType(this IType type) return type.Kind == TypeKind.NonNull; } + /// + /// Defines if a type is a composite type (object, interface or union). + /// + /// + /// The type. + /// + /// + /// Returns true if the type is a composite type; otherwise, false. + /// + /// + /// is null. + /// public static bool IsCompositeType(this IType type) { if (type is null) @@ -48,11 +107,21 @@ public static bool IsCompositeType(this IType type) throw new ArgumentNullException(nameof(type)); } - return IsType(type, TypeKind.Object) || - IsType(type, TypeKind.Interface) || - IsType(type, TypeKind.Union); + return IsType(type, TypeKind.Object, TypeKind.Interface, TypeKind.Union); } + /// + /// Defines if a type is a complex type (object or interface). + /// + /// + /// The type. + /// + /// + /// Returns true if the type is a complex type; otherwise, false. + /// + /// + /// is null. + /// public static bool IsComplexType(this IType type) { if (type is null) @@ -60,10 +129,21 @@ public static bool IsComplexType(this IType type) throw new ArgumentNullException(nameof(type)); } - return IsType(type, TypeKind.Object) || - IsType(type, TypeKind.Interface); + return IsType(type, TypeKind.Object, TypeKind.Interface); } + /// + /// Defines if a type is a leaf type (scalar or enum). + /// + /// + /// The type. + /// + /// + /// Returns true if the type is a leaf type; otherwise, false. + /// + /// + /// is null. + /// public static bool IsLeafType(this IType type) { if (type is null) @@ -71,10 +151,21 @@ public static bool IsLeafType(this IType type) throw new ArgumentNullException(nameof(type)); } - return IsType(type, TypeKind.Scalar) || - IsType(type, TypeKind.Enum); + return IsType(type, TypeKind.Scalar, TypeKind.Enum); } + /// + /// Defines if a type is an list type. + /// + /// + /// The type. + /// + /// + /// Returns true if the type is an list type; otherwise, false. + /// + /// + /// is null. + /// public static bool IsListType(this IType type) { if (type is null) @@ -85,6 +176,18 @@ public static bool IsListType(this IType type) return IsType(type, TypeKind.List); } + /// + /// Defines if a type is a scalar type. + /// + /// + /// The type. + /// + /// + /// Returns true if the type is a scalar type; otherwise, false. + /// + /// + /// is null. + /// public static bool IsScalarType(this IType type) { if (type is null) @@ -95,6 +198,18 @@ public static bool IsScalarType(this IType type) return IsType(type, TypeKind.Scalar); } + /// + /// Defines if a type is an object type. + /// + /// + /// The type. + /// + /// + /// Returns true if the type is an object type; otherwise, false. + /// + /// + /// is null. + /// public static bool IsObjectType(this IType type) { if (type is null) @@ -145,6 +260,21 @@ public static bool IsInputType(this IType type) return type.NamedType() is IInputType; } + internal static IInputType EnsureInputType(this IType type) + { + if (type is null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (type.NamedType() is not IInputType) + { + throw InputTypeExpected(type); + } + + return (IInputType)type; + } + public static bool IsOutputType(this IType type) { if (type is null) @@ -155,6 +285,21 @@ public static bool IsOutputType(this IType type) return type.NamedType() is IOutputType; } + internal static IOutputType EnsureOutputType(this IType type) + { + if (type is null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (type.NamedType() is not IOutputType) + { + throw OutputTypeExpected(type); + } + + return (IOutputType)type; + } + public static bool IsUnionType(this IType type) { if (type is null) @@ -192,6 +337,7 @@ public static bool IsNamedType(this IType type) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsType(this IType type, TypeKind kind) { if (type.Kind == kind) @@ -207,6 +353,46 @@ internal static bool IsType(this IType type, TypeKind kind) return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsType(this IType type, TypeKind kind1, TypeKind kind2) + { + if (type.Kind == kind1 || type.Kind == kind2) + { + return true; + } + + if (type.Kind == TypeKind.NonNull) + { + var innerKind = ((NonNullType)type).Type.Kind; + if (innerKind == kind1 || innerKind == kind2) + { + return true; + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsType(this IType type, TypeKind kind1, TypeKind kind2, TypeKind kind3) + { + if (type.Kind == kind1 || type.Kind == kind2 || type.Kind == kind3) + { + return true; + } + + if (type.Kind == TypeKind.NonNull) + { + var innerKind = ((NonNullType)type).Type.Kind; + if (innerKind == kind1 || innerKind == kind2 || type.Kind == kind3) + { + return true; + } + } + + return false; + } + public static IType InnerType(this IType type) { if (type is null) @@ -234,7 +420,9 @@ public static IType NullableType(this IType type) throw new ArgumentNullException(nameof(type)); } - return type.Kind != TypeKind.NonNull ? type : ((NonNullType)type).Type; + return type.Kind != TypeKind.NonNull + ? type + : ((NonNullType)type).Type; } public static string TypeName(this IType type) @@ -262,6 +450,7 @@ public static ListType ListType(this IType type) if (type.Kind == TypeKind.NonNull) { var innerType = ((NonNullType)type).Type; + if (innerType.Kind == TypeKind.List) { return (ListType)innerType; @@ -391,8 +580,7 @@ public static ITypeNode ToTypeNode(this IType type) throw new ArgumentNullException(nameof(type)); } - if (type is NonNullType nnt - && ToTypeNode(nnt.Type) is INullableTypeNode nntn) + if (type is NonNullType nnt && ToTypeNode(nnt.Type) is INullableTypeNode nntn) { return new NonNullTypeNode(null, nntn); } @@ -415,15 +603,16 @@ public static ITypeNode ToTypeNode( this IType original, INamedType namedType) { - if (original is NonNullType nnt - && ToTypeNode(nnt.Type, namedType) is INullableTypeNode nntn) + if (original is NonNullType nnt && + ToTypeNode(nnt.Type, namedType) is INullableTypeNode nntn) { return new NonNullTypeNode(null, nntn); } if (original is ListType lt) { - return new ListTypeNode(null, + return new ListTypeNode( + null, ToTypeNode(lt.ElementType, namedType)); } diff --git a/src/HotChocolate/Core/src/Types/Types/InputField.cs b/src/HotChocolate/Core/src/Types/Types/InputField.cs index fed67b41153..2c7e602b4fe 100644 --- a/src/HotChocolate/Core/src/Types/Types/InputField.cs +++ b/src/HotChocolate/Core/src/Types/Types/InputField.cs @@ -78,7 +78,7 @@ protected override void OnCompleteField( { base.OnCompleteField(context, declaringMember, definition); - Type = context.GetType(definition.Type!); + Type = context.GetType(definition.Type!).EnsureInputType(); _runtimeType = definition.RuntimeType ?? definition.Property?.PropertyType!; _runtimeType = CompleteRuntimeType(Type, _runtimeType, out var isOptional); DefaultValue = CompleteDefaultValue(context, definition, Type, Coordinate); diff --git a/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs b/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs index 4a4754946f5..25b58f93d64 100644 --- a/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs @@ -56,7 +56,7 @@ protected override void OnCompleteField( { base.OnCompleteField(context, declaringMember, definition); - Type = context.GetType(definition.Type!); + Type = context.GetType(definition.Type!).EnsureOutputType(); _runtimeType = CompleteRuntimeType(Type, null); Arguments = OnCompleteFields(context, definition); } diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/JsonType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/JsonType.cs index 868c4b5d34f..9e6473a98e3 100644 --- a/src/HotChocolate/Core/src/Types/Types/Scalars/JsonType.cs +++ b/src/HotChocolate/Core/src/Types/Types/Scalars/JsonType.cs @@ -182,7 +182,7 @@ public static JsonElement Format(IValueNode node) _visitor.Visit(node, new JsonFormatterContext(jsonWriter)); jsonWriter.Flush(); - var jsonReader = new Utf8JsonReader(bufferWriter.Body.Span); + var jsonReader = new Utf8JsonReader(bufferWriter.GetWrittenSpan()); return JsonElement.ParseValue(ref jsonReader); } diff --git a/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs index 09b9d849d2b..3e306fd8222 100644 --- a/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs +++ b/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs @@ -566,4 +566,28 @@ public static SchemaException Flags_IllegalFlagEnumName(Type type, string? value type.FullName ?? type.Name, valueName ?? "value is null") .Build()); + + public static SchemaException InputTypeExpected(IType type) + { + var namedType = type.NamedType(); + + return new SchemaException( + SchemaErrorBuilder.New() + .SetMessage(ThrowHelper_InputTypeExpected_Message, namedType.Name) + .SetTypeSystemObject((ITypeSystemObject)namedType) + .SetExtension("type", type.Print()) + .Build()); + } + + public static SchemaException OutputTypeExpected(IType type) + { + var namedType = type.NamedType(); + + return new SchemaException( + SchemaErrorBuilder.New() + .SetMessage(ThrowHelper_OutputTypeExpected_Message, namedType.Name) + .SetTypeSystemObject((ITypeSystemObject)namedType) + .SetExtension("type", type.Print()) + .Build()); + } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeDiscoveryHandlerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeDiscoveryHandlerTests.cs new file mode 100644 index 00000000000..0695dd94bbb --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeDiscoveryHandlerTests.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using CookieCrumble; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.Configuration; + +public class TypeDiscoveryHandlerTests +{ + // https://github.com/ChilliCream/graphql-platform/issues/5942 + [Fact] + public async Task Ensure_Inputs_Are_Not_Used_As_Outputs() + { + var schema = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .AddType() + .BuildSchemaAsync(); + + schema.MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + foo(foo: TestMeInput): TestMe + } + + type TestMe { + bar: String + } + + input TestMeInput { + bar: String + } + """); + } + + public class Query + { + public Foo GetFoo(Foo foo) => foo; + } + + [GraphQLName("TestMe")] + public class Foo + { + public string Bar { get; set; } + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists_NoSystemTypes.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists_NoSystemTypes.snap index 6cb82dfde24..85aca2e192f 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists_NoSystemTypes.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists_NoSystemTypes.snap @@ -30,6 +30,6 @@ "runtimeTypeRefs": { "Foo (Output)": "HotChocolate.Configuration.TypeDiscovererTests+FooType (Output)", "Bar (Output)": "HotChocolate.Configuration.TypeDiscovererTests+BarType (Output)", - "String (Output)": "StringType" + "String": "StringType" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_08_Single_Mutation.snap b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_08_Single_Mutation.snap index f21f7c0ae7a..2f9e5943a51 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_08_Single_Mutation.snap +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_08_Single_Mutation.snap @@ -23,7 +23,7 @@ QueryPlan { "type": "Resolve", "subgraph": "Reviews", - "document": "query AddReview_1 { addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { name } } } }", + "document": "mutation AddReview_1 { addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { name } } } }", "selectionSetId": 0 }, { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_09_Two_Mutation_Same_SubGraph.snap b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_09_Two_Mutation_Same_SubGraph.snap index b8ac1d66ae4..9055d4dd9b4 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_09_Two_Mutation_Same_SubGraph.snap +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_09_Two_Mutation_Same_SubGraph.snap @@ -31,7 +31,7 @@ QueryPlan { "type": "Resolve", "subgraph": "Reviews", - "document": "query AddReviews_1 { a: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { name } } } b: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { name } } } }", + "document": "mutation AddReviews_1 { a: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { name } } } b: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { name } } } }", "selectionSetId": 0 }, { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_10_Two_Mutation_Same_SubGraph.snap b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_10_Two_Mutation_Same_SubGraph.snap index f6acff7f913..d01ba310d7a 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_10_Two_Mutation_Same_SubGraph.snap +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_10_Two_Mutation_Same_SubGraph.snap @@ -32,7 +32,7 @@ QueryPlan { "type": "Resolve", "subgraph": "Reviews", - "document": "query AddReviews_1 { a: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { __fusion_exports__1: id } } } b: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { id __fusion_exports__2: id } } } }", + "document": "mutation AddReviews_1 { a: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { __fusion_exports__1: id } } } b: addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { id __fusion_exports__2: id } } } }", "selectionSetId": 0 }, { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_11_Two_Mutation_Two_SubGraph.snap b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_11_Two_Mutation_Two_SubGraph.snap index 22179cb63c1..078169b9ada 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_11_Two_Mutation_Two_SubGraph.snap +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Query_Plan_11_Two_Mutation_Two_SubGraph.snap @@ -32,7 +32,7 @@ QueryPlan { "type": "Resolve", "subgraph": "Reviews", - "document": "query AddReviewAndUser_1 { addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { id __fusion_exports__1: id } } } }", + "document": "mutation AddReviewAndUser_1 { addReview(input: { body: \u0022foo\u0022, authorId: 1, upc: 1 }) { review { body author { id __fusion_exports__1: id } } } }", "selectionSetId": 0 }, { @@ -44,7 +44,7 @@ QueryPlan { "type": "Resolve", "subgraph": "Accounts", - "document": "query AddReviewAndUser_2 { addUser(input: { name: \u0022foo\u0022, username: \u0022foo\u0022, birthdate: \u0022abc\u0022 }) { user { name } } }", + "document": "mutation AddReviewAndUser_2 { addUser(input: { name: \u0022foo\u0022, username: \u0022foo\u0022, birthdate: \u0022abc\u0022 }) { user { name } } }", "selectionSetId": 0 }, {