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

[Fusion] @semanticNonNull composition support #7731

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
@@ -1,3 +1,4 @@
using HotChocolate.Language;
using HotChocolate.Skimmed;
using static HotChocolate.Fusion.Composition.MergeExtensions;

Expand Down Expand Up @@ -64,6 +65,8 @@ public static void MergeField(
return;
}

MergeSemanticNonNullability(context, source, target);

if (!mergedType.Equals(target.Type, TypeComparison.Structural))
{
target.Type = mergedType;
Expand Down Expand Up @@ -101,7 +104,7 @@ public static void MergeField(
return;
}

if(!targetArgument.Type.Equals(mergedInputType, TypeComparison.Structural))
if (!targetArgument.Type.Equals(mergedInputType, TypeComparison.Structural))
{
targetArgument.Type = mergedInputType;
}
Expand All @@ -128,7 +131,7 @@ public static void MergeField(
// If the target field is not deprecated and the source field is deprecated, copy over the
target.MergeDeprecationWith(source);

target.MergeDirectivesWith(source, context);
target.MergeDirectivesWith(source, context, shouldApplySemanticNonNull: false);

foreach (var sourceArgument in source.Arguments)
{
Expand All @@ -151,4 +154,118 @@ public static void MergeField(
}
}
}

private static void MergeSemanticNonNullability(
this CompositionContext context,
OutputFieldDefinition source,
OutputFieldDefinition target)
{
var sourceSemanticNonNullLevels = GetSemanticNonNullLevels(source);
var targetSemanticNonNullLevels = GetSemanticNonNullLevels(target);

if (sourceSemanticNonNullLevels.Count < 1 && targetSemanticNonNullLevels.Count < 1)
{
return;
}

List<int> levels = [];

var currentLevel = 0;
var currentSourceType = source.Type;
var currentTargetType = target.Type;
while (true)
{
if (currentTargetType is NonNullTypeDefinition targetNonNullType)
{
if (currentSourceType is not NonNullTypeDefinition)
{
if (sourceSemanticNonNullLevels.Contains(currentLevel))
{
// Non-Null + Semantic Non-Null case
levels.Add(currentLevel);
}
}

currentTargetType = targetNonNullType.NullableType;
}
else if (targetSemanticNonNullLevels.Contains(currentLevel))
{
if (currentSourceType is NonNullTypeDefinition || sourceSemanticNonNullLevels.Contains(currentLevel))
{
// Semantic Non-Null + (Non-Null || Semantic Non-Null) case
levels.Add(currentLevel);
}
}

if (currentSourceType is NonNullTypeDefinition sourceNonNullType)
{
currentSourceType = sourceNonNullType.NullableType;
}

if (currentTargetType is ListTypeDefinition targetListType)
{
currentTargetType = targetListType.ElementType;

if (currentSourceType is ListTypeDefinition sourceListType)
{
currentSourceType = sourceListType.ElementType;
}

currentLevel++;

continue;
}

break;
}

var targetSemanticNonNullDirective = target.Directives
.FirstOrDefault(d => d.Name == BuiltIns.SemanticNonNull.Name);

if (targetSemanticNonNullDirective is not null)
{
target.Directives.Remove(targetSemanticNonNullDirective);
}

if (levels.Count < 1)
{
return;
}

if (context.FusionGraph.DirectiveDefinitions.TryGetDirective(BuiltIns.SemanticNonNull.Name,
out var semanticNonNullDirectiveDefinition))
{
if (levels is [0])
{
target.Directives.Add(new Directive(semanticNonNullDirectiveDefinition));
}
else
{
var levelsValueNode = new ListValueNode(levels.Select(l => new IntValueNode(l)).ToList());
var levelsArgument = new ArgumentAssignment(BuiltIns.SemanticNonNull.Levels, levelsValueNode);
target.Directives.Add(new Directive(semanticNonNullDirectiveDefinition, levelsArgument));
}
}
}

private static List<int> GetSemanticNonNullLevels(IDirectivesProvider provider)
{
var directive = provider.Directives
.FirstOrDefault(d => d.Name == BuiltIns.SemanticNonNull.Name);

if (directive is null)
{
return [];
}

if (directive.Arguments.TryGetValue(BuiltIns.SemanticNonNull.Levels, out var levelsArg))
{
if (levelsArg is ListValueNode listValueNode)
{
return listValueNode.Items.Cast<IntValueNode>().Select(i => i.ToInt32()).ToList();
}
}

return [0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ internal static void MergeDescriptionWith(this INamedTypeDefinition target, INam
internal static void MergeDirectivesWith(
this IDirectivesProvider target,
IDirectivesProvider source,
CompositionContext context)
CompositionContext context,
bool shouldApplySemanticNonNull = true)
{
foreach (var directive in source.Directives)
{
Expand All @@ -181,6 +182,11 @@ internal static void MergeDirectivesWith(
continue;
}

if (directive.Name == BuiltIns.SemanticNonNull.Name && !shouldApplySemanticNonNull)
{
continue;
}

context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition);

if (!target.Directives.ContainsName(directive.Name))
Expand Down
Loading
Loading