Skip to content

Commit

Permalink
Add basic plan serialization (#7785)
Browse files Browse the repository at this point in the history
  • Loading branch information
tobias-tengler authored Dec 1, 2024
1 parent 8b12f30 commit 0276674
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 103 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace HotChocolate.Fusion.Planning.Nodes;

public interface IPlanNodeProvider
{
public IReadOnlyList<PlanNode> Nodes { get; }

public void AddChildNode(PlanNode node);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Text.Json;

namespace HotChocolate.Fusion.Planning;

public interface ISerializablePlanNode
{
PlanNodeKind Kind { get; }

void Serialize(Utf8JsonWriter writer);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text.Json;
using HotChocolate.Fusion.Types;
using HotChocolate.Language;

Expand All @@ -6,11 +7,11 @@ namespace HotChocolate.Fusion.Planning.Nodes;
/// <summary>
/// Represents an operation to resolve data from a specific source schema.
/// </summary>
public sealed class OperationPlanNode : SelectionPlanNode, IOperationPlanNodeProvider
public sealed class OperationPlanNode : SelectionPlanNode, IPlanNodeProvider, ISerializablePlanNode
{
private static readonly IReadOnlyDictionary<string, VariableDefinitionNode> _emptyVariableMap =
new Dictionary<string, VariableDefinitionNode>();
private List<OperationPlanNode>? _operations;
private readonly List<PlanNode> _nodes = [];
private Dictionary<string, VariableDefinitionNode>? _variables;

public OperationPlanNode(
Expand Down Expand Up @@ -43,20 +44,19 @@ public OperationPlanNode(
public IReadOnlyDictionary<string, VariableDefinitionNode> VariableDefinitions
=> _variables ?? _emptyVariableMap;

public IReadOnlyList<OperationPlanNode> Operations
=> _operations ?? (IReadOnlyList<OperationPlanNode>)Array.Empty<OperationPlanNode>();
public IReadOnlyList<PlanNode> Nodes => _nodes;

public void AddVariableDefinition(VariableDefinitionNode variable)
{
ArgumentNullException.ThrowIfNull(variable);
(_variables ??= new Dictionary<string, VariableDefinitionNode>()).Add(variable.Variable.Name.Value, variable);
}

public void AddOperation(OperationPlanNode operation)
public void AddChildNode(PlanNode node)
{
ArgumentNullException.ThrowIfNull(operation);
(_operations ??= []).Add(operation);
operation.Parent = this;
ArgumentNullException.ThrowIfNull(node);
_nodes.Add(node);
node.Parent = this;
}

public OperationDefinitionNode ToSyntaxNode()
Expand All @@ -69,4 +69,16 @@ public OperationDefinitionNode ToSyntaxNode()
Directives.ToSyntaxNode(),
Selections.ToSyntaxNode());
}

public PlanNodeKind Kind => PlanNodeKind.Operation;

public void Serialize(Utf8JsonWriter writer)
{
writer.WriteStartObject();
SerializationHelper.WriteKind(writer, this);
writer.WriteString("schema", SchemaName);
writer.WriteString("document", ToSyntaxNode().ToString(indented: false));
SerializationHelper.WriteChildNodes(writer, this);
writer.WriteEndObject();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace HotChocolate.Fusion.Planning;

public enum PlanNodeKind
{
Root,
Operation
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
using System.Collections.Immutable;
using HotChocolate.Language;
using System.Text;
using System.Text.Json;

namespace HotChocolate.Fusion.Planning.Nodes;

public sealed class RootPlanNode : PlanNode, IOperationPlanNodeProvider
public sealed class RootPlanNode : PlanNode, IPlanNodeProvider, ISerializablePlanNode
{
private readonly List<OperationPlanNode> _operations = new();
private static readonly JsonWriterOptions SerializerOptions = new()
{
Indented = true,
};
private readonly List<PlanNode> _nodes = [];

public IReadOnlyList<OperationPlanNode> Operations
=> _operations;
public IReadOnlyList<PlanNode> Nodes => _nodes;

public void AddOperation(OperationPlanNode operation)
public void AddChildNode(PlanNode node)
{
ArgumentNullException.ThrowIfNull(operation);
_operations.Add(operation);
operation.Parent = this;
ArgumentNullException.ThrowIfNull(node);
_nodes.Add(node);
node.Parent = this;
}

public DocumentNode ToSyntaxNode()
public PlanNodeKind Kind => PlanNodeKind.Root;

public string Serialize()
{
var backlog = new Queue<OperationPlanNode>();
var definitions = ImmutableArray.CreateBuilder<IDefinitionNode>();
using var memoryStream = new MemoryStream();
var jsonWriter = new Utf8JsonWriter(memoryStream, SerializerOptions);

foreach(var operation in _operations)
{
backlog.Enqueue(operation);
}
Serialize(jsonWriter);

while(backlog.TryDequeue(out var operation))
{
definitions.Add(operation.ToSyntaxNode());
return Encoding.UTF8.GetString(memoryStream.ToArray());
}

foreach(var child in operation.Operations)
{
backlog.Enqueue(child);
}
}
public void Serialize(Utf8JsonWriter writer)
{
writer.WriteStartObject();
SerializationHelper.WriteKind(writer, this);
SerializationHelper.WriteChildNodes(writer, this);
writer.WriteEndObject();

return new DocumentNode(definitions.ToImmutable());
writer.Flush();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Text.Json;

namespace HotChocolate.Fusion.Planning.Nodes;

public static class SerializationHelper
{
public static void WriteChildNodes(Utf8JsonWriter writer, IPlanNodeProvider planNodeProvider)
{
if (planNodeProvider.Nodes.Count == 0)
{
return;
}

writer.WritePropertyName("nodes");
writer.WriteStartArray();

foreach (var node in planNodeProvider.Nodes.OfType<ISerializablePlanNode>())
{
node.Serialize(writer);
}

writer.WriteEndArray();
}

public static void WriteKind(Utf8JsonWriter writer, ISerializablePlanNode serializablePlanNode)
{
writer.WriteString("kind", serializablePlanNode.Kind.ToString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public RootPlanNode CreatePlan(DocumentNode document, string? operationName)

if (TryPlanSelectionSet(operation, operation, new Stack<SelectionPathSegment>()))
{
operationPlan.AddOperation(operation);
operationPlan.AddChildNode(operation);
}
}

Expand Down Expand Up @@ -191,7 +191,7 @@ private bool TryHandleUnresolvedSelections(
continue;
}

operation.AddOperation(lookupOperation);
operation.AddChildNode(lookupOperation);

foreach (var selection in lookupField.Selections)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static void BindOperationVariables(
var variableDefinitions = operationDefinition.VariableDefinitions.ToDictionary(t => t.Variable.Name.Value);
var usedVariables = new HashSet<string>();

foreach (var operation in operationPlan.Operations)
foreach (var operation in operationPlan.Nodes.OfType<OperationPlanNode>())
{
operationBacklog.Push(operation);
}
Expand All @@ -23,7 +23,7 @@ public static void BindOperationVariables(
{
CollectAndBindUsedVariables(operation, variableDefinitions, usedVariables, selectionBacklog);

foreach (var child in operation.Operations)
foreach (var child in operation.Nodes.OfType<OperationPlanNode>())
{
operationBacklog.Push(child);
}
Expand Down
Loading

0 comments on commit 0276674

Please sign in to comment.