[Fusion] Support parent entity calls in satisfiability validator#9792
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for "parent entity calls" in the Fusion satisfiability validator so that keyless types reachable via an ancestor field (e.g. Product.category where Category has no lookup in the target schema) are accepted by walking up the access path to an ancestor whose declaring type has a lookup in the target schema and validating that lookup's {key} requirement against the prefix position. The duplicated source-schema transition logic in SatisfiabilityValidator and RequirementsValidator is factored into a new SourceSchemaTransitionHelper so the new branch lands cleanly in one place, and a previously skipped federation-gateway-audit test is enabled.
Changes:
- Introduce
SourceSchemaTransitionHelperwith linear flow: direct lookup → parent entity call (with key-requirement validation) → one-to-one path fallback → error. - Replace duplicated
ValidateSourceSchemaTransition/CanTransitionToSchemaThroughPathbodies inSatisfiabilityValidatorandRequirementsValidatorwith calls into the helper, and add aPathNode.EnumerateFromLeaf()extension used by the satisfiability caller. - Un-skip
SatisfiabilityValidatorTests.ParentEntityCall.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/HotChocolate/Fusion/src/Fusion.Composition/Satisfiability/SourceSchemaTransitionHelper.cs |
New shared helper centralizing direct lookup, parent-call, and path-fallback transition checks. |
src/HotChocolate/Fusion/src/Fusion.Composition/SatisfiabilityValidator.cs |
ValidateSourceSchemaTransition now delegates to the helper, passing a leaf-first path and a requirement-validation closure. |
src/HotChocolate/Fusion/src/Fusion.Composition/Satisfiability/RequirementsValidator.cs |
Same delegation, using RequirementsValidator_* resx keys and threading cycle-detection through the closure. |
src/HotChocolate/Fusion/src/Fusion.Composition/Extensions/PathNodeExtensions.cs |
Adds EnumerateFromLeaf() extension used to materialize a leaf-first path list. |
src/HotChocolate/Fusion/test/Fusion.Composition.Tests/SatisfiabilityValidatorTests.cs |
Removes the Skip on ParentEntityCall now that the validator accepts the case. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Categoryreached viaProduct.category), the validator now walks the access path back to the nearest ancestor whose declaring type has a lookup in the target schema, validates that lookup's{key}requirement against the prefix position, and accepts the transition if it succeeds. This unblocks the federation-gateway-auditparent-entity-callsuite.ValidateSourceSchemaTransitionand path-traversal logic fromSatisfiabilityValidatorandRequirementsValidatorinto a newSourceSchemaTransitionHelper, with each caller passing in its own resx keys and requirement-validation closure. The two near-duplicates had already drifted; consolidating prevents future drift and is where the new edge lands cleanly.lookupDirectives.Count == 0branch into one linear flow: direct lookup → strict parent-call → one-to-one path fallback → error. The old branch'sCanTransitionThroughPathmixed two jobs — "some ancestor on the path has a lookup + field in the target schema" and "every field on the path exists in the target schema" — gated only on the type having no direct lookup. The first job is the same shape as the new parent-call and is now handled by it (with key-requirement validation rather than existence-only); the second remains as the trailing one-to-one fallback. The existingType_Without_Lookup_But_Parent_Has_Lookuptest now passes via the validated parent-call instead of the existence check.ParentEntityCallinSatisfiabilityValidatorTests.@require,@partial,@provides). The main satisfiability loop covers most cases implicitly, but a theoretical gap exists when this edge enables a(type, schema)combination the forward-from-Query traversal does not reach. Closing that gap properly would require restructuring the validator around fixed-point reachability over(type, schema)nodes with lookups as additional entry points; tracking as a Validator V2 follow-up rather than expanding this PR.Test plan
dotnet test src/HotChocolate/Fusion/test/Fusion.Composition.Tests --filter "FullyQualifiedName~SatisfiabilityValidatorTests|FullyQualifiedName~RequirementsValidatorTests"— 63 passed, 1 skipped (RequiresCircular, separate blocker), 0 failed on net8.0, net9.0, and net10.0.HotChocolate.Fusion.Composition.Tests— 497 passed, 1 skipped, 0 failed across all three TFMs.