Skip to content

feat: add support for requires in protographic#2439

Merged
Noroth merged 76 commits intomainfrom
ludwig/eng-8641-implement-proto-generation-in-protographic
Feb 26, 2026
Merged

feat: add support for requires in protographic#2439
Noroth merged 76 commits intomainfrom
ludwig/eng-8641-implement-proto-generation-in-protographic

Conversation

@Noroth
Copy link
Copy Markdown
Contributor

@Noroth Noroth commented Jan 5, 2026

Note

This PR has a lot of files changed because the common linting rules were included, which has been missed before.
A lot of files are simply re-formatted.

This PR introduces support for the @requires directive in protographic. Entities that define required and external fields will now generate rpc definitions for lookups.

The implementation is described in the SDL_TO_PROTO markdown file.

A couple new visitors were introduced for handling the selection set within the @requires directive.

  • The required-fields-visitor uses the selection set from the directive to define the structure for the rpcs and proto messages.
  • The abstract-selection-rewriter is used to normalize the selection set. In general we can imagine that each selection set inside of a requires directive reflects a Graphql operation. Therefore we can also include interfaces and Unions to define the exact fields that we want to use. As interfaces and unions can be nested we need to normalize those to provide all allowed types to generate the proper proto messages.

Note

Using Inline fragments is currently not yet allowed due to a validation rule. This functionality will be provided in a separate implementation for the engine to reduce the scope of the PRs. The protographic is independent therefore I decided to include the functionality in this PR already.

  • The selection-set-validation-visitor allows us to apply validation rules on an arbitrary selection set.

Summary by CodeRabbit

  • New Features

    • Per-field "required fields" for federated entities with generated RPCs, proto messages, and mappings; selection-set normalization for abstract/interface types; public API to produce deterministic proto output and expose required-field visitors.
  • Improvements

    • Modular proto-generation utilities, expanded naming helpers and constants, safer validation/traversal, deterministic ordering and wrapper handling for lists.
  • Validation

    • New rule disallowing abstract types inside @requires selections.
  • Tests

    • Large expansion of tests covering field sets, federation, proto output, naming, validation, and utilities.
  • Chores

    • Lint/config, import path and CI workflow updates.

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 5, 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 a RequiredFieldMapping proto and generated TS bindings; implements visitors and validators to parse/normalize GraphQL @requires selection sets and produce per-key RPCs/messages and mappings; centralizes proto rendering utilities and types; integrates required-field mappings into SDL→proto and SDL→mapping flows; expands tests, linting, and CI workspace handling.

Changes

Cohort / File(s) Summary
Proto & generated bindings
proto/wg/cosmo/node/v1/node.proto, connect/src/wg/cosmo/node/v1/node_pb.ts
Adds RequiredFieldMapping message and repeated RequiredFieldMapping required_field_mappings = 7 on EntityMapping; generates corresponding TypeScript class and serialization/equals helpers.
Required‑fields pipeline & visitors
protographic/src/required-fields-visitor.ts, protographic/src/abstract-selection-rewriter.ts, protographic/src/selection-set-validation-visitor.ts
New visitors and helpers to validate and normalize @requires selection sets, rewrite interface selections into concrete fragments, and synthesize Proto messages/RPCs and mapping metadata for required fields.
SDL→mapping integration
protographic/src/sdl-to-mapping-visitor.ts, protographic/src/sdl-validation-visitor.ts
Wires required-field generation into entity processing: extracts @requires field sets, validates them, runs RequiredFieldsVisitor, and appends generated mappings into EntityMapping.requiredFieldMappings.
SDL→proto refactor & proto utilities
protographic/src/sdl-to-proto-visitor.ts, protographic/src/proto-utils.ts, protographic/src/types.ts
Refactors proto assembly with new ProtoMessage/ProtoFieldType types and helpers, centralizes rendering (messages, RPCs, wrappers, composites), implements deterministic visit flow and wrapper/list handling, and exposes new visit()/lock-data APIs.
Naming, constants & public exports
protographic/src/naming-conventions.ts, protographic/src/string-constants.ts, protographic/src/index.ts
Adds key-normalization and name-generation helpers, directive/name string constants (key, external, requires, fields), and exports RequiredFieldsVisitor plus related constants.
Message/wrapper & lock internals
protographic/src/operations/*, protographic/src/proto-lock.ts, protographic/src/operation-to-proto.ts
Numerous internal refactors: iteration style changes, parameter annotation cleanups, wrapper/list wrapper handling improvements, and updated proto-lock reconciliation logic.
Tests: new & updated suites
protographic/tests/** (many files)
Large addition and updates of tests: field-set visitors, AbstractSelectionRewriter, buildProtoMessage, federation required-field scenarios, plus mass test import/path changes and snapshots.
Linting, packaging & CI
protographic/package.json, protographic/.eslintrc, protographic/.eslintignore, .github/workflows/protographic.yaml
Adds @wundergraph/composition dependency, ESLint tooling/config and ignore entries, updates lint scripts, and adjusts workspace install/build steps in CI.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature addition: support for the @requires directive in the protographic codebase, which is evident across multiple new and modified files.
Docstring Coverage ✅ Passed Docstring coverage is 89.13% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 5, 2026

Codecov Report

❌ Patch coverage is 89.59796% with 163 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.84%. Comparing base (cc360a6) to head (bdec880).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
protographic/src/abstract-selection-rewriter.ts 89.69% 37 Missing ⚠️
protographic/src/required-fields-visitor.ts 91.76% 27 Missing ⚠️
protographic/src/operations/message-builder.ts 18.75% 26 Missing ⚠️
protographic/src/sdl-to-proto-visitor.ts 92.80% 18 Missing ⚠️
...otographic/src/selection-set-validation-visitor.ts 84.11% 17 Missing ⚠️
protographic/src/sdl-validation-visitor.ts 90.21% 9 Missing ⚠️
protographic/src/sdl-to-mapping-visitor.ts 89.47% 8 Missing ⚠️
protographic/src/proto-lock.ts 70.58% 5 Missing ⚠️
protographic/src/operations/field-numbering.ts 55.55% 4 Missing ⚠️
...rotographic/src/operations/proto-text-generator.ts 81.81% 4 Missing ⚠️
... and 3 more
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2439       +/-   ##
===========================================
- Coverage   62.28%   43.84%   -18.45%     
===========================================
  Files         242     1034      +792     
  Lines       25559   144938   +119379     
  Branches        0     9274     +9274     
===========================================
+ Hits        15919    63542    +47623     
- Misses       8300    79700    +71400     
- Partials     1340     1696      +356     
Files with missing lines Coverage Δ
protographic/src/index.ts 86.84% <100.00%> (ø)
protographic/src/operation-to-proto.ts 83.79% <100.00%> (ø)
protographic/src/operations/proto-field-options.ts 100.00% <100.00%> (ø)
protographic/src/operations/type-mapper.ts 88.74% <100.00%> (ø)
protographic/src/string-constants.ts 100.00% <100.00%> (ø)
protographic/src/types.ts 100.00% <100.00%> (ø)
router/gen/proto/wg/cosmo/node/v1/node.pb.go 20.62% <ø> (-0.26%) ⬇️
protographic/src/naming-conventions.ts 96.96% <93.10%> (ø)
protographic/src/proto-utils.ts 98.98% <98.98%> (ø)
protographic/src/operations/field-numbering.ts 83.78% <55.55%> (ø)
... and 10 more

... and 780 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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Jan 19, 2026

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-453b6e0e1f6d5a2ee83c0d4978d5de55e094cf11

@Noroth Noroth marked this pull request as ready for review February 3, 2026 10:20
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: 8

🤖 Fix all issues with AI agents
In `@protographic/src/abstract-selection-rewriter.ts`:
- Line 57: The currentType property is only set in the constructor and never
updated during AST traversal, so findNamedTypeForField always resolves against
the root type; update currentType as the visitor descends and ascends into
fields: when entering a field (or selection set) look up the field's type from
the previous currentType using the same logic as findNamedTypeForField and set
currentType to the named/unwrapped type, and when leaving the field restore the
previous currentType (e.g., push/pop or store parent type); modify the visitor
methods in the AbstractSelectionRewriter class that handle entering/leaving
fields/selections to maintain currentType correctly so nested selections resolve
to their actual object types.

In `@protographic/src/naming-conventions.ts`:
- Around line 88-110: The doc examples for createRequiredFieldsMethodName are
out of sync with createMethodSuffixFromEntityKey which sorts keys; update the
examples or remove the sort—prefer updating the examples: change the example
showing "name,id" to reflect the sorted suffix (i.e.,
createMethodSuffixFromEntityKey('name,id') => 'ByIdAndName' and
createRequiredFieldsMethodName('User','post','name,id') =>
'RequireUserPostByIdAndName'); reference createRequiredFieldsMethodName and
createMethodSuffixFromEntityKey when making the change.

In `@protographic/src/required-fields-visitor.ts`:
- Around line 496-519: The code in handleCompositeType overwrites
this.current.compositeType for each interface/union field so only the last
composite is kept; change the message model to hold an array (e.g.,
this.current.compositeTypes) and update handleCompositeType to push a new
composite metadata object instead of assigning to compositeType (use
CompositeMessageKind.INTERFACE/UNION, implementingTypes/memberTypes and typeName
as in the diff); also update any consumers/readers of current.compositeType to
iterate the new compositeTypes array (and adjust types/interfaces for the
message object accordingly).
- Around line 439-477: The nested message naming uses the object type name which
can collide when the same object type is used by multiple fields; update
onEnterSelectionSet to derive a unique nested message identifier (e.g., include
the parent field name or path) instead of solely using this.currentType.name,
then check this.current.nestedMessages for an existing nested by that identifier
and reuse it (push existing into stack and set this.current) instead of always
creating a new one; reference functions/vars to change: onEnterSelectionSet,
findObjectTypeForField / findObjectType, this.current, this.currentType,
this.current.nestedMessages, nested (ProtoMessage), this.stack, and
this.current.name to ensure no duplicate nested message definitions.
- Around line 141-152: The visitor currently iterates raw key directive strings
and emits duplicate RPC/messages when keys differ only by order/spacing; update
visit() to normalize and deduplicate key field sets (use the same normalization
that createRequiredFieldsMethodName uses or normalize by parsing, trimming and
sorting field names) by computing a normalizedKey (e.g., from
getKeyFieldsString(keyDirective) then normalize) and maintain a Set of
seenNormalizedKeys; if normalizedKey is already seen skip emitting (skip setting
this.mapping[this.currentKeyFieldsString] and calling visit(this.fieldSetDoc,
this.visitor)), otherwise add to seen and proceed, ensuring you still reference
this.currentKeyFieldsString/getKeyFieldsString and the mapping/visit calls when
emitting the first occurrence.

In `@protographic/src/sdl-validation-visitor.ts`:
- Around line 397-424: validateDisallowAbstractTypesForRequires currently calls
parse(fieldSelections.value) which can throw and abort validation; wrap the
parse call in a try/catch inside validateDisallowAbstractTypesForRequires, and
on parse failure convert the exception into a validation error via this.addError
(include parse error message and ctx.node.loc) instead of rethrowing; only
proceed to construct SelectionSetValidationVisitor, call visit(), and relay its
errors/warnings if parse succeeds.

In `@protographic/src/selection-set-validation-visitor.ts`:
- Around line 110-181: getFieldDefinition currently throws when a field isn't
found which aborts validation; change it to return null (or undefined) instead
and push a validation error into this.validationResult.errors (include field
name and this.currentType.name for context). Update the getFieldDefinition
signature to allow null and remove the throw; then update callers — onEnterField
and onEnterSelectionSet — to check the returned value (e.g., if (!fieldDef) {
return; } or push errors as needed) before using fieldDef.type or accessing
properties. Ensure error messages match existing validation style and keep
traversal/validation continuing instead of throwing.

In `@protographic/tests/field-set/01-basics.test.ts`:
- Around line 69-122: The test's `@requires` field set never contains duplicates
so the deduplication logic isn't exercised; update the SDL in this test (the sdl
string used to build the schema in the 'should visit a field set for a scalar
type and deduplicate fields' test) to include a duplicate selection (e.g. change
`@requires`(fields: "name") to `@requires`(fields: "name name") or otherwise repeat
"name") so RequiredFieldsVisitor receives a duplicated field in fieldSet and the
dedupe path is tested.
🧹 Nitpick comments (5)
protographic/src/abstract-selection-rewriter.ts (1)

173-175: Unused private method.

The fieldDefinition method is defined but never called anywhere in this class.

♻️ Remove unused method
-  /**
-   * Retrieves the field definition for a given field name from the current type.
-   *
-   * `@param` fieldName - The name of the field to look up
-   * `@returns` The GraphQL field definition, or undefined if not found
-   */
-  private fieldDefinition(fieldName: string): GraphQLField<any, any, any> | undefined {
-    return this.currentType.getFields()[fieldName];
-  }
protographic/tests/sdl-to-proto/01-basic-types.test.ts (1)

1-1: Unused import: it is imported but not used in this file.

All tests in this file use test(...) syntax. The it import appears unused.

♻️ Remove unused import
-import { describe, expect, it, test } from 'vitest';
+import { describe, expect, test } from 'vitest';
protographic/tests/sdl-to-mapping/03-federation.test.ts (2)

1008-1008: Stale comment - appears to be a leftover from development.

The comment // RequireWarehouseStockHealthScoreByIdFields.RestockData doesn't match the test context (this test uses Product, not Warehouse).

🧹 Remove stale comment
     const mapping = compileGraphQLToMapping(sdl, 'ProductService');

-    // RequireWarehouseStockHealthScoreByIdFields.RestockData
     expect(mapping.toJson()).toMatchInlineSnapshot(`

1103-1103: Stale comment - same leftover as above.

🧹 Remove stale comment
     const mapping = compileGraphQLToMapping(sdl, 'ProductService');

-    // RequireWarehouseStockHealthScoreByIdFields.RestockData
     expect(mapping.toJson()).toMatchInlineSnapshot(`
protographic/src/sdl-to-mapping-visitor.ts (1)

131-141: Remove stale TODO to avoid confusion.

Required-field mapping is now implemented below, so this TODO is misleading.

💡 Suggested cleanup
-            // todo: add required fields mapping

Would you like me to open a small cleanup issue or prep a patch?

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

Caution

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

⚠️ Outside diff range comments (1)
protographic/src/sdl-to-mapping-visitor.ts (1)

130-141: ⚠️ Potential issue | 🟡 Minor

Remove the stale TODO comment.
The required-fields mapping is now implemented below, so this TODO is misleading.

🧹 Suggested cleanup
-            // todo: add required fields mapping

Copy link
Copy Markdown
Member

@endigma endigma left a comment

Choose a reason for hiding this comment

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

Looks good, design seems functional and not too complex on the gRPC side, few comments:

Copy link
Copy Markdown
Contributor

@dkorittki dkorittki left a comment

Choose a reason for hiding this comment

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

lgtm

@Noroth Noroth merged commit 48a1613 into main Feb 26, 2026
50 checks passed
@Noroth Noroth deleted the ludwig/eng-8641-implement-proto-generation-in-protographic branch February 26, 2026 16:02
@coderabbitai coderabbitai bot mentioned this pull request Mar 30, 2026
5 tasks
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.

6 participants