Skip to content

Commit 5a926fa

Browse files
N-Olbertglen-84
andauthored
Use correct type name when referencing type with ID<Type> (#8504)
Co-authored-by: Glen <[email protected]>
1 parent f4198b2 commit 5a926fa

File tree

8 files changed

+408
-73
lines changed

8 files changed

+408
-73
lines changed

src/HotChocolate/Core/src/Types/Types/Relay/Attributes/IDAttribute.cs

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -168,31 +168,8 @@ public class IDAttribute<T> : DescriptorAttribute
168168
/// <inheritdoc cref="IDAttribute{T}"/>
169169
public IDAttribute()
170170
{
171-
TypeName = typeof(T).Name;
172171
}
173172

174-
/// <summary>
175-
/// With the <see cref="IDAttribute.TypeName"/> property you can override the type name
176-
/// of the ID. This is useful to rewrite a parameter of a mutation or query, to a specific
177-
/// id.
178-
/// </summary>
179-
/// <example>
180-
/// <para>
181-
/// A field can be rewritten to an ID by adding <c>[ID]</c> to the resolver.
182-
/// </para>
183-
/// <code>
184-
/// public class UserQuery
185-
/// {
186-
/// public User GetUserById([ID("User")] int id) => //....
187-
/// }
188-
/// </code>
189-
/// <para>
190-
/// The argument is rewritten to <c>ID</c> and expect an ID of type User.
191-
/// Assuming `<c>User.id</c>` has the value 1. The following string is base64 encoded
192-
/// </para>
193-
/// </example>
194-
public string? TypeName { get; }
195-
196173
/// <inheritdoc />
197174
protected internal override void TryConfigure(
198175
IDescriptorContext context,
@@ -202,13 +179,13 @@ protected internal override void TryConfigure(
202179
switch (descriptor)
203180
{
204181
case IInputFieldDescriptor d:
205-
d.ID(TypeName);
182+
d.ID<T>();
206183
break;
207184
case IArgumentDescriptor d:
208-
d.ID(TypeName);
185+
d.ID<T>();
209186
break;
210187
case IObjectFieldDescriptor d:
211-
d.ID(TypeName);
188+
d.ID<T>();
212189
break;
213190
case IInterfaceFieldDescriptor d:
214191
d.ID();

src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,15 @@ public static IInputFieldDescriptor ID(
7474
return descriptor;
7575
}
7676

77-
/// <inheritdoc cref="RelayIdFieldExtensions"/>
78-
/// <param name="descriptor">the descriptor</param>
77+
/// <inheritdoc cref="ID(IInputFieldDescriptor,string?)"/>
7978
/// <typeparam name="T">
8079
/// the type from which the <see cref="IDAttribute.TypeName">type name</see> is derived
8180
/// </typeparam>
8281
public static IInputFieldDescriptor ID<T>(this IInputFieldDescriptor descriptor)
8382
{
8483
ArgumentNullException.ThrowIfNull(descriptor);
8584

86-
RelayIdFieldHelpers.ApplyIdToField(descriptor, typeof(T).Name);
85+
RelayIdFieldHelpers.ApplyIdToField<T>(descriptor);
8786

8887
return descriptor;
8988
}
@@ -104,16 +103,15 @@ public static IArgumentDescriptor ID(
104103
return descriptor;
105104
}
106105

107-
/// <inheritdoc cref="RelayIdFieldExtensions"/>
108-
/// <param name="descriptor">the descriptor</param>
106+
/// <inheritdoc cref="ID(IInputFieldDescriptor,string?)"/>
109107
/// <typeparam name="T">
110108
/// the type from which the <see cref="IDAttribute.TypeName">type name</see> is derived
111109
/// </typeparam>
112110
public static IArgumentDescriptor ID<T>(this IArgumentDescriptor descriptor)
113111
{
114112
ArgumentNullException.ThrowIfNull(descriptor);
115113

116-
RelayIdFieldHelpers.ApplyIdToField(descriptor, typeof(T).Name);
114+
RelayIdFieldHelpers.ApplyIdToField<T>(descriptor);
117115

118116
return descriptor;
119117
}
@@ -134,16 +132,15 @@ public static IObjectFieldDescriptor ID(
134132
return descriptor;
135133
}
136134

137-
/// <inheritdoc cref="RelayIdFieldExtensions"/>
138-
/// <param name="descriptor">the descriptor</param>
135+
/// <inheritdoc cref="ID(IInputFieldDescriptor,string?)"/>
139136
/// <typeparam name="T">
140137
/// the type from which the <see cref="IDAttribute.TypeName">type name</see> is derived
141138
/// </typeparam>
142139
public static IObjectFieldDescriptor ID<T>(this IObjectFieldDescriptor descriptor)
143140
{
144141
ArgumentNullException.ThrowIfNull(descriptor);
145142

146-
RelayIdFieldHelpers.ApplyIdToField(descriptor, typeof(T).Name);
143+
RelayIdFieldHelpers.ApplyIdToField<T>(descriptor);
147144

148145
return descriptor;
149146
}

src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs

Lines changed: 107 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,16 @@ internal static class RelayIdFieldHelpers
2323
/// </remarks>
2424
public static void ApplyIdToField(
2525
IDescriptor<ArgumentConfiguration> descriptor,
26-
string? typeName = null)
27-
{
28-
ArgumentNullException.ThrowIfNull(descriptor);
29-
30-
var extend = descriptor.Extend();
31-
32-
// rewrite type
33-
extend.OnBeforeCreate(RewriteConfiguration);
26+
string? typeName = null) =>
27+
ApplyIdToFieldCore(descriptor, NodeIdNameDefinitionUnion.Create(typeName));
3428

35-
// add serializer if globalID support is enabled.
36-
if (extend.Context.Features.Get<NodeSchemaFeature>()?.IsEnabled == true)
37-
{
38-
extend.OnBeforeCompletion((c, d) => AddSerializerToInputField(c, d, typeName));
39-
}
40-
}
29+
/// <inheritdoc cref="ApplyIdToField(IDescriptor{ArgumentConfiguration},string?)"/>
30+
/// <typeparam name="T">
31+
/// the type from which the <see cref="IDAttribute.TypeName">type name</see> is derived
32+
/// </typeparam>
33+
public static void ApplyIdToField<T>(
34+
IDescriptor<ArgumentConfiguration> descriptor) =>
35+
ApplyIdToFieldCore(descriptor, NodeIdNameDefinitionUnion.Create<T>());
4136

4237
/// <summary>
4338
/// Applies the <see cref="RelayIdFieldExtensions"><c>.ID()</c></see> to an argument
@@ -48,24 +43,16 @@ public static void ApplyIdToField(
4843
/// </remarks>
4944
public static void ApplyIdToField(
5045
IDescriptor<OutputFieldConfiguration> descriptor,
51-
string? typeName = null)
52-
{
53-
ArgumentNullException.ThrowIfNull(descriptor);
54-
55-
// rewrite type
56-
descriptor.Extend().OnBeforeCreate(RewriteConfiguration);
46+
string? typeName = null) =>
47+
ApplyIdToFieldCore(descriptor, NodeIdNameDefinitionUnion.Create(typeName));
5748

58-
if (descriptor is IDescriptor<ObjectFieldConfiguration> objectFieldDescriptor)
59-
{
60-
var extend = objectFieldDescriptor.Extend();
61-
62-
// add serializer if globalID support is enabled.
63-
if (extend.Context.Features.Get<NodeSchemaFeature>()?.IsEnabled == true)
64-
{
65-
ApplyIdToField(extend.Configuration, typeName);
66-
}
67-
}
68-
}
49+
/// <inheritdoc cref="ApplyIdToField(IDescriptor{OutputFieldConfiguration},string?)"/>
50+
/// <typeparam name="T">
51+
/// the type from which the <see cref="IDAttribute.TypeName">type name</see> is derived
52+
/// </typeparam>
53+
public static void ApplyIdToField<T>(
54+
IDescriptor<OutputFieldConfiguration> descriptor) =>
55+
ApplyIdToFieldCore(descriptor, NodeIdNameDefinitionUnion.Create<T>());
6956

7057
/// <summary>
7158
/// Applies the <see cref="RelayIdFieldExtensions"><c>.ID()</c></see> to an argument
@@ -76,7 +63,8 @@ public static void ApplyIdToField(
7663
/// </remarks>
7764
internal static void ApplyIdToField(
7865
ObjectFieldConfiguration configuration,
79-
string? typeName = null)
66+
NodeIdNameDefinitionUnion? nameDefinition = null,
67+
TypeReference? dependsOn = null)
8068
{
8169
var placeholder = new ResultFormatterConfiguration(
8270
(_, r) => r,
@@ -89,13 +77,77 @@ internal static void ApplyIdToField(
8977
ctx,
9078
(ObjectFieldConfiguration)def,
9179
placeholder,
92-
typeName),
80+
nameDefinition),
9381
configuration,
94-
ApplyConfigurationOn.BeforeCompletion);
82+
ApplyConfigurationOn.BeforeCompletion,
83+
typeReference: dependsOn);
9584

9685
configuration.Tasks.Add(configurationTask);
9786
}
9887

88+
internal static void ApplyIdToFieldCore(
89+
IDescriptor<OutputFieldConfiguration> descriptor,
90+
NodeIdNameDefinitionUnion? nameDefinition)
91+
{
92+
ArgumentNullException.ThrowIfNull(descriptor);
93+
94+
// rewrite type
95+
descriptor.Extend().OnBeforeCreate(RewriteConfiguration);
96+
97+
if (descriptor is IDescriptor<ObjectFieldConfiguration> objectFieldDescriptor)
98+
{
99+
var extend = objectFieldDescriptor.Extend();
100+
101+
// add serializer if globalID support is enabled.
102+
if (extend.Context.Features.Get<NodeSchemaFeature>()?.IsEnabled == true)
103+
{
104+
if (nameDefinition?.Type != null)
105+
{
106+
var dependsOn = extend.Context.TypeInspector.GetTypeRef(nameDefinition.Type);
107+
ApplyIdToField(extend.Configuration, nameDefinition, dependsOn);
108+
}
109+
else
110+
{
111+
ApplyIdToField(extend.Configuration, nameDefinition);
112+
}
113+
}
114+
}
115+
}
116+
117+
public static void ApplyIdToFieldCore(
118+
IDescriptor<ArgumentConfiguration> descriptor,
119+
NodeIdNameDefinitionUnion? nameDefinition)
120+
{
121+
ArgumentNullException.ThrowIfNull(descriptor);
122+
123+
var extend = descriptor.Extend();
124+
125+
// rewrite type
126+
extend.OnBeforeCreate(RewriteConfiguration);
127+
128+
// add serializer if globalID support is enabled.
129+
if (extend.Context.Features.Get<NodeSchemaFeature>()?.IsEnabled == true)
130+
{
131+
if (nameDefinition?.Type == null)
132+
{
133+
extend.OnBeforeCompletion((c, d) =>
134+
AddSerializerToInputField(c, d, nameDefinition));
135+
}
136+
else
137+
{
138+
var dependsOn = extend.Context.TypeInspector.GetTypeRef(nameDefinition.Type);
139+
140+
var configurationTask = new OnCompleteTypeSystemConfigurationTask(
141+
(ctx, def) => AddSerializerToInputField(ctx, (ArgumentConfiguration)def, nameDefinition),
142+
extend.Configuration,
143+
ApplyConfigurationOn.BeforeCompletion,
144+
typeReference: dependsOn);
145+
146+
extend.Configuration.Tasks.Add(configurationTask);
147+
}
148+
}
149+
}
150+
99151
private static void RewriteConfiguration(
100152
IDescriptorContext context,
101153
FieldConfiguration configuration)
@@ -137,7 +189,7 @@ private static IExtendedType RewriteType(ITypeInspector typeInspector, ITypeInfo
137189
internal static void AddSerializerToInputField(
138190
ITypeCompletionContext completionContext,
139191
ArgumentConfiguration configuration,
140-
string? typeName)
192+
NodeIdNameDefinitionUnion? nameDefinition)
141193
{
142194
var typeInspector = completionContext.TypeInspector;
143195
IExtendedType? resultType;
@@ -165,6 +217,8 @@ internal static void AddSerializerToInputField(
165217
completionContext.Type);
166218
}
167219

220+
var typeName = GetIdTypeName(completionContext, nameDefinition, typeInspector);
221+
168222
var validateType = typeName is not null;
169223
typeName ??= completionContext.Type.Name;
170224
SetSerializerInfos(completionContext.DescriptorContext, typeName, resultType);
@@ -176,7 +230,7 @@ private static void AddSerializerToObjectField(
176230
ITypeCompletionContext completionContext,
177231
ObjectFieldConfiguration configuration,
178232
ResultFormatterConfiguration placeholder,
179-
string? typeName)
233+
NodeIdNameDefinitionUnion? nameDefinition)
180234
{
181235
var typeInspector = completionContext.TypeInspector;
182236
IExtendedType? resultType;
@@ -199,6 +253,8 @@ private static void AddSerializerToObjectField(
199253
var serializerAccessor = completionContext.DescriptorContext.NodeIdSerializerAccessor;
200254
var index = configuration.FormatterConfigurations.IndexOf(placeholder);
201255

256+
var typeName = GetIdTypeName(completionContext, nameDefinition, typeInspector);
257+
202258
typeName ??= completionContext.Type.Name;
203259
SetSerializerInfos(completionContext.DescriptorContext, typeName, resultType);
204260

@@ -279,4 +335,19 @@ internal static void SetSerializerInfos(IDescriptorContext context, string typeN
279335
var feature = context.Features.GetOrSet<NodeSchemaFeature>();
280336
feature.NodeIdTypes.TryAdd(typeName, runtimeTypeInfo.NamedType);
281337
}
338+
339+
private static string? GetIdTypeName(ITypeCompletionContext completionContext,
340+
NodeIdNameDefinitionUnion? nameDefinition,
341+
ITypeInspector typeInspector)
342+
{
343+
var typeName = nameDefinition?.Literal;
344+
if (nameDefinition?.Type is { } t)
345+
{
346+
var referencedType = typeInspector.GetType(t);
347+
var foo = completionContext.GetType<IType>(TypeReference.Create(referencedType));
348+
typeName = foo.NamedType().Name;
349+
}
350+
351+
return typeName;
352+
}
282353
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace HotChocolate.Types.Relay;
2+
3+
/// <summary>
4+
/// A discriminated union, containing either a literal or a type that defines
5+
/// the name of the node identifier.
6+
/// </summary>
7+
internal record NodeIdNameDefinitionUnion(string? Literal, Type? Type)
8+
{
9+
public static NodeIdNameDefinitionUnion? Create(string? literal) =>
10+
literal == null ? null : new NodeIdNameDefinitionUnion(literal, null);
11+
12+
public static NodeIdNameDefinitionUnion Create<T>() =>
13+
new NodeIdNameDefinitionUnion(null, typeof(T));
14+
}

src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public override void OnAfterMergeTypeExtensions()
162162
RelayIdFieldHelpers.AddSerializerToInputField(
163163
CompletionContext,
164164
argument,
165-
fieldTypeDef.Name);
165+
NodeIdNameDefinitionUnion.Create(fieldTypeDef.Name));
166166

167167
// As with the id argument, we also want to make sure that the ID field of
168168
// the field result type is a non-null ID type.

0 commit comments

Comments
 (0)