Detect unbreakable OneOf input object cycles in schema validation - #10356
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The legacy validator’s unbreakable-cycle error message formatting currently diverges from the graphql-js wording described in the PR metadata, which impacts the intended user-visible consistency.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates Hot Chocolate’s schema validation to reject input object graphs that cannot be assigned a finite value, including unbreakable @oneOf cycles, aligning behavior with the OneOf “inhabitability” proposal and graphql-js.
Changes:
- Replaced the old input object cycle rule with a new “unbreakable cycle” rule in both the abstract validator (
HotChocolate.Types.Validation) and legacy validation (HotChocolate.Types), using a two-phase finiteness propagation algorithm. - Updated user-facing error messages/extensions (RFC link) and renamed the abstract rule (
NoInputObjectCycleRule→NoInputObjectUnbreakableCycleRule) while keeping error codeHCV0018. - Added/updated comprehensive tests and snapshots, plus a short documentation update for
@oneOffiniteness.
File summaries
| File | Description |
|---|---|
| website/content/docs/hotchocolate/defining-a-schema/input-object-types.md | Documents the finite-value requirement for @oneOf inputs. |
| src/HotChocolate/Core/test/Types.Validation.Tests/SchemaValidatorTests.cs | Updates default-rule list expectation for the renamed rule. |
| src/HotChocolate/Core/test/Types.Validation.Tests/Rules/NoInputObjectUnbreakableCycleRuleTests.cs | New test suite for the abstract unbreakable-cycle rule and message shape. |
| src/HotChocolate/Core/test/Types.Validation.Tests/Rules/NoInputObjectCycleRuleTests.cs | Removes the old cycle-rule tests. |
| src/HotChocolate/Core/test/Types.Tests/Types/Validation/InputObjectTypeValidationRuleTests.cs | Adds legacy validator accept/reject coverage for @oneOf finiteness and mixed cycles. |
| src/HotChocolate/Core/test/Types.Tests/Types/Validation/snapshots/*.snap | Updates/adds snapshots to reflect the new legacy validation errors. |
| src/HotChocolate/Core/src/Types/Utilities/ErrorHelper.cs | Renames and reworks the legacy schema error helper for unbreakable cycles and RFC extension handling. |
| src/HotChocolate/Core/src/Types/Properties/TypeResources.resx | Updates the legacy error message template to the new finite-value wording. |
| src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs | Regenerates resource bindings for the renamed message key. |
| src/HotChocolate/Core/src/Types/Configuration/Validation/InputObjectTypeValidationRule.cs | Replaces recursive non-null cycle detection with finiteness propagation + cycle reporting (legacy validation). |
| src/HotChocolate/Core/src/Types.Validation/SchemaValidator.cs | Swaps out the old rule for NoInputObjectUnbreakableCycleRule in the default abstract rule set. |
| src/HotChocolate/Core/src/Types.Validation/Rules/NoInputObjectUnbreakableCycleRule.cs | New abstract validator rule implementing the two-phase algorithm and reporting. |
| src/HotChocolate/Core/src/Types.Validation/Rules/NoInputObjectCycleRule.cs | Removes the old abstract cycle rule implementation. |
| src/HotChocolate/Core/src/Types.Validation/Properties/ValidationResources.resx | Replaces old cycle message resources with the new unbreakable-cycle message. |
| src/HotChocolate/Core/src/Types.Validation/Properties/ValidationResources.Designer.cs | Regenerates resource bindings for the new message key. |
| src/HotChocolate/Core/src/Types.Validation/Logging/LogEntryHelper.cs | Adds InputObjectUnbreakableCycle log entry builder and RFC extension handling. |
| src/HotChocolate/Core/src/Types.Validation/Logging/LogEntryCodes.cs | Renames the constant to InputObjectUnbreakableCycle while keeping HCV0018. |
Review details
Files not reviewed (2)
- src/HotChocolate/Core/src/Types.Validation/Properties/ValidationResources.Designer.cs: Generated file
- src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs: Generated file
- Files reviewed: 26/28 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Patch coverage100.0% of changed lines covered (220/220)
Project coverage: 57.8% (287680/497562 lines) |
Summary
@oneOfinput object has an unbreakable cycle when none of its fields can hold a finite value, and a non-OneOf input object when any of its non-null fields cannot; lists, nullable fields on non-OneOf types, and non-input-object types always break a cycle. Both the abstract validator (HotChocolate.Types.Validation) and the legacyHotChocolate.Typesvalidation implement the check with the same two-phase algorithm: collect the fields through which each input object requires another, propagate finiteness through reverse edges to a fixed point, then report each cycle among the remaining types once with its field path.NoInputObjectCycleRuleis renamed toNoInputObjectUnbreakableCycleRule. Error codeHCV0018is unchanged. Code that constructsSchemaValidatorwith an explicit rule list must use the new name.Input Object 'A' cannot be provided a finite value because it references itself through fields: 'A.b', 'B.a'., name the input object the cycle returns to, and carry anrfcextension pointing at the spec proposal instead of aspecifiedBylink, because the published spec does not yet contain the rule. The legacy error reports field coordinates and is appended after the other input object errors rather than interleaved per type.Test plan
NoInputObjectUnbreakableCycleRuleTests(25 tests) and the fullHotChocolate.Types.Validation.Testsproject pass.InputObjectTypeValidationRuleTests(34 tests) and theHotChocolate.Types.ValidationandHotChocolate.Configuration.Validationtest namespaces ofHotChocolate.Types.Testspass, with new file snapshots for every rejected OneOf topology.HotChocolate.Fusion.Composition.Testspasses in full, since composition runs the abstract validator's default rules on every source schema.