Skip to content

fix: handle fully external, unresolvable entity keys#2568

Merged
Aenimus merged 4 commits intomainfrom
david/eng-9040-validate-true-external-keys
Feb 27, 2026
Merged

fix: handle fully external, unresolvable entity keys#2568
Aenimus merged 4 commits intomainfrom
david/eng-9040-validate-true-external-keys

Conversation

@Aenimus
Copy link
Copy Markdown
Member

@Aenimus Aenimus commented Feb 27, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved resolvability behavior so external fields no longer cause incorrect cross-subgraph jumps and produce clearer error messages.
  • Chores

    • Enhanced tracking of external subgraph relationships and resolved selection paths to make resolution more accurate.
  • Tests

    • Added coverage for external key-field scenarios and related resolvability cases.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 27, 2026

Walkthrough

Adds tracking for external edges/field-sets and resolved selection paths across the resolvability graph, propagating external-subgraph names into resolution data and adjusting traversal to short-circuit or record external resolution outcomes.

Changes

Cohort / File(s) Summary
Graph node & core types
composition/src/resolvability-graph/graph-nodes.ts, composition/src/resolvability-graph/types/types.ts, composition/src/resolvability-graph/types/params.ts
Added Edge.isExternal and Edge.isEdgeInaccessible(); added GraphNode.externalFieldSets; extended VisitNodeResult and VisitEntityParams with external/resolved-path tracking.
Graph traversal
composition/src/resolvability-graph/graph.ts
Threaded new resolvedPaths through visitEntity and related flows to enable resolved-path propagation during resolvability analysis.
Entity walker
composition/src/resolvability-graph/walker/entity-walker/entity-walker.ts, composition/src/resolvability-graph/walker/entity-walker/types/params.ts
Added resolvedPaths to EntityWalker; detect/short-circuit external edges; propagate external subgraph names; update unresolvable path bookkeeping to consult/mark resolvedPaths.
Root-field walkers
composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts, composition/src/resolvability-graph/walker/root-field-walkers/types/params.ts
Use isEdgeInaccessible() and propagate isExternal through visitEdge/propagateVisitedField so external edges record external-subgraph relationships and avoid local descendant resolution.
Node resolution data
composition/src/resolvability-graph/node-resolution-data/node-resolution-data.ts, composition/src/resolvability-graph/node-resolution-data/types/params.ts
Refactored addData() to destructured params; added addExternalSubgraphName() and AddExternalSubgraphNameParams to record external subgraph names on fields.
Error reasoning & utils
composition/src/resolvability-graph/utils/utils.ts
Expanded UnresolvableFieldData to use FieldName/SubgraphName/TypeName and new externalSubgraphNames; error message generation now distinguishes external vs non-external subgraph availability.
Normalization & federation data
composition/src/v1/normalization/utils.ts, composition/src/v1/normalization/normalization-factory.ts, composition/src/v1/federation/federation-factory.ts, composition/src/utils/types.ts
Track unconditionally external key fields and mark corresponding edges/graphNode.externalFieldSets; added externalSubgraphNames to GraphFieldData and migrated subgraph-name fields to SubgraphName types.
Tests
composition/tests/v1/resolvability.test.ts
Updated test UnresolvableFieldData to include externalSubgraphNames; added four new subgraph fixtures and test cases exercising external/@external key scenarios.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: handling fully external, unresolvable entity keys, which aligns with the substantial modifications throughout the codebase to track external subgraph relationships and validate external entity keys.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 27, 2026

Codecov Report

❌ Patch coverage is 92.02899% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.53%. Comparing base (9b69e59) to head (5827269).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...aph/walker/root-field-walkers/root-field-walker.ts 72.22% 5 Missing ⚠️
...bility-graph/walker/entity-walker/entity-walker.ts 86.66% 4 Missing ⚠️
...graph/node-resolution-data/node-resolution-data.ts 80.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2568       +/-   ##
===========================================
- Coverage   63.59%   42.53%   -21.06%     
===========================================
  Files         298     1014      +716     
  Lines       41920   140682    +98762     
  Branches     4439     8088     +3649     
===========================================
+ Hits        26657    59836    +33179     
- Misses      15241    79147    +63906     
- Partials       22     1699     +1677     
Files with missing lines Coverage Δ
composition/src/resolvability-graph/graph-nodes.ts 98.03% <100.00%> (ø)
composition/src/resolvability-graph/graph.ts 93.21% <100.00%> (ø)
...omposition/src/resolvability-graph/types/params.ts 100.00% <ø> (ø)
composition/src/resolvability-graph/types/types.ts 100.00% <ø> (ø)
composition/src/resolvability-graph/utils/utils.ts 99.36% <100.00%> (ø)
...ability-graph/walker/entity-walker/types/params.ts 100.00% <ø> (ø)
...ty-graph/walker/root-field-walkers/types/params.ts 100.00% <ø> (ø)
...omposition/src/v1/federation/federation-factory.ts 88.74% <100.00%> (ø)
...tion/src/v1/normalization/normalization-factory.ts 89.62% <100.00%> (ø)
composition/src/v1/normalization/utils.ts 93.93% <100.00%> (ø)
... and 3 more

... and 703 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 27, 2026

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-2aa4478710aabbd0db9928be8b372c890496f4e6

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@composition/tests/v1/resolvability.test.ts`:
- Around line 1821-1823: The test "that an `@external` key can still be a valid
target" destructures success from federateSubgraphsSuccess([jaaa, jaab],
ROUTER_COMPATIBILITY_VERSION_ONE) but never asserts it; update the test to
assert the expected value (e.g., expect(success).toBe(true) or equivalent) so
the test fails on regressions—locate the test by its name and the call to
federateSubgraphsSuccess and add the appropriate assertion against the success
variable.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efbec53 and 2582268.

📒 Files selected for processing (17)
  • composition-go/index.global.js
  • composition/src/resolvability-graph/graph-nodes.ts
  • composition/src/resolvability-graph/graph.ts
  • composition/src/resolvability-graph/node-resolution-data/node-resolution-data.ts
  • composition/src/resolvability-graph/node-resolution-data/types/params.ts
  • composition/src/resolvability-graph/types/params.ts
  • composition/src/resolvability-graph/types/types.ts
  • composition/src/resolvability-graph/utils/utils.ts
  • composition/src/resolvability-graph/walker/entity-walker/entity-walker.ts
  • composition/src/resolvability-graph/walker/entity-walker/types/params.ts
  • composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts
  • composition/src/resolvability-graph/walker/root-field-walkers/types/params.ts
  • composition/src/utils/types.ts
  • composition/src/v1/federation/federation-factory.ts
  • composition/src/v1/normalization/normalization-factory.ts
  • composition/src/v1/normalization/utils.ts
  • composition/tests/v1/resolvability.test.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
composition/src/resolvability-graph/walker/entity-walker/entity-walker.ts (1)

237-249: ⚠️ Potential issue | 🟠 Major

Mark direct paths as resolved in the non-relative branch.

removeUnresolvablePaths deletes selectionPath (Line 239) but never adds it to resolvedPaths. Because addUnresolvablePaths checks resolvedPaths first (Line 222), this misses the direct-path guard and can re-register a path that was already resolved.

💡 Proposed fix
  removeUnresolvablePaths({ selectionPath, removeDescendantPaths }: RemoveUnresolvablePathsParams) {
    if (!this.relativeOriginPaths) {
      this.subgraphNameByUnresolvablePath.delete(selectionPath);
+     this.resolvedPaths.add(selectionPath);
      if (removeDescendantPaths) {
        for (const unresolvablePath of this.subgraphNameByUnresolvablePath.keys()) {
          if (unresolvablePath.startsWith(selectionPath)) {
            this.subgraphNameByUnresolvablePath.delete(unresolvablePath);
            this.resolvedPaths.add(unresolvablePath);
          }
        }
      }
      return;
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@composition/src/resolvability-graph/walker/entity-walker/entity-walker.ts`
around lines 237 - 249, In removeUnresolvablePaths (entity-walker.ts) the
non-relative branch deletes selectionPath from subgraphNameByUnresolvablePath
but never marks it in resolvedPaths, so addUnresolvablePaths can later re-add
it; fix by adding selectionPath to this.resolvedPaths right after deleting it in
the !this.relativeOriginPaths branch (keep the existing removeDescendantPaths
loop behavior that also adds descendant unresolvablePath entries to
resolvedPaths).
🧹 Nitpick comments (1)
composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts (1)

29-35: Consider extracting duplicated edge precheck logic.

The inaccessible/external/leaf/revisit prechecks are now repeated in both visitEdge and visitSharedEdge. A shared helper would reduce drift risk as this logic evolves.

Also applies to: 127-133

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts`
around lines 29 - 35, The precheck logic for edges (inaccessible, external,
leaf, revisit) is duplicated between visitEdge and visitSharedEdge; extract this
into a shared helper (e.g., create a function like precheckEdge or
evaluateEdgePreconditions) that accepts the same inputs used by
visitEdge/visitSharedEdge (edge, selectionPath, and any visited-tracking
context) and returns the VisitNodeResult-like precheck outcome
(visited/areDescendantsResolved/isExternal/shouldSkip). Replace the duplicated
blocks in visitEdge and visitSharedEdge to call this new helper first and
early-return when the helper indicates a precheck result, keeping existing
function signatures (visitEdge, visitSharedEdge, VisitEdgeParams,
VisitNodeResult) intact so callers and types remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts`:
- Around line 127-133: visitSharedEdge now returns isExternal but that flag is
dropped by visitSharedConcreteNode and propagateSharedVisitedField; update
visitSharedConcreteNode to forward the isExternal value from visitSharedEdge
into its VisitNodeResult and modify propagateSharedVisitedField to accept and
handle an isExternal branch (e.g., when isExternal is true, mark the returned
visited/areDescendantsResolved appropriately and set an external flag on the
propagated result) so external-subgraph tracking is preserved during shared
root-field traversal; adjust all call sites in visitSharedConcreteNode (and the
similar blocks around lines 192-200 and 273-299) to propagate the new isExternal
field through.

---

Outside diff comments:
In `@composition/src/resolvability-graph/walker/entity-walker/entity-walker.ts`:
- Around line 237-249: In removeUnresolvablePaths (entity-walker.ts) the
non-relative branch deletes selectionPath from subgraphNameByUnresolvablePath
but never marks it in resolvedPaths, so addUnresolvablePaths can later re-add
it; fix by adding selectionPath to this.resolvedPaths right after deleting it in
the !this.relativeOriginPaths branch (keep the existing removeDescendantPaths
loop behavior that also adds descendant unresolvablePath entries to
resolvedPaths).

---

Nitpick comments:
In
`@composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts`:
- Around line 29-35: The precheck logic for edges (inaccessible, external, leaf,
revisit) is duplicated between visitEdge and visitSharedEdge; extract this into
a shared helper (e.g., create a function like precheckEdge or
evaluateEdgePreconditions) that accepts the same inputs used by
visitEdge/visitSharedEdge (edge, selectionPath, and any visited-tracking
context) and returns the VisitNodeResult-like precheck outcome
(visited/areDescendantsResolved/isExternal/shouldSkip). Replace the duplicated
blocks in visitEdge and visitSharedEdge to call this new helper first and
early-return when the helper indicates a precheck result, keeping existing
function signatures (visitEdge, visitSharedEdge, VisitEdgeParams,
VisitNodeResult) intact so callers and types remain consistent.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2582268 and 7bec512.

📒 Files selected for processing (4)
  • composition-go/index.global.js
  • composition/src/resolvability-graph/walker/entity-walker/entity-walker.ts
  • composition/src/resolvability-graph/walker/root-field-walkers/root-field-walker.ts
  • composition/tests/v1/resolvability.test.ts

Copy link
Copy Markdown
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

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

LGTM

@Aenimus Aenimus enabled auto-merge (squash) February 27, 2026 16:59
@Aenimus Aenimus merged commit 3168ab0 into main Feb 27, 2026
36 checks passed
@Aenimus Aenimus deleted the david/eng-9040-validate-true-external-keys branch February 27, 2026 17:00
@coderabbitai coderabbitai bot mentioned this pull request Mar 6, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants