Skip to content

refactor!: refactor union properties handling#701

Open
stuebingerb wants to merge 1 commit into
mainfrom
refactor/refactor-union-properties
Open

refactor!: refactor union properties handling#701
stuebingerb wants to merge 1 commit into
mainfrom
refactor/refactor-union-properties

Conversation

@stuebingerb

Copy link
Copy Markdown
Owner

Refactors handling of union properties and processes them just like regular fragments, thereby benefitting from all recent bugfixes and simplifying code.

Fixes #697

BREAKING CHANGE: removed Type.Union, Field.Union, and Execution.Union.

BREAKING CHANGE: invalid fragment conditions in union properties were previously simply ignored and now cause the query to fail.

@stuebingerb stuebingerb added the breaking Introduces a breaking change label Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Changes
    • Updated union-field processing to use the standard field and execution flow.
    • Improved fragment handling and validation for impossible or invalid type conditions on union properties.
    • Refined error messages for invalid union selections and null union results.
  • Tests
    • Added coverage for invalid fragments on union properties.
    • Updated schema representation and expected validation and execution errors.

Walkthrough

Union-specific field and execution classes are removed. Union properties now use generic function fields and execution paths, while fragment validation rejects impossible or invalid union type conditions.

Changes

Union execution simplification

Layer / File(s) Summary
Remove union-specific public models
kgraphql/src/main/kotlin/.../Execution.kt, kgraphql/src/main/kotlin/.../Field.kt, kgraphql/api/kgraphql.api
The Execution.Node.Union and Field.Union classes, their members, and exported API declarations are removed.
Route union fields through generic interpretation
kgraphql/src/main/kotlin/.../SchemaCompilation.kt, kgraphql/src/main/kotlin/.../RequestInterpreter.kt, kgraphql/src/main/kotlin/.../Validation.kt, kgraphql/src/test/kotlin/.../FragmentsSpecificationTest.kt
Union properties are compiled as Field.Function; union-specific request handling and validation are removed, and invalid fragment type conditions are tested.
Simplify union execution
kgraphql/src/main/kotlin/.../ParallelRequestExecutor.kt, kgraphql/src/test/kotlin/.../SchemaBuilderTest.kt, kgraphql/src/test/kotlin/.../UnionsSpecificationTest.kt
Execution no longer creates or traverses union operation nodes, and expected field and error results are updated.

Possibly related PRs

Suggested labels: breaking

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, descriptive, and uses a conventional-commit style breaking-change prefix.
Description check ✅ Passed The description clearly matches the union-property refactor and its breaking changes.
Linked Issues check ✅ Passed The changes add fragment-condition validation for union properties and reject invalid type conditions as requested in #697.
Out of Scope Changes check ✅ Passed The removals and test updates align with the union-property refactor and its breaking changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/refactor-union-properties

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Refactors handling of union properties and processes them just like
regular fragments, thereby benefitting from all recent bugfixes and
simplifying code.

Fixes #697

BREAKING CHANGE: removed `Type.Union`, `Field.Union`, and
`Execution.Union`.

BREAKING CHANGE: invalid fragment conditions in union properties were
previously simply ignored and now cause the query to fail.
@stuebingerb
stuebingerb force-pushed the refactor/refactor-union-properties branch from 626bac4 to dbec446 Compare July 21, 2026 16:21

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 935f9711-9e5f-41fa-ad6c-09d59f9d8b42

📥 Commits

Reviewing files that changed from the base of the PR and between f72e1f3 and 626bac4.

📒 Files selected for processing (10)
  • kgraphql/api/kgraphql.api
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/Execution.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/ParallelRequestExecutor.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Field.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/SchemaCompilation.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Validation.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/schema/SchemaBuilderTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/language/FragmentsSpecificationTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/typesystem/UnionsSpecificationTest.kt
💤 Files with no reviewable changes (5)
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Validation.kt
  • kgraphql/api/kgraphql.api
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/Execution.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Field.kt

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.06%. Comparing base (f72e1f3) to head (dbec446).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #701   +/-   ##
=======================================
  Coverage   84.05%   84.06%           
=======================================
  Files         151      151           
  Lines        5024     4925   -99     
  Branches      870      850   -20     
=======================================
- Hits         4223     4140   -83     
+ Misses        493      488    -5     
+ Partials      308      297   -11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

🐰 Bencher Report

ProjectKGraphQL
Branchrefactor/refactor-union-properties
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
de.stuebingerb.kgraphql.FunctionExecutionBenchmark.benchmarkFunctionExecution📈 view plot
🚷 view threshold
5,974,742.64 ops/s
(+3.87%)Baseline: 5,752,321.95 ops/s
4,990,269.02 ops/s
(83.52%)
de.stuebingerb.kgraphql.ParallelExecutionBenchmark.queryBenchmark📈 view plot
🚷 view threshold
1.30 ops/s
(+0.01%)Baseline: 1.30 ops/s
1.30 ops/s
(99.84%)
de.stuebingerb.kgraphql.QueryBenchmark.executionError📈 view plot
🚷 view threshold
15,263.76 ops/s
(-12.92%)Baseline: 17,527.48 ops/s
10,830.62 ops/s
(70.96%)
de.stuebingerb.kgraphql.QueryBenchmark.inputFromDocument📈 view plot
🚷 view threshold
19,551.55 ops/s
(-9.16%)Baseline: 21,522.53 ops/s
14,081.46 ops/s
(72.02%)
de.stuebingerb.kgraphql.QueryBenchmark.inputFromVariable📈 view plot
🚷 view threshold
18,506.16 ops/s
(-11.10%)Baseline: 20,817.14 ops/s
13,893.32 ops/s
(75.07%)
de.stuebingerb.kgraphql.QueryBenchmark.largeList📈 view plot
🚷 view threshold
4.86 ops/s
(+2.99%)Baseline: 4.72 ops/s
4.15 ops/s
(85.42%)
de.stuebingerb.kgraphql.QueryBenchmark.largeListWithFragment📈 view plot
🚷 view threshold
5.28 ops/s
(+0.86%)Baseline: 5.24 ops/s
4.60 ops/s
(87.03%)
de.stuebingerb.kgraphql.QueryBenchmark.manyChildren📈 view plot
🚷 view threshold
196.42 ops/s
(+7.34%)Baseline: 183.00 ops/s
143.40 ops/s
(73.01%)
de.stuebingerb.kgraphql.QueryBenchmark.manyChildrenWithFragment📈 view plot
🚷 view threshold
199.91 ops/s
(+2.83%)Baseline: 194.41 ops/s
153.83 ops/s
(76.95%)
de.stuebingerb.kgraphql.QueryBenchmark.manyDataChildren📈 view plot
🚷 view threshold
8.92 ops/s
(+0.14%)Baseline: 8.91 ops/s
8.79 ops/s
(98.56%)
de.stuebingerb.kgraphql.QueryBenchmark.manyOperations📈 view plot
🚷 view threshold
313.94 ops/s
(+2.86%)Baseline: 305.23 ops/s
245.21 ops/s
(78.11%)
de.stuebingerb.kgraphql.QueryBenchmark.manyOperationsWithFragment📈 view plot
🚷 view threshold
324.53 ops/s
(+2.54%)Baseline: 316.49 ops/s
259.76 ops/s
(80.04%)
de.stuebingerb.kgraphql.QueryBenchmark.nestedObject📈 view plot
🚷 view threshold
7,524.00 ops/s
(-3.45%)Baseline: 7,792.58 ops/s
6,035.50 ops/s
(80.22%)
de.stuebingerb.kgraphql.RequestCachingBenchmark.invalidRequest📈 view plot
🚷 view threshold
136,526.14 ops/s
(-3.81%)Baseline: 141,934.06 ops/s
135,534.91 ops/s
(99.27%)
de.stuebingerb.kgraphql.RequestCachingBenchmark.largeRequest📈 view plot
🚷 view threshold
7,687.84 ops/s
(-4.03%)Baseline: 8,010.46 ops/s
5,827.56 ops/s
(75.80%)
de.stuebingerb.kgraphql.RequestCachingBenchmark.smallRequest📈 view plot
🚷 view threshold
10,841.96 ops/s
(-6.21%)Baseline: 11,560.29 ops/s
8,346.93 ops/s
(76.99%)
de.stuebingerb.kgraphql.SimpleExecutionOverheadBenchmark.benchmark📈 view plot
🚷 view threshold
452,181.44 ops/s
(-4.23%)Baseline: 472,157.67 ops/s
446,326.41 ops/s
(98.71%)
🐰 View full continuous benchmarking report in Bencher

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 10 files

Architecture diagram
sequenceDiagram
    participant Client
    participant SC as SchemaCompilation
    participant RI as RequestInterpreter
    participant Exe as Execution.Node
    participant PRE as ParallelRequestExecutor
    participant Resolver

    Note over SC,Resolver: Schema Build Time
    SC->>SC: handleUnionProperty(unionProperty)
    SC->>SC: CHANGED: create Field.Function instead of Field.Union

    Note over Client,PRE: Query Execution Time
    Client->>RI: POST /graphql (query with union field + fragments)
    RI->>RI: parse selection set for union field
    RI->>RI: lookup field: returns Field.Function (returnType=Type.Union)
    loop each selection (fragment spread / inline fragment)
        RI->>RI: validate fragment type condition against union's possibleTypes
        alt Invalid type condition
            RI-->>Client: ValidationException (error)
        else Valid
            RI->>Exe: CHANGED: create Execution.Node<br>with children as Execution.Fragment<br>per typed fragment
        end
    end
    RI->>PRE: pass execution tree

    PRE->>PRE: createNode(value, node, returnType)
    PRE->>Resolver: invoke union property resolver
    Resolver-->>PRE: union value (e.g. Actor instance)
    loop each child Execution.Fragment
        PRE->>PRE: check if value's type matches fragment's type condition
        alt Match
            PRE->>PRE: execute fragment's field selections
        else No match
            PRE->>PRE: skip fragment
        end
    end
    PRE-->>Client: JSON result with fields from matching fragment
Loading

Re-trigger cubic

@stuebingerb

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/ParallelRequestExecutor.kt (1)

190-192: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Restore runtime type validation for union returns.

By deleting createUnionOperationNode and routing unions through this generic path, the executor no longer validates that the resolved value is actually a member of the union's possibleTypes. This introduces two specification violations for abstract types:

  1. Type Leakage: If a union resolver returns an invalid object (e.g., Director instead of Actor or Film), querying __typename will successfully return "Director", leaking a type that isn't defined as a valid member of the union into the response.
  2. Silent Empty Objects: If the invalid object is queried with inline fragments, no fragments will match, and the executor will silently return {} instead of raising a field error as required by the GraphQL specification for invalid abstract type resolutions.

Additionally, because primitive checks (like value is String) happen before node.children.isNotEmpty(), returning a primitive from a union resolver will incorrectly serialize as a scalar, bypassing the selection set entirely.

Please consider restoring a runtime validation check in this block to ensure that value conforms to returnType.unwrapped().possibleTypes for abstract types before proceeding to create the object node.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0bb5979a-20f6-4a53-846f-0b6e57a2c54b

📥 Commits

Reviewing files that changed from the base of the PR and between f72e1f3 and dbec446.

📒 Files selected for processing (10)
  • kgraphql/api/kgraphql.api
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/Execution.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/ParallelRequestExecutor.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Field.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/SchemaCompilation.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Validation.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/schema/SchemaBuilderTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/language/FragmentsSpecificationTest.kt
  • kgraphql/src/test/kotlin/de/stuebingerb/kgraphql/specification/typesystem/UnionsSpecificationTest.kt
💤 Files with no reviewable changes (5)
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Validation.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/execution/Execution.kt
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/Field.kt
  • kgraphql/api/kgraphql.api
  • kgraphql/src/main/kotlin/de/stuebingerb/kgraphql/schema/structure/RequestInterpreter.kt

@stuebingerb

Copy link
Copy Markdown
Owner Author

CodeRabbit is right, the actual return type is no longer validated against the possible union types. I have a draft implementation but I'm not sure if I want to restore that check now, and if to what extent - I'd expect that invalid return types are possible in other places as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Introduces a breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing/incorrect fragment condition validation on union properties

1 participant