Skip to content

fix: handle complex types inside inline fragments in gRPC execution plan#1411

Merged
dkorittki merged 20 commits intomasterfrom
dominik/eng-9007-router-does-not-resolve-grpc-nested-list-fields-in-response
Mar 5, 2026
Merged

fix: handle complex types inside inline fragments in gRPC execution plan#1411
dkorittki merged 20 commits intomasterfrom
dominik/eng-9007-router-does-not-resolve-grpc-nested-list-fields-in-response

Conversation

@dkorittki
Copy link
Copy Markdown
Contributor

@dkorittki dkorittki commented Feb 27, 2026

Summary by CodeRabbit

  • Schema Updates

    • Added required pet field (Animal!) to Owner and featuredCategory(includeChildren: Boolean!) to Subcategory, enabling queries for an owner’s pet and subcategory’s featured category.
  • Refactor

    • Improved handling of inline fragments, per-type fragment selections and oneOf responses for more accurate nested fragment resolution and traversal.
  • Tests

    • Expanded coverage with numerous nested inline-fragment and federation scenarios; updated expectations to reflect the new fragment selection behavior.

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.

When a GraphQL operation contains complex types inside an inline fragment for gRPC subgraphs the fields of that complex type never get populated with the data of the subgraphs response and hence are becoming null, even if there actually was is data. This is because the gRPC execution visitors don't create the expected response for complex types inside inline fragments. This is because the visitor doesn't account for inline fragments when traversing through selection sets and tries to place the response message at currentResponse.Fields rather than currentResponse.FieldSelectionSet. This is fixed by ensuring the latter fields are populated in this case.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Renames RPCMessage.FieldSelectionSet → FragmentFields, adds rpcPlanningContext traversal helpers (enterNestedField, lastResponseField, leaveNestedField), updates visitors and JSON builder to use FragmentFields for inline/oneOf fragment fields, and adds Owner.pet (Animal) plus corresponding mapping and tests.

Changes

Cohort / File(s) Summary
Core Execution Plan
v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
Renamed exported field FieldSelectionSetFragmentFields on RPCMessage; added RPCFields.Last(); updated sites that construct/populate fragment selections to use FragmentFields keyed by fragment type.
Planning helpers / Context
v2/pkg/engine/datasource/grpc_datasource/util.go, v2/pkg/engine/datasource/grpc_datasource/execution_plan.go (rpcPlanningContext methods)
Added inlineFragmentRefFromAncestors and rpcPlanningContext helpers: enterNestedField, lastResponseField, leaveNestedField to centralize nested-response-message descent/ascend and inline-fragment indexing.
Visitor Logic (non-federation)
v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
Replaced manual ancestor/response-message stack manipulation with planCtx.enterNestedField / planCtx.leaveNestedField; collect inline-fragment fields into message.FragmentFields instead of FieldSelectionSet.
Visitor Logic (federation)
v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor_federation.go
Same planCtx-driven descent/ascend refactor for federation visitor; compute inlineFragmentRef from ancestors, skip entity fragments, and store per-fragment fields under FragmentFields.
JSON Response Builder
v2/pkg/engine/datasource/grpc_datasource/json_builder.go
When serializing oneOf/variant types, read per-type fields from message.FragmentFields instead of FieldSelectionSet.
Schema / Mappings
v2/pkg/grpctest/product.proto, v2/pkg/grpctest/testdata/products.graphqls, v2/pkg/grpctest/mapping/mapping.go, v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go
Added Owner.pet (type Animal) in proto/GraphQL and updated mappings to include Owner -> pet plus added Subcategory nested resolver fields (itemCount, featuredCategory) in test mapping helper.
Tests
v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go, .../execution_plan_composite_test.go, .../execution_plan_federation_test.go, .../execution_plan_field_resolvers_test.go
Updated expected RPC execution-plan literals to use FragmentFields: RPCFieldSelectionSet{...} in place of FieldSelectionSet and expanded nested inline-fragment and federation test cases to cover per-fragment type selections and new regression scenarios.
Misc / Helpers
v2/pkg/engine/datasource/grpc_datasource/execution_plan_composite_test.go, .../mapping_test_helper.go
Test data and mapping helpers updated to reflect new fragment wiring and added test cases/mapping entries for new fields.

Sequence Diagram

sequenceDiagram
    participant Visitor as rpcPlanVisitor
    participant PlanCtx as rpcPlanningContext
    participant Message as RPCMessage
    participant Ancestors as AncestorStack

    Ancestors->>Visitor: push AST nodes (field/selection/inlineFragment)
    Visitor->>PlanCtx: enterNestedField(info, enclosingTypeNode, selectionSetRef, inlineFragmentRef)
    Note over PlanCtx,Message: PlanCtx creates/accesses nested RPCMessage and tracks ancestors
    PlanCtx->>Message: return current nested RPCMessage
    Visitor->>Message: EnterField(name)
    Message->>Message: add field to Fields and/or FragmentFields[typeName]
    Visitor->>PlanCtx: lastResponseField(message, inlineFragmentRef)
    Visitor->>PlanCtx: leaveNestedField(info)
    Note over PlanCtx,Ancestors: PlanCtx pops/restores previous response-message context
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and accurately describes the main fix: handling complex types inside inline fragments in gRPC execution plan, which directly aligns with the core issue and changes made.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dominik/eng-9007-router-does-not-resolve-grpc-nested-list-fields-in-response

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 and usage tips.

@dkorittki
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 27, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 2

🧹 Nitpick comments (1)
v2/pkg/engine/datasource/grpc_datasource/execution_plan_federation_test.go (1)

2141-2170: Prefer fresh mapping/config instances per test case for parallel isolation.

Line 2173 and Line 2683 run tests in parallel while sharing package-level vars (nestedInlineFragmentFederationMapping / nestedInlineFragmentFederationConfigs). Creating fresh instances per case will avoid hidden coupling and future mutation hazards.

♻️ Suggested refactor
-var nestedInlineFragmentFederationMapping = &GRPCMapping{
-    ...
-}
+func newNestedInlineFragmentFederationMapping() *GRPCMapping {
+    return &GRPCMapping{
+        ...
+    }
+}

-var nestedInlineFragmentFederationConfigs = plan.FederationFieldConfigurations{
-    {
-        TypeName:     "User",
-        SelectionSet: "id",
-    },
-}
+func newNestedInlineFragmentFederationConfigs() plan.FederationFieldConfigurations {
+    return plan.FederationFieldConfigurations{
+        {
+            TypeName:     "User",
+            SelectionSet: "id",
+        },
+    }
+}

- mapping:           nestedInlineFragmentFederationMapping,
- federationConfigs: nestedInlineFragmentFederationConfigs,
+ mapping:           newNestedInlineFragmentFederationMapping(),
+ federationConfigs: newNestedInlineFragmentFederationConfigs(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v2/pkg/engine/datasource/grpc_datasource/execution_plan_federation_test.go`
around lines 2141 - 2170, The package-level shared variables
nestedInlineFragmentFederationMapping and nestedInlineFragmentFederationConfigs
cause data races when tests run in parallel; change tests to create fresh
instances per test by replacing references to these globals with factory calls
or local constructions (e.g., NewNestedInlineFragmentFederationMapping() /
NewNestedInlineFragmentFederationConfigs()) inside each test case or before
t.Run so each test gets its own GRPCMapping and
plan.FederationFieldConfigurations instance; ensure any slices/maps are newly
allocated (not shared) to prevent mutation hazards during parallel execution.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor_federation.go`:
- Around line 142-143: When entering an inline fragment handler you overwrite
r.planInfo.inlineFragmentRef without preserving the outer context; change the
enter/leave logic to save the previous inlineFragmentRef into a temporary
variable (e.g., prev := r.planInfo.inlineFragmentRef) before setting
r.planInfo.inlineFragmentRef = ref, and on leave restore it
(r.planInfo.inlineFragmentRef = prev) so nested inline fragments do not clobber
their parent scope; apply the same save/restore pattern to the other
inline-fragment enter/leave pair referenced (the second occurrence that
currently sets and then clears inlineFragmentRef).

In `@v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go`:
- Around line 99-105: EnterInlineFragment currently overwrites
planInfo.inlineFragmentRef without saving the outer context; LeaveInlineFragment
then unconditionally clears it which drops outer fragment context for nested
fragments. Modify EnterInlineFragment (method on rpcPlanVisitor) to save the
current planInfo.inlineFragmentRef (e.g., push onto a small stack or temporary
field) before setting the new ref, and modify LeaveInlineFragment to restore the
previously saved ref instead of setting planInfo.inlineFragmentRef to
ast.InvalidRef; ensure the saved context is cleared after restore so nested
fragment state is preserved correctly.

---

Nitpick comments:
In `@v2/pkg/engine/datasource/grpc_datasource/execution_plan_federation_test.go`:
- Around line 2141-2170: The package-level shared variables
nestedInlineFragmentFederationMapping and nestedInlineFragmentFederationConfigs
cause data races when tests run in parallel; change tests to create fresh
instances per test by replacing references to these globals with factory calls
or local constructions (e.g., NewNestedInlineFragmentFederationMapping() /
NewNestedInlineFragmentFederationConfigs()) inside each test case or before
t.Run so each test gets its own GRPCMapping and
plan.FederationFieldConfigurations instance; ensure any slices/maps are newly
allocated (not shared) to prevent mutation hazards during parallel execution.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78ea8ff and 0851f2f.

📒 Files selected for processing (12)
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_composite_test.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_federation_test.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_field_resolvers_test.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor_federation.go
  • v2/pkg/engine/datasource/grpc_datasource/json_builder.go
  • v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go
  • v2/pkg/grpctest/mapping/mapping.go
  • v2/pkg/grpctest/product.proto
  • v2/pkg/grpctest/testdata/products.graphqls

Comment thread v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor_federation.go Outdated
Comment thread v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go Outdated
@dkorittki dkorittki marked this pull request as ready for review February 27, 2026 17:40
Comment thread v2/pkg/engine/datasource/grpc_datasource/execution_plan.go Outdated
Comment thread v2/pkg/engine/datasource/grpc_datasource/execution_plan.go Outdated
Comment thread v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
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 `@v2/pkg/engine/datasource/grpc_datasource/execution_plan.go`:
- Around line 496-501: The bug is that leaveNestedField pops the ancestors
unconditionally which corrupts currentResponseMessage when EnterSelectionSet
handlers (resolver selections) returned early and didn't push; fix by making
enterNestedField always push the prior currentResponseMessage onto
planningInfo.responseMessageAncestors before changing currentResponseMessage,
and keep leaveNestedField to only pop/restore when responseMessageAncestors is
non-empty (i.e., restore the exact prior value that enterNestedField pushed).
Update the enterNestedField and leaveNestedField implementations (referencing
enterNestedField, leaveNestedField, planningInfo.responseMessageAncestors, and
planningInfo.currentResponseMessage) so pushes and pops are symmetric.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6308734 and 3bd0a59.

📒 Files selected for processing (3)
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor_federation.go

Comment thread v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
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: 2

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

Inline comments:
In `@v2/pkg/engine/datasource/grpc_datasource/execution_plan_federation_test.go`:
- Around line 2735-2752: The mapping block for Fields (the FieldMap literal
including "Product" and "ProductSpecs") is misformatted per gci; reformat the
block so it passes the linter — ensure consistent indentation, spacing, and
alignment for entries like "ProductSpecs", its nested fields ("id", "weight",
"dimensions") and the "relatedProduct" entry (with TargetName "related_product"
and ArgumentMappings using FieldArgumentMap{"category":"category"} style); run
the project formatter and gci (or gofmt/goimports/gci) on
execution_plan_federation_test.go to apply the automatic cleanup so the
FieldMap/FieldArgumentMap literal conforms to the repo style.

In `@v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go`:
- Around line 381-405: The map literal around "Subcategory" and the following
"TestContainer" block has indentation/formatting drift causing gci to fail;
reformat mapping_test_helper.go (the map entries using FieldMapData and
FieldArgumentMap, and keys "Subcategory" and "TestContainer") with your
formatter/import sorter (e.g., gofmt/goimports and gci -w) so the map
indentation and import grouping are corrected and the file passes CI.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3bd0a59 and 143e99d.

📒 Files selected for processing (6)
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_federation_test.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_field_resolvers_test.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
  • v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor_federation.go
  • v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go
  • v2/pkg/grpctest/testdata/products.graphqls

Comment thread v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go Outdated
Copy link
Copy Markdown
Contributor

@Noroth Noroth left a comment

Choose a reason for hiding this comment

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

LGTM

@dkorittki dkorittki merged commit 3b4dbcb into master Mar 5, 2026
10 checks passed
@dkorittki dkorittki deleted the dominik/eng-9007-router-does-not-resolve-grpc-nested-list-fields-in-response branch March 5, 2026 09:30
dkorittki pushed a commit that referenced this pull request Mar 5, 2026
🤖 I have created a release *beep* *boop*
---


##
[2.0.0-rc.258](v2.0.0-rc.257...v2.0.0-rc.258)
(2026-03-05)


### Bug Fixes

* handle complex types inside inline fragments in gRPC execution plan
([#1411](#1411))
([3b4dbcb](3b4dbcb))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
  * Fixed an issue with handling complex types in inline fragments.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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