Skip to content

Keep generated Fusion operation names valid for every hash format - #10388

Merged
glen-84 merged 3 commits into
mainfrom
gai/fusion-name-safe-operation-short-hash
Sep 14, 2026
Merged

glen-84 merged 3 commits into
mainfrom
gai/fusion-name-safe-operation-short-hash

Conversation

@glen-84

@glen-84 glen-84 commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

Summary

The gateway names every operation it sends to a source schema {clientOperationName}_{shortHash}_{stepId}, where the short hash is the first eight characters of the client document hash as rendered by the configured IDocumentHashProvider. HashFormat.Base64 yields URL-safe Base64, whose - is not a GraphQL name character, so roughly one document in four produced an unparsable name. The failure landed in the gateway rather than at the source schema, because OperationExecutionNode parses the operation text the planner generates. A client-supplied hash reaches the same code path.

SyntaxException : Invalid number, expected digit but got: `_` (95)
  at Utf8GraphQLOperationParser.ParseOperationDefinition
  at OperationExecutionNode..ctor
  at OperationPlanner.CreatePlan

Characters a GraphQL name cannot hold are now replaced with an underscore at the two places a name is built, PlanNode.CreateOperationName and AliasBatchedRequestBody. The hash itself is never rewritten, so Operation.ShortHash, PlanNode.ShortHash, and the shortHash field of the serialized operation plan all keep the real document hash prefix and stay correlatable with the hash the gateway reports.

Digits are kept in every position. The short hash always sits behind Op_ or the client operation name, so GraphQL's NameStart rule does not reach it, and NameUtils.MakeValidGraphQLName applies the whole-name grammar instead and would rewrite 94a407d5 to _4a407d5. A hex short hash is returned unchanged and allocates nothing.

Test plan

  • GeneratedOperationNameTests gains the counterpart to CreatePlan_Should_KeepTheShortHash_When_ItHoldsNameCharactersOnly, covering the generated name and, separately, that Operation.ShortHash still reports the unmodified hash.
  • AliasBatchedRequestBodyTests covers the merged batch name for a non-name short hash, which had no assertion before.
  • OperationShortHashTests covers the character classes and pins the leading-digit contract. Mutating the helper to the whole-name rule turns that test red with _4a407d5.
  • dotnet test src/HotChocolate/Fusion/test/Fusion.Execution.Tests passes, 1147 tests.
  • Fusion.AspNetCore.Tests, Fusion.Diagnostics.Tests, and Fusion.Connectors.InMemory.Tests pass unchanged. No snapshot moved, because a hex short hash is returned as-is.
  • dotnet build src/HotChocolate/Fusion -p:EnforceCodeStyleInBuild=true reports no errors.

Copilot AI lite review requested due to automatic review settings September 14, 2026 09:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

One or more issues must be addressed before approval.

Pull request overview

Normalizes Fusion operation short hashes so generated GraphQL operation names remain parseable.

Changes:

  • Adds targeted short-hash normalization.
  • Applies normalized hashes throughout operation planning.
  • Adds coverage for invalid characters and leading digits.
File summaries
File Description
src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Planning/OperationShortHashTests.cs Updated as part of this pull request.
src/HotChocolate/Fusion/test/Fusion.Execution.Tests/Planning/GeneratedOperationNameTests.cs Updated as part of this pull request.
src/HotChocolate/Fusion/src/Fusion.Execution/Planning/OperationShortHash.cs Updated as part of this pull request.
src/HotChocolate/Fusion/src/Fusion.Execution/Planning/OperationPlanner.cs Updated as part of this pull request.
Review details

Suppressed comments (4)

src/HotChocolate/Fusion/src/Fusion.Execution/Planning/OperationPlanner.cs:82

  • This comment says the generated names embed the short hash verbatim, but the next line deliberately replaces characters that are not valid in a GraphQL name. Please update the comment to describe the normalization step so it does not contradict the behavior or the normalized Operation.ShortHash value.
        // The generated source schema operation names embed the short hash verbatim.

src/HotChocolate/Fusion/src/Fusion.Execution/Planning/OperationPlanner.cs:83

  • This enforces the invariant only on the planner path. JsonOperationPlanParser.ParseOperation and other direct OperationCompiler.Compile callers bypass CreatePlan, so an unsafe serialized shortHash can still reach alias batching, where operation.ShortHash is written directly into the merged GraphQL operation name. Apply the same normalization at the common operation-compilation path as well, while retaining this planner-side normalization for PlanNode.CreateOperationName.
        // The generated source schema operation names embed the short hash verbatim.
        shortHash = OperationShortHash.ToNameSafe(shortHash);

src/HotChocolate/Fusion/src/Fusion.Execution/Planning/OperationPlanner.cs:83

  • The cancellation guard is intentionally before planning work, and OperationPlannerCancellationTests covers the already-canceled case. This normalization now scans and, for a non-name hash, allocates before that guard, so canceled requests do work the method explicitly promises to avoid; move the normalization below ThrowIfCancellationRequested().
        // The generated source schema operation names embed the short hash verbatim.
        shortHash = OperationShortHash.ToNameSafe(shortHash);

src/HotChocolate/Fusion/src/Fusion.Execution/Planning/OperationPlanner.cs:83

  • Normalizing the parameter here changes Operation.ShortHash from the first eight characters of the original document hash into a lossy value. That property is public and is serialized as shortHash, while Operation.Hash remains unchanged, so hashes containing -, /, or + can no longer be correlated by consumers using shortHash as the hash prefix. Keep the original short hash for operation metadata and carry a separate name-safe value for PlanNode and alias batching, or explicitly revise this public contract.
        shortHash = OperationShortHash.ToNameSafe(shortHash);
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Patch coverage

100.0% of changed lines covered (31/31)

File Covered Changed Patch %
…/Execution/Clients/AliasBatching/AliasBatchedRequestBody.cs 3 3 100.0% 🟢
…/src/Fusion.Execution/Execution/Nodes/OperationShortHash.cs 25 25 100.0% 🟢
…/Fusion/src/Fusion.Execution/Planning/PlanNode.cs 3 3 100.0% 🟢

Project coverage: 57.9% (288327/497915 lines)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The remaining documentation nit is non-blocking, and the reviewed behavior is covered by tests.

Review details

Suppressed comments (1)

src/HotChocolate/Fusion/src/Fusion.Execution/Execution/Nodes/OperationCompiler.cs:68

  • This normalization changes Operation.ShortHash from the supplied short hash to a rewritten value (for example, BnjEJe8- becomes BnjEJe8_), while its contract still says it is the short hash of the original document (Execution/Nodes/Operation.cs:90-93). Please update that public property's documentation, and any related serialization contract documentation, to describe the name-safe value returned after this change.
        // Operation.ShortHash is embedded in the alias-batched source schema operation name.
        shortHash = OperationShortHash.ToNameSafe(shortHash);
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@glen-84 glen-84 changed the title Make the Fusion operation short hash GraphQL-name-safe Keep generated Fusion operation names valid for every hash format Sep 14, 2026
@glen-84
glen-84 requested a lite review from Copilot September 14, 2026 10:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No unresolved blocking issues were identified, and focused tests cover the changed behavior.

Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@glen-84
glen-84 merged commit e30737e into main Sep 14, 2026
152 checks passed
@glen-84
glen-84 deleted the gai/fusion-name-safe-operation-short-hash branch September 14, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants