Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -79,6 +79,9 @@ public OperationPlan CreatePlan(
ArgumentException.ThrowIfNullOrEmpty(shortHash);
ArgumentNullException.ThrowIfNull(operationDefinition);

// The generated source schema operation names embed the short hash verbatim.
shortHash = OperationShortHash.ToNameSafe(shortHash);

// We make sire that the cancellation token is observed right at the beginning of the method,
// so that if the caller passed in an already canceled token we don't do any unnecessary work.
cancellationToken.ThrowIfCancellationRequested();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace HotChocolate.Fusion.Planning;

/// <summary>
/// Provides the short operation document hash that generated source schema operation names carry.
/// </summary>
internal static class OperationShortHash
{
/// <summary>
/// Replaces every character of a short hash that a GraphQL name cannot hold with an underscore.
/// Digits are kept in every position: a short hash is embedded in a name, never used as one.
/// </summary>
/// <param name="shortHash">
/// The short hash of the operation document.
/// </param>
/// <returns>
/// <paramref name="shortHash"/> itself when it holds GraphQL name characters only.
/// </returns>
public static string ToNameSafe(string shortHash)
{
var index = IndexOfNonNameCharacter(shortHash);

if (index < 0)
{
return shortHash;
}

var buffer = shortHash.ToCharArray();

for (var i = index; i < buffer.Length; i++)
{
if (!IsNameCharacter(buffer[i]))
{
buffer[i] = '_';
}
}

return new string(buffer);
}

private static int IndexOfNonNameCharacter(string shortHash)
{
for (var i = 0; i < shortHash.Length; i++)
{
if (!IsNameCharacter(shortHash[i]))
{
return i;
}
}

return -1;
}

private static bool IsNameCharacter(char value)
=> value is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '0' and <= '9' or '_';
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ public void CreatePlan_Should_KeepTheShortHash_When_ItHoldsNameCharactersOnly()
Assert.Equal("Op_abcdef12_1", GetOperationName(plan));
}

[Fact]
public void CreatePlan_Should_ReplaceTheCharacter_When_TheShortHashHoldsANonNameCharacter()
{
// arrange
var schema = CreateCompositeSchema();

// act
var plan = CreatePlan(schema, "BnjEJe8-");

// assert
Assert.Equal("Op_BnjEJe8__1", GetOperationName(plan));
}

[Fact]
public void CreatePlan_Should_ReplaceTheCharacter_When_TheOperationShortHashIsRead()
{
// arrange
var schema = CreateCompositeSchema();

// act
var plan = CreatePlan(schema, "BnjEJe8-");

// assert
Assert.Equal("BnjEJe8_", plan.Operation.ShortHash);
}

private static string GetOperationName(OperationPlan plan)
=> plan.AllNodes.OfType<OperationExecutionNode>().First().Operation.Name;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace HotChocolate.Fusion.Planning;

public sealed class OperationShortHashTests
{
[Fact]
public void ToNameSafe_Should_ReturnTheSameInstance_When_TheShortHashHoldsNameCharactersOnly()
{
// arrange
const string shortHash = "f26ee48d";

// act
var nameSafe = OperationShortHash.ToNameSafe(shortHash);

// assert
Assert.Same(shortHash, nameSafe);
}

[Fact]
public void ToNameSafe_Should_KeepTheLeadingDigit_When_TheShortHashStartsWithOne()
{
// arrange
const string shortHash = "94a407d5";

// act
var nameSafe = OperationShortHash.ToNameSafe(shortHash);

// assert
Assert.Equal("94a407d5", nameSafe);
}

[Theory]
[InlineData("BnjEJe8-", "BnjEJe8_")]
[InlineData("-BnjEJe8", "_BnjEJe8")]
[InlineData("--------", "________")]
[InlineData("Bnj.Je8+", "Bnj_Je8_")]
[InlineData("BnjéJe8/", "Bnj_Je8_")]
public void ToNameSafe_Should_ReplaceTheCharacter_When_AGraphQLNameCannotHoldIt(
string shortHash,
string expected)
{
// act
var nameSafe = OperationShortHash.ToNameSafe(shortHash);

// assert
Assert.Equal(expected, nameSafe);
}
}
Loading