feat: add support for requires in protographic#2439
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a RequiredFieldMapping proto and generated TS bindings; implements visitors and validators to parse/normalize GraphQL Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Codecov Report❌ Patch coverage is 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
🚀 New features to boost your workflow:
|
Router image scan passed✅ No security vulnerabilities found in image: |
There was a problem hiding this comment.
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
fieldDefinitionmethod 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:itis imported but not used in this file.All tests in this file use
test(...)syntax. Theitimport 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.RestockDatadoesn't match the test context (this test usesProduct, notWarehouse).🧹 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 mappingWould you like me to open a small cleanup issue or prep a patch?
There was a problem hiding this comment.
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 | 🟡 MinorRemove the stale TODO comment.
The required-fields mapping is now implemented below, so this TODO is misleading.🧹 Suggested cleanup
- // todo: add required fields mapping
…641-implement-proto-generation-in-protographic
…641-implement-proto-generation-in-protographic
endigma
left a comment
There was a problem hiding this comment.
Looks good, design seems functional and not too complex on the gRPC side, few comments:
…641-implement-proto-generation-in-protographic
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
@requiresdirective in protographic. Entities that define required and external fields will now generate rpc definitions for lookups.The implementation is described in the
SDL_TO_PROTOmarkdown file.A couple new visitors were introduced for handling the selection set within the
@requiresdirective.required-fields-visitoruses the selection set from the directive to define the structure for the rpcs and proto messages.abstract-selection-rewriteris 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.
selection-set-validation-visitorallows us to apply validation rules on an arbitrary selection set.Summary by CodeRabbit
New Features
Improvements
Validation
Tests
Chores
Checklist