Skip to content

Commit 970c285

Browse files
authored
Rename UNSATISFIABLE log code to UNSATISFIABLE_QUERY_PATH (#9780)
1 parent 4e28434 commit 970c285

5 files changed

Lines changed: 24 additions & 11 deletions

File tree

src/HotChocolate/Fusion/src/Fusion.Composition/Logging/LogEntryCodes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,5 @@ public static class LogEntryCodes
7070
public const string RootSubscriptionUsed = "ROOT_SUBSCRIPTION_USED";
7171
public const string SpecifiedByUrlMismatch = "SPECIFIED_BY_URL_MISMATCH";
7272
public const string TypeKindMismatch = "TYPE_KIND_MISMATCH";
73-
public const string Unsatisfiable = "UNSATISFIABLE";
73+
public const string UnsatisfiableQueryPath = "UNSATISFIABLE_QUERY_PATH";
7474
}

src/HotChocolate/Fusion/src/Fusion.Composition/SatisfiabilityValidator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private void VisitOutputField(
238238
_log.Write(
239239
LogEntryBuilder.New()
240240
.SetMessage(error.ToString())
241-
.SetCode(LogEntryCodes.Unsatisfiable)
241+
.SetCode(LogEntryCodes.UnsatisfiableQueryPath)
242242
.SetSeverity(LogSeverity.Error)
243243
.SetExtension("error", error)
244244
.Build());
@@ -297,7 +297,7 @@ private void VisitNodeField(
297297
_log.Write(
298298
LogEntryBuilder.New()
299299
.SetMessage(error.ToString())
300-
.SetCode(LogEntryCodes.Unsatisfiable)
300+
.SetCode(LogEntryCodes.UnsatisfiableQueryPath)
301301
.SetSeverity(LogSeverity.Error)
302302
.SetExtension("error", error)
303303
.Build());

src/HotChocolate/Fusion/test/Fusion.Composition.Tests/SatisfiabilityValidatorTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type User {
6262

6363
// assert
6464
Assert.True(result.IsFailure);
65+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
6566
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
6667
"""
6768
Unable to access the field 'User.membershipStatus' on path 'A:Query.profileById<Profile> -> A:Profile.user<User>'.
@@ -119,6 +120,7 @@ type Product {
119120

120121
// assert
121122
Assert.True(result.IsFailure);
123+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
122124
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
123125
"""
124126
Unable to access the field 'Product.sku' on path 'B:Query.productById<Product>'.
@@ -179,6 +181,7 @@ type Product {
179181

180182
// assert
181183
Assert.True(result.IsFailure);
184+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
182185
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
183186
"""
184187
Unable to access the field 'Product.sku' on path 'A:Query.productById<Product>'.
@@ -319,6 +322,7 @@ type Address @shareable {
319322

320323
// assert
321324
Assert.True(result.IsFailure);
325+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
322326
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
323327
"""
324328
Unable to access the field 'Address.country' on path 'A:Query.userById<User> -> A:User.address<Address>'.
@@ -375,6 +379,7 @@ type Category @key(fields: "id") {
375379

376380
// assert
377381
Assert.True(result.IsFailure);
382+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
378383
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
379384
"""
380385
Unable to access the field 'Category.name' on path 'B:Query.categoryById<Category>'.
@@ -1470,6 +1475,7 @@ type Product {
14701475

14711476
// assert
14721477
Assert.True(result.IsFailure);
1478+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
14731479
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
14741480
"""
14751481
Unable to access the field 'Product.sku' on path 'A:Query.productById<Product>'.
@@ -1632,6 +1638,7 @@ interface Animal {
16321638

16331639
// assert
16341640
Assert.False(result.IsSuccess);
1641+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
16351642
string.Join("\n\n", log.Select(e => e.Message))
16361643
.MatchInlineSnapshot(
16371644
"""
@@ -1980,6 +1987,7 @@ type Dog {
19801987

19811988
// assert
19821989
Assert.False(result.IsSuccess);
1990+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
19831991
string.Join("\n\n", log.Select(e => e.Message))
19841992
.MatchInlineSnapshot(
19851993
"""
@@ -2032,6 +2040,7 @@ type Product {
20322040

20332041
// assert
20342042
Assert.True(result.IsFailure);
2043+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
20352044
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
20362045
"""
20372046
Unable to access the field 'Product.title' on path 'A:Query.productById<Product>'.
@@ -2120,6 +2129,7 @@ type Section {
21202129

21212130
// assert
21222131
Assert.True(result.IsFailure);
2132+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
21232133
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
21242134
"""
21252135
Unable to access the field 'Product.title' on path 'A:Query.productById<Product>'.
@@ -2300,6 +2310,7 @@ type Viewer {
23002310

23012311
// assert
23022312
Assert.True(result.IsFailure);
2313+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
23032314
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
23042315
"""
23052316
Unable to access the field 'Viewer.lastName' on path 'A:Query.viewer<Viewer>'.
@@ -2360,6 +2371,7 @@ type Viewer {
23602371

23612372
// assert
23622373
Assert.True(result.IsFailure);
2374+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
23632375
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
23642376
"""
23652377
Unable to access the field 'Viewer.fullName' on path 'A:Query.viewer<Viewer>'.
@@ -2529,6 +2541,7 @@ type Section {
25292541

25302542
// assert
25312543
Assert.True(result.IsFailure);
2544+
Assert.All(log, e => Assert.Equal(LogEntryCodes.UnsatisfiableQueryPath, e.Code));
25322545
string.Join("\n\n", log.Select(e => e.Message)).MatchInlineSnapshot(
25332546
"""
25342547
Unable to access the field 'Category.name' on path 'A:Query.productById<Product> -> B:Product.category<Category>'.

website/src/docs/fusion/v16/composition.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Validates the merged schema as a whole. The rules here treat the composed schema
7171

7272
### 8. Validate Satisfiability
7373

74-
Performs reachability analysis. Starting from the root types, the pipeline walks every reachable field in the merged schema and confirms it can be resolved by at least one subgraph given the available `@lookup` and `@key` paths. If a field is reachable from a query but no subgraph can produce it, satisfiability fails with `UNSATISFIABLE`. This is the last line of defense against shapes that look valid statically but cannot actually be served at runtime.
74+
Performs reachability analysis. Starting from the root types, the pipeline walks every reachable field in the merged schema and confirms it can be resolved by at least one subgraph given the available `@lookup` and `@key` paths. If a field is reachable from a query but no subgraph can produce it, satisfiability fails with `UNSATISFIABLE_QUERY_PATH`. This is the last line of defense against shapes that look valid statically but cannot actually be served at runtime.
7575

7676
## Common Scenarios
7777

@@ -180,7 +180,7 @@ The placeholders `{0}`, `{1}`, etc. in the message column are replaced with the
180180
| [ROOT_QUERY_USED](https://graphql.github.io/composite-schemas-spec/draft/#sec-Root-Query-Used) | The root query type in schema '{0}' must be named 'Query'. | Fusion requires the root query type to use the canonical name `Query`. Rename the type in the named source schema. A type named `Query` must not exist unless it is the root query type. See [Getting Started](/docs/fusion/v16/getting-started) for the expected schema layout. |
181181
| [ROOT_SUBSCRIPTION_USED](https://graphql.github.io/composite-schemas-spec/draft/#sec-Root-Subscription-Used) | The root subscription type in schema '{0}' must be named 'Subscription'. | Fusion requires the root subscription type to use the canonical name `Subscription`. Rename the type in the named source schema. A type named `Subscription` must not exist unless it is the root subscription type. |
182182
| [TYPE_KIND_MISMATCH](https://graphql.github.io/composite-schemas-spec/draft/#sec-Type-Kind-Mismatch) | The type '{0}' has a different kind in schema '{1}' ({2}) than it does in schema '{3}' ({4}). | The same type name was used for different kinds (for example, an object in one source schema and an interface in another). Decide which kind is correct and update the other source schema, or rename one of the types so they no longer collide. |
183-
| `UNSATISFIABLE` | (Message varies. Includes the unreachable field, the path the validator tried, and the lookups it considered.) | Some reachable field cannot be resolved through the available `@lookup` and `@key` paths. See [Diagnosing UNSATISFIABLE Errors](#diagnosing-unsatisfiable-errors) for how to read the message and fix the underlying gap. |
183+
| [UNSATISFIABLE_QUERY_PATH](https://graphql.github.io/composite-schemas-spec/draft/#sec-Unsatisfiable-Query-Path) | (Message varies. Includes the unreachable field, the path the validator tried, and the lookups it considered.) | Some reachable field cannot be resolved through the available `@lookup` and `@key` paths. See [Diagnosing UNSATISFIABLE_QUERY_PATH Errors](#diagnosing-unsatisfiable_query_path-errors) for how to read the message and fix the underlying gap. |
184184

185185
### Warnings
186186

@@ -189,9 +189,9 @@ The placeholders `{0}`, `{1}`, etc. in the message column are replaced with the
189189
| [LOOKUP_RETURNS_NON_NULLABLE_TYPE](https://graphql.github.io/composite-schemas-spec/draft/#sec-Lookup-Returns-Non-Nullable-Type) | The lookup field '{0}' in schema '{1}' should return a nullable type. | Lookups should return nullable entities so the gateway can represent missing keys without throwing. Change the return type of the lookup field to nullable. See [Entities and Lookups](/docs/fusion/v16/entities-and-lookups). |
190190
| [SPECIFIED_BY_URL_MISMATCH](https://graphql.github.io/composite-schemas-spec/draft/#sec-SpecifiedBy-URL-Mismatch) | The scalar type '{0}' has a different specified-by URL in schema '{1}' ({2}) than it does in schema '{3}' ({4}). | The same scalar declares conflicting `@specifiedBy(url: ...)` URLs in different source schemas. Align the URL across all source schemas that define the scalar so the composed schema points to a single specification. |
191191

192-
### Diagnosing UNSATISFIABLE Errors
192+
### Diagnosing UNSATISFIABLE_QUERY_PATH Errors
193193

194-
Most composition errors point at a single source schema or definition. `UNSATISFIABLE` is different. It is raised by the satisfiability validator (phase 8) and tells you that the merged schema as a whole has at least one reachable field that no source schema can serve through the available `@lookup` and `@key` paths.
194+
Most composition errors point at a single source schema or definition. `UNSATISFIABLE_QUERY_PATH` is different. It is raised by the satisfiability validator (phase 8) and tells you that the merged schema as a whole has at least one reachable field that no source schema can serve through the available `@lookup` and `@key` paths.
195195

196196
Because the validator weighs every option for reaching a field across every source schema, the diagnostic carries a tree of nested errors that explain why each candidate option failed. Reading that tree from the bottom up is the fastest way to find the gap.
197197

@@ -231,7 +231,7 @@ Set the option via the CLI flag during composition:
231231
nitro fusion compose --include-satisfiability-paths
232232
```
233233

234-
Paths add noise to short outputs and pay off on any non-trivial graph. Turn them on whenever an `UNSATISFIABLE` error is hard to triage.
234+
Paths add noise to short outputs and pay off on any non-trivial graph. Turn them on whenever an `UNSATISFIABLE_QUERY_PATH` error is hard to triage.
235235

236236
#### Common causes and fixes
237237

@@ -240,7 +240,7 @@ Paths add noise to short outputs and pay off on any non-trivial graph. Turn them
240240
- **`@require` reaches for a field that is not on the path.** A `@require(field: "...")` references parent fields that the validator cannot resolve given the path it took. Either expose the required field on the parent type along that path, or rewrite the requirement.
241241
- **`Node` interface without a node lookup.** A type implements `Node` but no source schema exposes a `node(id: ID!): Node` lookup that returns it. Add one in any source schema that owns the entity.
242242

243-
When you fix the root cause, both the top-level `UNSATISFIABLE` error and its nested children disappear together.
243+
When you fix the root cause, both the top-level `UNSATISFIABLE_QUERY_PATH` error and its nested children disappear together.
244244

245245
#### Temporarily ignoring a non-accessible field
246246

website/src/docs/fusion/v16/migration/migrate-from-15-to-16.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Validating Fusion configuration of API 'QXBpCmcwMTlkMmIzMGUzNGY3YzQ2OTBjNTgxOTNk
2929
3030
❌ [ERR] Unable to access the field 'Review.productVariant'.
3131
Unable to transition between schemas 'REVIEWS' and 'PRODUCTS' for access to field 'PRODUCTS:Review.productVariant<Product>'.
32-
No lookups found for type 'Review' in schema 'PRODUCTS'. (UNSATISFIABLE)
32+
No lookups found for type 'Review' in schema 'PRODUCTS'. (UNSATISFIABLE_QUERY_PATH)
3333
Satisfiability validation failed.
3434
3535
-->
@@ -410,7 +410,7 @@ public static class Query
410410
}
411411
```
412412

413-
Missing `@lookup` annotations will usually manifest as `UNSATISFIABLE` errors in the composition. See [Entities and lookups](/docs/fusion/v16/entities-and-lookups) for details.
413+
Missing `@lookup` annotations will usually manifest as `UNSATISFIABLE_QUERY_PATH` errors in the composition. See [Entities and lookups](/docs/fusion/v16/entities-and-lookups) for details.
414414

415415
If your graph has overlapping fields, i.e. multiple subgraphs providing the same field, you now also have to explicitly mark those fields as shareable from both sides.
416416

0 commit comments

Comments
 (0)