Skip to content

feat(router): add support for @requires in grpc#2610

Merged
Noroth merged 15 commits intomainfrom
ludwig/eng-9128-update-demo-schema-and-tests-with-required-fields-in-cosmo
Mar 12, 2026
Merged

feat(router): add support for @requires in grpc#2610
Noroth merged 15 commits intomainfrom
ludwig/eng-9128-update-demo-schema-and-tests-with-required-fields-in-cosmo

Conversation

@Noroth
Copy link
Copy Markdown
Contributor

@Noroth Noroth commented Mar 9, 2026

Summary by CodeRabbit

  • New Features

    • Added an Employee "expertise" field and a computed "taggedProjectSummary" that returns an expertise+project-tags summary; service-side support added to resolve per-item summaries.
  • Tests

    • Added end-to-end tests for the computed field: resolves with expertise, with default/no expertise, and returns null for missing employees.
  • Chores

    • Updated router and test harness configuration and dependency bookkeeping to support federation SDL changes.

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.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

@github-actions github-actions bot added the router label Mar 9, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 9, 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

Adds an external expertise field and a computed taggedProjectSummary on Employee in the projects subgraph, implements a gRPC handler to resolve the computed field, propagates required-field RPC mappings in the router, updates test fixtures and tests for @requires behavior, bumps a dependency, and extends demo employee data with expertise values.

Changes

Cohort / File(s) Summary
Projects subgraph schema
demo/pkg/subgraphs/projects/src/schema.graphql
Added expertise: String! @external`` and taggedProjectSummary: String! @requires`(fields: "expertise")` on `Employee`.
Projects subgraph service
demo/pkg/subgraphs/projects/src/service/service.go
Added RequireEmployeeTaggedProjectSummaryById RPC handler that reads item contexts, validates IDs, aggregates project tags, uses incoming expertise tag (defaulting to "none") and formats per-item summary strings; added fmt import.
Router resolver config
router/core/factoryresolver.go
Populate per-entity RequiredFieldsRPC mappings when RequiredFieldMappings exist, embedding required-field RPC configs into EntityRPCConfig.
Router & tests dependency
router/go.mod, router-tests/go.mod
Bumped github.com/wundergraph/graphql-go-tools/v2 to a more specific prerelease build metadata version.
Router tests and configs
router-tests/grpc_subgraph_test.go, router-tests/router_plugin_test.go, router-tests/testenv/testdata/config.json
Added tests exercising Employee @requires resolution (existent and non-existent employees); replaced some require assertions with assert; inserted explicit federation serviceSdl payloads into test configs.
Employees subgraph models & data
demo/pkg/subgraphs/employees/subgraph/model/models_gen.go, demo/pkg/subgraphs/employees/subgraph/employees.go, demo/pkg/subgraphs/employees/subgraph/entity.resolvers.go, demo/pkg/subgraphs/employees/subgraph/schema.graphqls, demo/pkg/subgraphs/employees/subgraph/schema.resolvers.go
Added Expertise string field to Employee model and schema; populated Expertise in sample data and returned objects in resolvers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(router): add support for @requires in grpc' accurately describes the main objective of implementing @requires directive support for gRPC, as evidenced by schema changes, new RPC handlers, and comprehensive test coverage across multiple subgraph and router test files.

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


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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 9, 2026

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-a847aa175a478e2156edcdc12f131b449dc4c3c0

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 9, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 62.41%. Comparing base (73a7e73) to head (e321e17).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##            main    #2610       +/-   ##
==========================================
+ Coverage   1.59%   62.41%   +60.81%     
==========================================
  Files        296      244       -52     
  Lines      41472    25826    -15646     
  Branches     432        0      -432     
==========================================
+ Hits         662    16119    +15457     
+ Misses     40524     8326    -32198     
- Partials     286     1381     +1095     
Files with missing lines Coverage Δ
router/core/factoryresolver.go 79.50% <100.00%> (ø)

... and 539 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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 `@demo/pkg/subgraphs/projects/src/service/service.go`:
- Around line 493-537: The handler RequireEmployeeTaggedProjectSummaryById
currently ignores the required external field ctx.Fields.Tag in the successful
path and silently falls back to it only on ID parse errors; change the logic so
the RPC actually depends on ctx.Fields.Tag (or rejects requests missing/invalid
tag), e.g. validate ctx.Fields.Tag early and return an error if it's empty or
malformed, or incorporate ctx.Fields.Tag into the built summary
(RequireEmployeeTaggedProjectSummaryByIdResult.TaggedProjectSummary) instead of
only using internal project tags; ensure you reference ctx.Key.Id for parsing,
ctx.Fields.Tag for the required field, and return/propagate failures via the
RequireEmployeeTaggedProjectSummaryByIdResponse error path so `@requires` is
enforced.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f4afa476-87a3-4320-bd4c-80eea6983a3c

📥 Commits

Reviewing files that changed from the base of the PR and between ea70e91 and 723c498.

⛔ Files ignored due to path filters (6)
  • demo/pkg/subgraphs/projects/generated/mapping.json is excluded by !**/generated/**
  • demo/pkg/subgraphs/projects/generated/service.pb.go is excluded by !**/*.pb.go, !**/generated/**
  • demo/pkg/subgraphs/projects/generated/service.proto is excluded by !**/generated/**
  • demo/pkg/subgraphs/projects/generated/service_grpc.pb.go is excluded by !**/*.pb.go, !**/generated/**
  • router-tests/go.sum is excluded by !**/*.sum
  • router/go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • demo/pkg/subgraphs/projects/src/schema.graphql
  • demo/pkg/subgraphs/projects/src/service/service.go
  • router-tests/go.mod
  • router-tests/grpc_subgraph_test.go
  • router-tests/router_plugin_test.go
  • router-tests/testenv/testdata/config.json
  • router-tests/testenv/testdata/configWithGRPC.json
  • router-tests/testenv/testdata/configWithPlugins.json
  • router/core/factoryresolver.go
  • router/go.mod

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

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

Inline comments:
In `@demo/pkg/subgraphs/projects/src/service/service.go`:
- Around line 499-526: This loop dereferences ctx.Key.Id and ctx.Fields.Tag
without nil checks which can panic; before calling strconv.ParseInt on
ctx.Key.Id validate ctx.Key != nil and ctx.Key.Id != "" and return
status.Errorf(codes.InvalidArgument, ...) on invalid input (replace the
fmt.Errorf) and before using ctx.Fields.Tag verify ctx.Fields != nil (and
ctx.Fields.Tag presence as needed), then proceed to compute projectTags and
build summary; update error returns to use status.Errorf(codes.InvalidArgument,
"invalid employee id %q: %v", ctx.Key.Id, err) to match other handlers.

In `@router-tests/grpc_subgraph_test.go`:
- Around line 288-296: The test is asserting an empty "employee tag" because the
employee fixture (id 1) leaves the proto3 Tag string uninitialized; set the
employee fixture's Tag field to a non-empty value (e.g., "teamA" or similar) so
the forwarding/hydration path is exercised, then update the test case named
"query employee `@requires` field resolved with tag from employees subgraph" to
expect that tag value in the taggedProjectSummary JSON (replace the empty
"employee tag: ," substring with the chosen non-empty value).

In `@router-tests/router_plugin_test.go`:
- Around line 478-486: The test uses an empty sentinel for the employee's tag
which hides whether the router forwards the `@requires` field; update the test
fixture so employee with id 1 has a non-empty tag (so ctx.Fields.Tag is
populated) and change the expected string in the test case (the entry with name
"query employee `@requires` field resolved with tag from employees subgraph") to
include that tag value instead of the empty string; locate the test case in
router_plugin_test.go and the resolver reference to ctx.Fields.Tag to confirm
the exact formatted output to assert.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d16e5b95-9c34-42b1-b921-29c204f492a0

📥 Commits

Reviewing files that changed from the base of the PR and between 723c498 and c6ede3f.

📒 Files selected for processing (3)
  • demo/pkg/subgraphs/projects/src/service/service.go
  • router-tests/grpc_subgraph_test.go
  • router-tests/router_plugin_test.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.

♻️ Duplicate comments (1)
demo/pkg/subgraphs/projects/src/service/service.go (1)

499-503: ⚠️ Potential issue | 🔴 Critical

Guard nullable protobuf submessages and return a gRPC status error.

Line 500 and Line 522 dereference protobuf submessages before validating them, so a malformed context item can panic this handler. Line 502 also returns fmt.Errorf, which turns invalid input into a generic gRPC Unknown instead of InvalidArgument.

🛠️ Suggested fix
-	for _, ctx := range req.Context {
+	for i, ctx := range req.Context {
+		if ctx == nil || ctx.Key == nil || ctx.Fields == nil {
+			return nil, status.Errorf(codes.InvalidArgument, "context[%d] is missing key or required fields", i)
+		}
+
 		id, err := strconv.ParseInt(ctx.Key.Id, 10, 32)
 		if err != nil {
-			return nil, fmt.Errorf("failed to parse employee id %q: %w", ctx.Key.Id, err)
+			return nil, status.Errorf(codes.InvalidArgument, "invalid employee id %q: %v", ctx.Key.Id, err)
 		}
#!/bin/bash
set -euo pipefail

rg -n -C2 'type RequireEmployeeTaggedProjectSummaryByIdContext struct|Key .*|Fields .*' demo/pkg/subgraphs/projects/generated/service.pb.go
rg -n -C3 'RequireEmployeeTaggedProjectSummaryById|fmt\.Errorf|status\.Errorf' demo/pkg/subgraphs/projects/src/service/service.go

Also applies to: 522-522

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@demo/pkg/subgraphs/projects/src/service/service.go` around lines 499 - 503,
The handler is dereferencing protobuf submessages without null checks and
returns fmt.Errorf which yields gRPC Unknown; update the loop over req.Context
to guard nullable fields (check ctx != nil and ctx.Key != nil before using
ctx.Key.Id) and similarly protect any other dereferences in the same function
(e.g., the other deref near line 522), and replace fmt.Errorf returns for
invalid input with status.Errorf(codes.InvalidArgument, ...) so
invalid/malformed context items produce a proper InvalidArgument gRPC error;
look for RequireEmployeeTaggedProjectSummaryByIdContext and the fmt.Errorf
usages in this handler to locate the spots to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@demo/pkg/subgraphs/projects/src/service/service.go`:
- Around line 499-503: The handler is dereferencing protobuf submessages without
null checks and returns fmt.Errorf which yields gRPC Unknown; update the loop
over req.Context to guard nullable fields (check ctx != nil and ctx.Key != nil
before using ctx.Key.Id) and similarly protect any other dereferences in the
same function (e.g., the other deref near line 522), and replace fmt.Errorf
returns for invalid input with status.Errorf(codes.InvalidArgument, ...) so
invalid/malformed context items produce a proper InvalidArgument gRPC error;
look for RequireEmployeeTaggedProjectSummaryByIdContext and the fmt.Errorf
usages in this handler to locate the spots to change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d7815b5-bcd2-47a1-b7fd-bb99baf56632

📥 Commits

Reviewing files that changed from the base of the PR and between c6ede3f and a53c1f1.

📒 Files selected for processing (4)
  • demo/pkg/subgraphs/employees/subgraph/employees.go
  • demo/pkg/subgraphs/projects/src/service/service.go
  • router-tests/grpc_subgraph_test.go
  • router-tests/router_plugin_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • router-tests/grpc_subgraph_test.go
  • router-tests/router_plugin_test.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 `@demo/pkg/subgraphs/projects/src/service/service.go`:
- Around line 499-529: The code currently defaults a missing required field to
"none" by setting employeeTag := ctx.GetFields().GetTag(); if employeeTag == ""
{ employeeTag = "none" }, which masks missing `@requires` data; instead, detect an
empty tag after calling ctx.GetFields().GetTag() and return an error using
status.Errorf(codes.InvalidArgument, "missing tag in requires field") (or
similar) so the RPC fails fast; update the handler that iterates req.Context and
uses ctx/GetFields/GetTag (the employeeTag variable) to return InvalidArgument
when empty rather than assigning a default.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c646e234-5ffa-47e3-801f-b92b09db30bc

📥 Commits

Reviewing files that changed from the base of the PR and between a53c1f1 and 24a1a3d.

📒 Files selected for processing (1)
  • demo/pkg/subgraphs/projects/src/service/service.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.

Caution

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

⚠️ Outside diff range comments (2)
demo/pkg/subgraphs/employees/subgraph/schema.resolvers.go (2)

186-194: ⚠️ Potential issue | 🟡 Minor

Missing Expertise field in FirstEmployee return value.

Similar to UpdateEmployeeTag, the FirstEmployee resolver doesn't include Expertise in the constructed Employee object, creating an inconsistency with other resolvers.

Suggested fix
 	employee := &model.Employee{
 		ID:        Employees[0].ID,
 		Details:   Employees[0].Details,
 		Tag:       Employees[0].Tag,
+		Expertise: Employees[0].Expertise,
 		Role:      Employees[0].Role,
 		Notes:     Employees[0].Notes,
 		UpdatedAt: time.Now().String(),
 		StartDate: Employees[0].StartDate,
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@demo/pkg/subgraphs/employees/subgraph/schema.resolvers.go` around lines 186 -
194, The constructed Employee returned by the FirstEmployee resolver is missing
the Expertise field; update the Employee initialization in FirstEmployee (the
model.Employee literal created from Employees[0]) to include Expertise:
Employees[0].Expertise so it matches other resolvers like UpdateEmployeeTag and
maintains consistent returned shape.

70-78: ⚠️ Potential issue | 🟡 Minor

Missing Expertise field in UpdateEmployeeTag return value.

The UpdateEmployeeTag mutation constructs a new Employee object but doesn't include the Expertise field, while other resolvers (e.g., Employee, Employees) now do. This inconsistency could cause queries that fetch expertise after a tag update to return an empty string.

Suggested fix
 			return &model.Employee{
 				ID:        employee.ID,
 				Details:   details,
 				Tag:       tag,
+				Expertise: employee.Expertise,
 				Role:      employee.Role,
 				Notes:     employee.Notes,
 				UpdatedAt: time.Now().String(),
 				StartDate: employee.StartDate,
 			}, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@demo/pkg/subgraphs/employees/subgraph/schema.resolvers.go` around lines 70 -
78, The UpdateEmployeeTag resolver builds and returns a model.Employee but omits
the Expertise field; modify the returned struct in UpdateEmployeeTag to include
Expertise: employee.Expertise (matching other resolvers like Employee/Employees)
so the updated payload includes the employee's expertise after a tag update.
🧹 Nitpick comments (1)
demo/pkg/subgraphs/projects/src/service/service.go (1)

526-529: Consider rejecting missing expertise instead of defaulting.

The fallback to "none" when expertise is empty could mask a broken @requires forwarding path. Since this RPC exists specifically to resolve a field that depends on the external expertise field, silently using a default defeats the purpose of validating that the router correctly forwards required data.

The tests currently use employees with non-empty Expertise values (e.g., "Backend Architecture"), so this won't hide bugs today, but it reduces confidence that the @requires contract is enforced.

Suggested fix
 		employeeExpertise := ctx.GetFields().GetExpertise()
 		if employeeExpertise == "" {
-			employeeExpertise = "none"
+			return nil, status.Errorf(codes.InvalidArgument, "missing required expertise field for employee id %q", keyID)
 		}

Based on learnings: "fields with the requires directive are intentionally resolved by separate dedicated RPCs so the request must carry the external dependencies they depend on."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@demo/pkg/subgraphs/projects/src/service/service.go` around lines 526 - 529,
The code currently defaults an empty expertise to "none"
(ctx.GetFields().GetExpertise()) which can mask missing forwarded `@requires`
data; remove the fallback and instead return an error when expertise is empty by
validating ctx.GetFields().GetExpertise() at the start of the resolver (the
function handling this RPC in service.go) and returning an appropriate failure
(e.g., InvalidArgument or Unauthenticated) with a clear message if it's empty so
callers/router must forward the required field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@demo/pkg/subgraphs/employees/subgraph/schema.resolvers.go`:
- Around line 186-194: The constructed Employee returned by the FirstEmployee
resolver is missing the Expertise field; update the Employee initialization in
FirstEmployee (the model.Employee literal created from Employees[0]) to include
Expertise: Employees[0].Expertise so it matches other resolvers like
UpdateEmployeeTag and maintains consistent returned shape.
- Around line 70-78: The UpdateEmployeeTag resolver builds and returns a
model.Employee but omits the Expertise field; modify the returned struct in
UpdateEmployeeTag to include Expertise: employee.Expertise (matching other
resolvers like Employee/Employees) so the updated payload includes the
employee's expertise after a tag update.

---

Nitpick comments:
In `@demo/pkg/subgraphs/projects/src/service/service.go`:
- Around line 526-529: The code currently defaults an empty expertise to "none"
(ctx.GetFields().GetExpertise()) which can mask missing forwarded `@requires`
data; remove the fallback and instead return an error when expertise is empty by
validating ctx.GetFields().GetExpertise() at the start of the resolver (the
function handling this RPC in service.go) and returning an appropriate failure
(e.g., InvalidArgument or Unauthenticated) with a clear message if it's empty so
callers/router must forward the required field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4e3be140-73d2-4618-81e4-2677b53f17a4

📥 Commits

Reviewing files that changed from the base of the PR and between 24a1a3d and e7c4158.

⛔ Files ignored due to path filters (4)
  • demo/pkg/subgraphs/employees/subgraph/generated/generated.go is excluded by !**/generated/**
  • demo/pkg/subgraphs/projects/generated/mapping.json is excluded by !**/generated/**
  • demo/pkg/subgraphs/projects/generated/service.pb.go is excluded by !**/*.pb.go, !**/generated/**
  • demo/pkg/subgraphs/projects/generated/service.proto is excluded by !**/generated/**
📒 Files selected for processing (13)
  • demo/pkg/subgraphs/employees/subgraph/employees.go
  • demo/pkg/subgraphs/employees/subgraph/entity.resolvers.go
  • demo/pkg/subgraphs/employees/subgraph/model/models_gen.go
  • demo/pkg/subgraphs/employees/subgraph/schema.graphqls
  • demo/pkg/subgraphs/employees/subgraph/schema.resolvers.go
  • demo/pkg/subgraphs/projects/src/schema.graphql
  • demo/pkg/subgraphs/projects/src/service/service.go
  • router-tests/grpc_subgraph_test.go
  • router-tests/router_plugin_test.go
  • router-tests/testenv/testdata/config.json
  • router-tests/testenv/testdata/configWithEdfs.json
  • router-tests/testenv/testdata/configWithGRPC.json
  • router-tests/testenv/testdata/configWithPlugins.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • demo/pkg/subgraphs/projects/src/schema.graphql

@Noroth Noroth marked this pull request as ready for review March 10, 2026 14:19
@Noroth Noroth requested a review from wilsonrivera March 10, 2026 14:19
@Noroth Noroth requested a review from SkArchon as a code owner March 11, 2026 08:43
@Noroth Noroth merged commit 670f75d into main Mar 12, 2026
31 checks passed
@Noroth Noroth deleted the ludwig/eng-9128-update-demo-schema-and-tests-with-required-fields-in-cosmo branch March 12, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants