Commit 877eb9e
fix(composition): resolve @composeDirective definitions per-directive instead of per-spec (#3391)
## Summary
Fixes a bug in `ComposeDirectiveManager` where `@composeDirective` would
fail
with a false `DIRECTIVE_COMPOSITION_ERROR` when two subgraphs linked the
same
custom spec but imported different subsets of its directives.
Closes FED-849
---
## Problem
`getLatestDirectiveDefinition()` resolved a directive's definition
through a two-hop chain:
```
directiveName → spec identity → "latest" subgraph for that spec → schema.directive(...)
```
The "latest" subgraph was determined solely by its spec version (highest
minor).
This meant that if the latest-version subgraph had not imported the
specific directive
being resolved, the lookup would silently fail and produce a false error
— even though
another subgraph had a perfectly valid definition for it.
**Failing scenarios:**
```graphql
# SG1 — imports @foo AND @bar, composes both
@link(url: "https://myorg.dev/myspec/v1.0", import: ["@foo", "@bar"])
@composeDirective(name: "@foo") @composeDirective(name: "@bar")
# SG2 — imports only @foo from the same spec
@link(url: "https://myorg.dev/myspec/v1.0", import: ["@foo"])
@composeDirective(name: "@foo")
```
If the iterator elected SG2 as "latest", resolving `@bar` would fail:
```
DIRECTIVE_COMPOSITION_ERROR: Core feature "https://myorg.dev/myspec" in subgraph "SG2"
does not have a directive definition for "@bar"
```
The bug was order-dependent: it succeeded only when the elected subgraph
happened to be
a superset of all other subgraphs' imports. Completely disjoint imports
(SG1 has `@foo`,
SG2 has `@bar`) always failed regardless of order.
---
## Fix
Added a `directiveDefinitionMap: Map<string, DirectiveDefinition>` built
during `validate()`.
For each composed directive, it finds the highest-minor-version subgraph
that **actually
imported** that specific directive, and stores its definition directly.
`getLatestDirectiveDefinition()` now does a single lookup on this map
instead of the
indirect two-hop chain.
**Before:**
```
directiveName → spec identity → latestSubgraph (may not have the directive!) → definition
```
**After:**
```
directiveName → definition (sourced from the subgraph that actually imported it)
```
---
## Changes
| File | Description |
|---|---|
| `composition-js/src/composeDirectiveManager.ts` | New
`directiveDefinitionMap`, populated in `validate()`,
`getLatestDirectiveDefinition()` simplified to a direct lookup |
| `composition-js/src/__tests__/compose.composeDirective.test.ts` | Two
new regression tests covering the subset and fully-disjoint import
scenarios; one existing test updated that was asserting the previous
(buggy) error behavior |
---
## Testing
```bash
# Regression tests for FED-849
node_modules/.bin/jest --projects composition-js --no-coverage --testNamePattern="FED-849"
# Full composition suite
node_modules/.bin/jest --projects composition-js --no-coverage
```
All 15 test suites pass (400 tests, 0 failures).
---------
Co-authored-by: Dariusz Kuc <9501705+dariuszkuc@users.noreply.github.com>1 parent 5b36fc6 commit 877eb9e
3 files changed
Lines changed: 188 additions & 35 deletions
File tree
- .changeset
- composition-js/src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
Lines changed: 121 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
159 | 268 | | |
160 | 269 | | |
161 | 270 | | |
| |||
549 | 658 | | |
550 | 659 | | |
551 | 660 | | |
552 | | - | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
553 | 666 | | |
554 | 667 | | |
555 | 668 | | |
| |||
570 | 683 | | |
571 | 684 | | |
572 | 685 | | |
| 686 | + | |
| 687 | + | |
573 | 688 | | |
574 | | - | |
575 | | - | |
576 | | - | |
577 | | - | |
578 | | - | |
579 | | - | |
580 | | - | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
581 | 694 | | |
582 | 695 | | |
583 | 696 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
83 | 88 | | |
84 | 89 | | |
85 | 90 | | |
| |||
90 | 95 | | |
91 | 96 | | |
92 | 97 | | |
| 98 | + | |
93 | 99 | | |
94 | 100 | | |
95 | 101 | | |
| |||
436 | 442 | | |
437 | 443 | | |
438 | 444 | | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
439 | 472 | | |
440 | 473 | | |
441 | 474 | | |
| |||
455 | 488 | | |
456 | 489 | | |
457 | 490 | | |
458 | | - | |
459 | | - | |
460 | | - | |
461 | | - | |
462 | | - | |
463 | | - | |
464 | | - | |
465 | | - | |
466 | | - | |
467 | | - | |
468 | | - | |
469 | | - | |
470 | | - | |
471 | | - | |
472 | | - | |
473 | | - | |
474 | | - | |
475 | | - | |
476 | | - | |
477 | | - | |
478 | | - | |
479 | | - | |
480 | | - | |
481 | | - | |
482 | | - | |
483 | | - | |
484 | | - | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
485 | 496 | | |
486 | 497 | | |
487 | 498 | | |
| |||
0 commit comments