feat(core): request-context threading & per-parameter schema validate hooks - #807
Conversation
… hooks Parse/decode calls accept an opaque context option (e.g. the authenticated actor) that is forwarded to schema validate hooks: - relations/fields/sort schemas gain a per-key validate hook, invoked on the canonical key against the schema that governs it (the target schema for dotted paths); rejections follow the drop-vs-throwOnFailure policy via the new KEY_VALIDATE_REJECTED error code, and a rejected relation prunes its descendants - the filters validate hook receives the context as second argument - hooks are collected during resolution and evaluated afterwards, so sync parse() refuses async hooks (validatorAsyncRequiresAsyncParser) while parseAsync()/decodeAsync() await them sequentially - Schema/SchemaRegistry/define* carry a defaulted CONTEXT generic for definition-site hook typing; schema defaults bypass the hooks - the never-invoked fields.verify option (VerifyFn) is removed
|
Note Reviews pausedUse the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChangesContext-aware Context-aware validation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant SimpleParser
participant SchemaValidator
participant ParsedQuery
Client->>SimpleParser: parseAsync(input, context)
SimpleParser->>SchemaValidator: validate requested keys and filters with context
SchemaValidator-->>SimpleParser: accepted values or rejected paths
SimpleParser-->>ParsedQuery: pruned parsed output
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/parser-simple/test/unit/parser/validate-context.spec.ts (1)
187-231: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winConsider adding a regression test for excluded fields under a rejecting hook.
Given the
EXCLUDE-operator issue flagged inpackages/parser-simple/src/parameter/fields/module.ts(pending validated even for-fieldexclusions, throwing underthrowOnFailurefor a field the client isn't trying to read), a test likeparser.parse(['-email'], { schema: throwOnFailureSchemaRejectingEmail })expecting no throw would pin down the fix.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/parser-simple/test/unit/parser/validate-context.spec.ts` around lines 187 - 231, The validation tests in the fields parser should cover excluded fields: add a regression case alongside the existing validate-hook tests using a schema with throwOnFailure enabled whose validate hook rejects the excluded field, then parse that field with the EXCLUDE operator (for example, “-email”) and assert parsing completes without throwing. Ensure the exclusion prevents validation for fields the client is not requesting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/core/src/parser/parameter/validate.ts`:
- Around line 73-75: Use the full client-facing validation path when
constructing key rejection errors: update both synchronous and asynchronous
validators in validate.ts to pass entry.path, rather than entry.key, to
options.errors.keyValidateRejected at lines 73-75 and 103-105.
In `@packages/parser-simple/src/parameter/fields/module.ts`:
- Around line 145-151: Only add fields to pending in the non-EXCLUDE operator
path, so excluded fields skip read-access validation while remaining in
fields.value for exclusion processing. Also de-duplicate pending entries by
canonical field identity, matching the existing output.value de-duplication, so
alias expansion and relation traversal invoke validation only once.
In `@packages/parser-simple/src/parameter/relations/module.ts`:
- Around line 131-138: Update the mapped-alias handling around Relation creation
and pending validation so dotted mapping targets validate every path segment,
including the root schema’s relations.validate for items. Mirror the
parent-segment processing used for literal dotted inputs, or reject dotted
mapping targets before enqueueing; preserve existing behavior for single-segment
mappings.
---
Nitpick comments:
In `@packages/parser-simple/test/unit/parser/validate-context.spec.ts`:
- Around line 187-231: The validation tests in the fields parser should cover
excluded fields: add a regression case alongside the existing validate-hook
tests using a schema with throwOnFailure enabled whose validate hook rejects the
excluded field, then parse that field with the EXCLUDE operator (for example,
“-email”) and assert parsing completes without throwing. Ensure the exclusion
prevents validation for fields the client is not requesting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 12abc9e9-a699-4937-8495-29280b047346
📒 Files selected for processing (37)
packages/core/src/errors/code.tspackages/core/src/errors/parse.tspackages/core/src/parser/parameter/filters/types.tspackages/core/src/parser/parameter/filters/validate.tspackages/core/src/parser/parameter/index.tspackages/core/src/parser/parameter/relations/types.tspackages/core/src/parser/parameter/sort/types.tspackages/core/src/parser/parameter/validate.tspackages/core/src/parser/query.tspackages/core/src/parser/types.tspackages/core/src/schema/define.tspackages/core/src/schema/module.tspackages/core/src/schema/parameter/fields/define.tspackages/core/src/schema/parameter/fields/schema.tspackages/core/src/schema/parameter/fields/types.tspackages/core/src/schema/parameter/filters/define.tspackages/core/src/schema/parameter/filters/schema.tspackages/core/src/schema/parameter/filters/types.tspackages/core/src/schema/parameter/relations/define.tspackages/core/src/schema/parameter/relations/schema.tspackages/core/src/schema/parameter/relations/types.tspackages/core/src/schema/parameter/sort/define.tspackages/core/src/schema/parameter/sort/schema.tspackages/core/src/schema/parameter/sort/types.tspackages/core/src/schema/registry/module.tspackages/core/src/schema/types.tspackages/docs/guide/errors.mdpackages/docs/guide/filters.mdpackages/docs/guide/recipes/authorization.mdpackages/docs/guide/schemas.mdpackages/parser-expression/src/parameter/filters/module.tspackages/parser-mongo/src/parameter/filters/module.tspackages/parser-simple/src/parameter/fields/module.tspackages/parser-simple/src/parameter/filters/module.tspackages/parser-simple/src/parameter/relations/module.tspackages/parser-simple/src/parameter/sorts/module.tspackages/parser-simple/test/unit/parser/validate-context.spec.ts
|
@coderabbitai pause |
✅ Action performedReviews paused. |
- throw KEY_VALIDATE_REJECTED with the full client-facing path - skip read-access validation for EXCLUDE-operator fields - validate every traversed segment of dotted relation mapping targets - de-duplicate hook invocations for repeated client input
Implements RFC #806, phase 1: per-request dynamic authorization at decode time.
What
contextparse/decode option —ParseQueryOptions/ParseParameterOptionscarry an opaquecontext?: unknown(typically the authenticated actor).URLCodec.decode/decodeAsyncforward options verbatim, so the codec entry points get it for free.validatehooks on therelations,fieldsandsortsub-schemas —(name, context) => MaybeAsync<boolean | undefined>. Hooks run on the canonical (alias-resolved) key against the schema that governs it:include=items.realminvokes the root schema's hook withitemsand the item schema's hook withrealm(viaschemaMapping), so an include can never bypass the related schema's own gate. A rejected relation also prunes every deeper relation reached through it.filters.validatearity extension — the existing leaf validator now receives the context as a second argument (backward compatible; all three dialects forward it).throwOnFailurewith the newErrorCode.KEY_VALIDATE_REJECTED(distinguishable from allow-list misses).parse()refuses a thenable withSCHEMA_VALIDATOR_ASYNC_REQUIRES_ASYNC_PARSER,parseAsync()/decodeAsync()await sequentially.Schema,SchemaRegistry,defineSchemaand the per-parameter options/factories carry a defaultedCONTEXTgeneric, so hooks are typed at the definition site (defineSchema<User, Actor>).fields.verifyoption and itsVerifyFntype (declared with a context parameter but dead since introduction).Design decisions
ResolutionScope. Key resolution stays fully synchronous; parsers record(key, path, schema)obligations during resolution and evaluate them once the parameter is assembled (applyKeySchemaValidation/...Asyncin core). One resolution pass serves both entry points, and pruning is name-based sinceFields.executeclones nodes.defaults are server-authored and bypass the hooks; rejected keys are removed after parameter assembly, sofields/sortdefaults do not re-materialize when a hook empties the selection (documented).CONTEXTdefaults toany, notunknown— under strict function-type contravariance,unknownwould makeSchema<User, Actor>unassignable to the bareSchema<RECORD>references used internally (resolver, registry, parsers).Out of scope (per the RFC)
Relation-scoped residual conditions (join-ON rendering),
ExecuteOptions.contexton backends, and any policy vocabulary — rapiq stays IR + hooks + interpreters.Docs
guide/schemas.md: new "Validate hooks & parse context" section + per-parameter option table rowsguide/recipes/authorization.md: new "Gating: per-actor checks at decode time" layer (motivating include-permission case) + layered-defense rowguide/filters.md,guide/errors.md: context argument,KEY_VALIDATE_REJECTEDTests
parser-simplespec: relation drop/keep with context,undefinedcontext default, target-schema resolution for deep paths, descendant pruning,throwOnFailure, sync-refuses-async,parseAsyncawaiting, fields/sort veto + defaults-bypass, filters context forwarding, end-to-end context threading throughSimpleParser.Closes #806
Summary by CodeRabbit
New Features
Documentation