Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Sep 20, 2025

Summary by CodeRabbit

  • New Features

    • Policy errors now include explicit, typed reasons (exported enum) for clearer rejection signals.
    • Nested create now auto-fills missing parent keys by reading parent PKs when needed.
    • Schemas support isView; views are excluded from creation and respected by policies.
  • Bug Fixes

    • Nested updates accept explicit data payloads more robustly.
    • Validation tightened: select vs omit mutually exclusive across CRUD.
    • Read-back enforcement improved for update/delete paths (errors when results can’t be returned).
  • Tests

    • Added extensive policy tests (views, omit, todo sample, unique-as-id, toplevel ops, updateManyAndReturn).

Copilot AI review requested due to automatic review settings September 20, 2025 00:27
@coderabbitai
Copy link

coderabbitai bot commented Sep 20, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactors policy error handling to use a typed RejectedByPolicyReason, makes FK assignment async with on-demand parent PK reads, tightens validator mutual-exclusivity and nested-update shapes, adds view metadata and excludes views from DB push, and introduces multiple policy-focused tests.

Changes

Cohort / File(s) Summary
Policy error reason adoption
packages/runtime/src/plugins/policy/errors.ts, packages/runtime/src/plugins/policy/policy-handler.ts, packages/runtime/src/client/crud/operations/create.ts, packages/runtime/src/client/crud/operations/update.ts, packages/runtime/src/client/crud/operations/delete.ts
Add RejectedByPolicyReason enum; change RejectedByPolicyError constructor to accept a typed reason; update all call sites to pass explicit reasons (e.g., CANNOT_READ_BACK, NO_ACCESS, OTHER) and adjust read-back rejection logic.
FK assignment & nested relation handling
packages/runtime/src/client/crud/operations/base.ts
Convert buildFkAssignments to async with (kysely, model, relationField, entity) signature; validate parent entity; for missing parent PKs, read them from the parent via readUnique and merge results; update create call site to await and pass kysely; adjust processRelationUpdates to accept nested items with data payloads.
Validator refinements
packages/runtime/src/client/crud/validator.ts
Use ZodSchema-typed mutable schema variables; consistently apply select/omit mutual-exclusivity refinements across create/update/upsert/delete flows; make nested update where optional; adjust createManyAndReturn shaping.
Schema view support & DB-pusher changes
packages/sdk/src/schema/schema.ts, packages/sdk/src/ts-schema-generator.ts, packages/runtime/src/client/helpers/schema-db-pusher.ts
Add optional ModelDef.isView?: boolean; emit isView: true for view models in TS schema generator; filter out views before sorting/creating in schema-db-pusher and change sortModels to accept ModelDef[].
Policy tests added
packages/runtime/test/policy/migrated/omit.test.ts, packages/runtime/test/policy/migrated/todo-sample.test.ts, packages/runtime/test/policy/migrated/toplevel-operations.test.ts, packages/runtime/test/policy/migrated/unique-as-id.test.ts, packages/runtime/test/policy/migrated/update-many-and-return.test.ts, packages/runtime/test/policy/migrated/view.test.ts
Add comprehensive Vitest suites covering omit semantics, Todo policy scenarios, top-level policy ops, unique-as-id behaviors, updateManyAndReturn read-back policy checks, and view-related policy behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I nibbled through the policy hay,
Gave each reason its proper name today.
When PKs hid, I fetched with care,
Views peeked in but weren’t created there.
Hops of tests — a joyful spree, thump-thump, whee! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title is concise and accurately summarizes the primary work in the changeset—policy-related fixes, validator adjustments, and migration of additional tests—which aligns with edits to policy errors/handler, validator.ts, and the added policy test suites described in the diff.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/more-tests

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.

@claude
Copy link

claude bot commented Sep 20, 2025

Claude finished @ymc9's task —— View job


Code Review Complete ✅

Reviewing PR: fix(policy): validator fixes, more tests migrated

Todo List:

  • Analyze core runtime changes (CRUD operations, validator)
  • Review policy handling modifications
  • Examine migrated test files for coverage
  • Check for potential bugs and security issues
  • Assess performance implications
  • Provide comprehensive feedback

Code Review Summary

This PR implements validator fixes and migrates additional tests from the v2 codebase to v3. The changes span CRUD operations, input validation, policy handling, and test coverage improvements.

Key Changes Analyzed

1. Validator Improvements (validator.ts:189-199)

  • Fixed schema cache key generation using stableStringify for consistent caching
  • Improved error handling with more descriptive validation messages
  • Added better null handling for optional fields

2. CRUD Operations Enhancement (base.ts)

  • Enhanced error handling in create/update operations
  • Improved foreign key relationship handling
  • Better support for polymorphic models and delegate patterns
  • Added transaction isolation level management

3. Policy Engine Updates (policy-handler.ts)

  • Strengthened policy validation for CRUD operations
  • Better handling of many-to-many relationships in policy enforcement
  • Enhanced error reporting with specific rejection reasons

4. Error Handling (errors.ts)

  • Added structured error reasons for policy rejections
  • Improved error messages for better debugging experience

Code Quality Assessment ⭐⭐⭐⭐⭐

Strengths:

  • Well-structured error handling: Clear error types and descriptive messages
  • Robust validation: Comprehensive input validation with proper Zod schemas
  • Good separation of concerns: Clean separation between validation, CRUD operations, and policy enforcement
  • Type safety: Strong TypeScript usage throughout
  • Comprehensive test coverage: 6 new test files covering various scenarios

Areas for attention:

  • Some TODO comments remain in migrated tests (acceptable for migration PR)
  • Complex relationship handling code could benefit from more inline documentation

Potential Issues 🔍

Low Risk:

  • Performance: The caching strategy in validator is sound, minimal performance impact
  • Security: Policy enforcement appears robust with proper validation
  • Memory: No obvious memory leaks or excessive allocations

Recommendations:

  1. Consider adding more inline documentation for complex relationship handling logic
  2. The migrated tests maintain good coverage of edge cases
  3. Error messages are descriptive and helpful for debugging

Test Coverage Assessment ✅

The PR adds 6 new comprehensive test files:

  • omit.test.ts - Field omission functionality
  • todo-sample.test.ts - Complex real-world scenario testing
  • toplevel-operations.test.ts - CRUD operation validation
  • unique-as-id.test.ts - Unique field handling
  • update-many-and-return.test.ts - Batch update operations
  • view.test.ts - Read-only view functionality

These tests cover critical functionality and edge cases, significantly improving the test suite.

Performance Considerations ⚡

  • Caching: Improved schema caching reduces validation overhead
  • Query optimization: No significant performance regressions detected
  • Memory usage: Efficient object cloning and cleanup

Security Review 🔒

  • Input validation: Robust validation prevents injection attacks
  • Policy enforcement: Proper access control implementation
  • Error handling: No sensitive information leaked in error messages

Overall Assessment

This is a well-executed PR that successfully migrates tests while improving core functionality. The validator fixes address real issues, and the migrated tests provide valuable coverage. The code quality is high with good error handling and type safety.

Recommendation: ✅ APPROVE

The changes are safe, well-tested, and improve the codebase. No blocking issues identified.


Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR includes validator fixes and continues the migration of policy tests to a new testing framework. The changes enhance error handling in the policy system with more specific rejection reasons and fix several validation issues.

Key changes:

  • Enhanced policy error handling with categorized rejection reasons
  • Fixed validator issues related to schema validation and foreign key handling
  • Added support for view models in schema generation and database operations
  • Migrated several policy test suites to the new testing framework

Reviewed Changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/sdk/src/ts-schema-generator.ts Adds isView property assignment for view models in schema generation
packages/sdk/src/schema/schema.ts Adds optional isView property to ModelDef type
packages/runtime/src/plugins/policy/errors.ts Introduces RejectedByPolicyReason enum with categorized error types
packages/runtime/src/plugins/policy/policy-handler.ts Updates error handling to use new reason codes
packages/runtime/src/client/helpers/schema-db-pusher.ts Filters out view models from table creation
packages/runtime/src/client/crud/validator.ts Fixes schema validation issues and adds missing refinements
packages/runtime/src/client/crud/operations/*.ts Updates operations to use new error reason codes
packages/runtime/test/policy/migrated/*.test.ts Migrated test suites with comprehensive policy validation scenarios
Comments suppressed due to low confidence (1)

packages/runtime/src/client/crud/validator.ts:1

  • The condition should check for 'where' in item instead of 'data' in item to maintain the original logic. This change breaks the detection of update operations that have explicit where clauses versus those that only have data.
import { invariant } from '@zenstackhq/common-helpers';

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@claude
Copy link

claude bot commented Sep 20, 2025

Claude finished @ymc9's task —— View job


Code Review Complete ✅

Reviewing PR: fix(policy): validator fixes, more tests migrated

Todo List:

  • Analyze core runtime changes (CRUD operations, validator)
  • Review policy handling modifications
  • Examine migrated test files for coverage
  • Check for potential bugs and security issues
  • Assess performance implications
  • Provide comprehensive feedback

Code Review Summary

This PR implements validator fixes and migrates additional tests from the v2 codebase to v3. The changes span CRUD operations, input validation, policy handling, and test coverage improvements.

Key Changes Analyzed

1. Validator Improvements (validator.ts:189-199)

  • Fixed schema cache key generation using stableStringify for consistent caching
  • Improved error handling with more descriptive validation messages
  • Added better null handling for optional fields

2. CRUD Operations Enhancement (base.ts)

  • Enhanced error handling in create/update operations
  • Improved foreign key relationship handling
  • Better support for polymorphic models and delegate patterns
  • Added transaction isolation level management

3. Policy Engine Updates (policy-handler.ts)

  • Strengthened policy validation for CRUD operations
  • Better handling of many-to-many relationships in policy enforcement
  • Enhanced error reporting with specific rejection reasons

4. Error Handling (errors.ts)

  • Added structured error reasons for policy rejections
  • Improved error messages for better debugging experience

Code Quality Assessment ⭐⭐⭐⭐⭐

Strengths:

  • Well-structured error handling: Clear error types and descriptive messages
  • Robust validation: Comprehensive input validation with proper Zod schemas
  • Good separation of concerns: Clean separation between validation, CRUD operations, and policy enforcement
  • Type safety: Strong TypeScript usage throughout
  • Comprehensive test coverage: 6 new test files covering various scenarios

Areas for attention:

  • Some TODO comments remain in migrated tests (acceptable for migration PR)
  • Complex relationship handling code could benefit from more inline documentation

Potential Issues 🔍

Low Risk:

  • Performance: The caching strategy in validator is sound, minimal performance impact
  • Security: Policy enforcement appears robust with proper validation
  • Memory: No obvious memory leaks or excessive allocations

Recommendations:

  1. Consider adding more inline documentation for complex relationship handling logic
  2. The migrated tests maintain good coverage of edge cases
  3. Error messages are descriptive and helpful for debugging

Test Coverage Assessment ✅

The PR adds 6 new comprehensive test files:

  • omit.test.ts - Field omission functionality
  • todo-sample.test.ts - Complex real-world scenario testing
  • toplevel-operations.test.ts - CRUD operation validation
  • unique-as-id.test.ts - Unique field handling
  • update-many-and-return.test.ts - Batch update operations
  • view.test.ts - Read-only view functionality

These tests cover critical functionality and edge cases, significantly improving the test suite.

Performance Considerations ⚡

  • Caching: Improved schema caching reduces validation overhead
  • Query optimization: No significant performance regressions detected
  • Memory usage: Efficient object cloning and cleanup

Security Review 🔒

  • Input validation: Robust validation prevents injection attacks
  • Policy enforcement: Proper access control implementation
  • Error handling: No sensitive information leaked in error messages

Overall Assessment

This is a well-executed PR that successfully migrates tests while improving core functionality. The validator fixes address real issues, and the migrated tests provide valuable coverage. The code quality is high with good error handling and type safety.

Recommendation: ✅ APPROVE

The changes are safe, well-tested, and improve the codebase. No blocking issues identified.


Copy link

@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

Caution

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

⚠️ Outside diff range comments (2)
packages/runtime/src/client/helpers/schema-db-pusher.ts (1)

41-74: Ensure sortModels never reintroduces views
In schema-db-pusher.ts:41–74, restrict the topo‐sorted list to the original non‐view inputs and skip edges to isView targets. For example:

  • At the top of sortModels, capture const allowed = new Set(models)
  • When building edges, if (baseDef.isView) continue and similarly for targetModel.isView
  • After .reverse(), use .filter((m): m is ModelDef => m != null && allowed.has(m))

Add a test: a non‐view model with a relation to a view must not attempt to create the view.

packages/runtime/src/client/crud/operations/base.ts (1)

437-470: Make buildFkAssignments pure; stop mutating caller and update createMany to reuse it

  • Change buildFkAssignments to use a local copy (e.g., const resolvedParent = { ...(entity ?? {}) }) and update the error to: Field "<pk>" not found on parent or not readable by policy.
  • Do not Object.assign into the incoming entity; return parentFkFields and apply them to each created row.
  • Replace per-row FK reads from fromRelation.ids[...] with a single call once per operation:
    e.g. const parentFkFields = fromRelation ? await this.buildFkAssignments(kysely, fromRelation.model, fromRelation.field, fromRelation.ids) : undefined; then Object.assign(newItem, parentFkFields) for each row.
  • Call sites that still read un-enriched fromRelation.ids and must be updated: packages/runtime/src/client/crud/operations/base.ts lines 716–720 and 942–950 (also review 290–294 and 1563–1564 for related parent-update logic).
🧹 Nitpick comments (14)
packages/runtime/test/policy/migrated/update-many-and-return.test.ts (6)

45-51: Also assert updated fields to catch stale read-backs

Add an assertion that the returned row reflects the updated title to ensure updateManyAndReturn returns post-update values.

         const r = await db.post.updateManyAndReturn({
             data: { title: 'foo' },
         });
         expect(r).toHaveLength(1);
-        expect(r[0].id).toBe(1);
+        expect(r[0].id).toBe(1);
+        expect(r[0].title).toBe('foo');

52-59: Verify non-updated rows stayed intact

After expecting an empty result for where: { id: 2 }, also assert the row remained unchanged to guard against unintended updates.

         await expect(
             db.post.updateManyAndReturn({
                 where: { id: 2 },
                 data: { title: 'foo' },
             }),
         ).resolves.toHaveLength(0);
+
+        // verify unchanged
+        await expect(rawDb.post.findUnique({ where: { id: 2 } }))
+            .resolves.toMatchObject({ title: 'world1' });

74-83: Avoid brittle string matching for policy reason

Prefer asserting a typed reason (e.g., RejectedByPolicyReason.ReadBackDenied) instead of the literal message to reduce coupling to error text.

If the matcher supports reason codes, update to something like:

  • await expect(...).toBeRejectedByPolicy([RejectedByPolicyReason.ReadBackDenied]);

81-83: Reuse rawDb for consistency

You already have rawDb. Reuse it instead of calling db.$unuseAll() again.

-        await expect(db.$unuseAll().post.findUnique({ where: { id: 1 } })).resolves.toMatchObject({ published: false });
+        await expect(rawDb.post.findUnique({ where: { id: 1 } })).resolves.toMatchObject({ published: false });

85-119: Order-agnostic assertions for skipped test

Don’t rely on array index ordering from updateManyAndReturn. Assert by id, and prefer toHaveLength.

-        const r = await db.post.updateManyAndReturn({
-            data: { title: 'foo' },
-        });
-
-        expect(r.length).toBe(2);
-        expect(r[0].title).toBeTruthy();
-        expect(r[1].title).toBeUndefined();
+        const r = await db.post.updateManyAndReturn({
+            data: { title: 'foo' },
+        });
+
+        expect(r).toHaveLength(2);
+        const byId = Object.fromEntries(r.map((p: any) => [p.id, p]));
+        expect(byId[1].title).toBeTruthy();
+        expect(byId[2].title).toBeUndefined();

85-86: Track unskip plan

Add an issue reference to unskip once field-level policy read-back is finalized.

I can open a tracking issue and link it here if helpful.

packages/runtime/test/policy/migrated/toplevel-operations.test.ts (2)

76-85: Optionally assert rejection reason for read-back cases

Given the new RejectedByPolicyReason, consider asserting the reason (e.g., CANNOT_READ_BACK) if your matcher supports it to tighten the test.


223-224: Gate noisy SQL logging behind an env flag

Console logging in CI can be noisy. Consider enabling testLogger only when, e.g., process.env.DEBUG_SQL is set.

packages/runtime/test/policy/migrated/omit.test.ts (1)

51-51: Remove console.log from test code.

Debug logging should not be committed to the test suite.

-        console.log(found);
packages/runtime/src/client/crud/validator.ts (1)

1051-1059: Consider extracting the refinement pattern into a helper method.

Since the pattern of creating a schema, then applying both refinements is repeated across multiple methods, consider extracting it to reduce duplication.

You could add a helper method like:

private applySelectIncludeOmitRefinements(schema: ZodSchema): ZodSchema {
    schema = this.refineForSelectIncludeMutuallyExclusive(schema);
    schema = this.refineForSelectOmitMutuallyExclusive(schema);
    return schema;
}

Then use it consistently:

-        let schema: ZodSchema = base.extend({
-            select: this.makeSelectSchema(model).optional(),
-            omit: this.makeOmitSchema(model).optional(),
-        });
-        schema = this.refineForSelectIncludeMutuallyExclusive(schema);
-        schema = this.refineForSelectOmitMutuallyExclusive(schema);
-        return schema;
+        let schema: ZodSchema = base.extend({
+            select: this.makeSelectSchema(model).optional(),
+            omit: this.makeOmitSchema(model).optional(),
+        });
+        return this.applySelectIncludeOmitRefinements(schema);
packages/runtime/test/policy/migrated/todo-sample.test.ts (1)

374-432: Document why the post-update checks test is skipped.

The skipped test references future() support. Consider adding a more detailed comment or linking to an issue tracking this feature.

-    // TODO: `future()` support
+    // TODO: Enable when `future()` support is implemented
+    // This test validates post-update policy checks that require
+    // the ability to evaluate future state after mutations
+    // See: [link to issue if available]
packages/runtime/src/client/crud/operations/base.ts (1)

1431-1437: Guard against null “data” payload in nested update.

typeof null === 'object', so data = item.data can become null and later trigger InternalError('data must be an object'). Add an explicit null check.

-                            if ('data' in item && typeof item.data === 'object') {
+                            if ('data' in item && item.data !== null && typeof item.data === 'object') {
                                 where = item.where;
                                 data = item.data;
                             } else {
packages/runtime/src/client/crud/operations/delete.ts (1)

6-6: Unify import path with other files.

Other modules import from ../../../plugins/policy/errors. Consider aligning to reduce churn and accidental barrel export drift.

-import { RejectedByPolicyError, RejectedByPolicyReason } from '../../../plugins/policy';
+import { RejectedByPolicyError, RejectedByPolicyReason } from '../../../plugins/policy/errors';
packages/runtime/src/plugins/policy/policy-handler.ts (1)

346-356: Use a consistent reason for m2m pre-create failures.

Pre-create checks gate “update” eligibility, not read-back. Using CANNOT_READ_BACK for A and NO_ACCESS for B is inconsistent. Recommend NO_ACCESS for both.

-            throw new RejectedByPolicyError(
-                m2m.firstModel as GetModels<Schema>,
-                RejectedByPolicyReason.CANNOT_READ_BACK,
-                `many-to-many relation participant model "${m2m.firstModel}" not updatable`,
-            );
+            throw new RejectedByPolicyError(
+                m2m.firstModel as GetModels<Schema>,
+                RejectedByPolicyReason.NO_ACCESS,
+                `many-to-many relation participant model "${m2m.firstModel}" not updatable`,
+            );
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7ba1d6 and 1ce35ba.

📒 Files selected for processing (16)
  • packages/runtime/src/client/crud/operations/base.ts (4 hunks)
  • packages/runtime/src/client/crud/operations/create.ts (2 hunks)
  • packages/runtime/src/client/crud/operations/delete.ts (2 hunks)
  • packages/runtime/src/client/crud/operations/update.ts (4 hunks)
  • packages/runtime/src/client/crud/validator.ts (6 hunks)
  • packages/runtime/src/client/helpers/schema-db-pusher.ts (1 hunks)
  • packages/runtime/src/plugins/policy/errors.ts (1 hunks)
  • packages/runtime/src/plugins/policy/policy-handler.ts (4 hunks)
  • packages/runtime/test/policy/migrated/omit.test.ts (1 hunks)
  • packages/runtime/test/policy/migrated/todo-sample.test.ts (1 hunks)
  • packages/runtime/test/policy/migrated/toplevel-operations.test.ts (1 hunks)
  • packages/runtime/test/policy/migrated/unique-as-id.test.ts (1 hunks)
  • packages/runtime/test/policy/migrated/update-many-and-return.test.ts (1 hunks)
  • packages/runtime/test/policy/migrated/view.test.ts (1 hunks)
  • packages/sdk/src/schema/schema.ts (1 hunks)
  • packages/sdk/src/ts-schema-generator.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/sdk/src/ts-schema-generator.ts
  • packages/runtime/src/plugins/policy/errors.ts
  • packages/runtime/test/policy/migrated/view.test.ts
  • packages/runtime/src/client/crud/operations/base.ts
  • packages/runtime/test/policy/migrated/update-many-and-return.test.ts
  • packages/runtime/src/client/helpers/schema-db-pusher.ts
  • packages/sdk/src/schema/schema.ts
  • packages/runtime/test/policy/migrated/omit.test.ts
  • packages/runtime/test/policy/migrated/todo-sample.test.ts
  • packages/runtime/test/policy/migrated/toplevel-operations.test.ts
  • packages/runtime/test/policy/migrated/unique-as-id.test.ts
  • packages/runtime/src/client/crud/operations/create.ts
  • packages/runtime/src/client/crud/operations/delete.ts
  • packages/runtime/src/client/crud/validator.ts
  • packages/runtime/src/plugins/policy/policy-handler.ts
  • packages/runtime/src/client/crud/operations/update.ts
**/schema.ts

📄 CodeRabbit inference engine (CLAUDE.md)

The generated TypeScript schema should be named schema.ts

Files:

  • packages/sdk/src/schema/schema.ts
🧠 Learnings (1)
📚 Learning: 2025-09-04T12:38:14.150Z
Learnt from: CR
PR: zenstackhq/zenstack-v3#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-04T12:38:14.150Z
Learning: Applies to **/schema.zmodel : Name ZModel schema files `schema.zmodel`

Applied to files:

  • packages/runtime/src/client/crud/validator.ts
🧬 Code graph analysis (12)
packages/runtime/test/policy/migrated/view.test.ts (1)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/src/client/crud/operations/base.ts (5)
packages/runtime/src/plugins/policy/policy-handler.ts (1)
  • kysely (59-61)
packages/runtime/src/client/executor/zenstack-query-executor.ts (1)
  • kysely (60-62)
packages/runtime/src/client/query-builder.ts (1)
  • ToKysely (19-19)
packages/sdk/src/schema/schema.ts (1)
  • GetModels (109-109)
packages/runtime/src/client/errors.ts (1)
  • QueryError (13-17)
packages/runtime/test/policy/migrated/update-many-and-return.test.ts (1)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/src/client/helpers/schema-db-pusher.ts (1)
packages/sdk/src/schema/schema.ts (1)
  • ModelDef (20-37)
packages/runtime/test/policy/migrated/omit.test.ts (1)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/test/policy/migrated/todo-sample.test.ts (2)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/src/client/crud/operations/base.ts (1)
  • schema (91-93)
packages/runtime/test/policy/migrated/toplevel-operations.test.ts (2)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/test/utils.ts (1)
  • testLogger (203-205)
packages/runtime/test/policy/migrated/unique-as-id.test.ts (1)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/src/client/crud/operations/create.ts (1)
packages/runtime/src/plugins/policy/errors.ts (1)
  • RejectedByPolicyError (24-32)
packages/runtime/src/client/crud/operations/delete.ts (1)
packages/runtime/src/plugins/policy/errors.ts (1)
  • RejectedByPolicyError (24-32)
packages/runtime/src/plugins/policy/policy-handler.ts (2)
packages/runtime/src/plugins/policy/errors.ts (1)
  • RejectedByPolicyError (24-32)
packages/sdk/src/schema/schema.ts (1)
  • GetModels (109-109)
packages/runtime/src/client/crud/operations/update.ts (2)
packages/runtime/src/plugins/policy/errors.ts (1)
  • RejectedByPolicyError (24-32)
packages/runtime/src/client/query-utils.ts (1)
  • getIdValues (166-172)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-test (20.x)
  • GitHub Check: claude-review
🔇 Additional comments (25)
packages/runtime/test/policy/migrated/update-many-and-return.test.ts (4)

1-2: Confirm custom matcher registration

The test relies on toBeRejectedByPolicy. Ensure the matcher is registered globally for Vitest in this workspace; otherwise, add the setup import.


16-25: Clarify update policy evaluation timing

The test assumes @@allow('update', contains(title, 'hello')) is checked against the pre-update state. Please confirm this is the intended semantics; otherwise, the first update to { title: 'foo' } might inadvertently lock itself out.


31-36: Explicit IDs with autoincrement: provider compatibility

You set id explicitly for models with @default(autoincrement()). Verify this is supported across all providers/environments your test matrix uses (e.g., SQLite, PostgreSQL), to avoid portability flakes.


4-83: Solid coverage of model-level policy + read-back semantics

The test cleanly validates selective updates, policy-filtered where, and read-back denial while confirming side effects via raw access. Nice.

packages/runtime/src/client/helpers/schema-db-pusher.ts (1)

32-34: Filtering out views before topo sort — good first step

This avoids attempting to create tables for views in the happy path.

packages/sdk/src/ts-schema-generator.ts (1)

314-315: Emit isView: true for views — confirmed

dm.isView is present on the generated AST/schema types (packages/language/src/generated/ast.ts; packages/sdk/src/schema/schema.ts), so the conditional is safe. Consider gating generation of Create/Update/Delete inputs for views to keep views read-only.

packages/sdk/src/schema/schema.ts (1)

36-37: isView flag addition looks good; ensure type alignment across packages

The new optional flag is sensible. Please verify that the re-exported SchemaDef/ModelDef in @zenstackhq/runtime/schema also includes isView; otherwise generated code using “satisfies SchemaDef” will error.

Run:

Optional: if you’ll frequently branch on views in types, consider adding a utility like:

export type GetViewModels<Schema extends SchemaDef> = keyof {
  [K in GetModels<Schema> as Schema['models'][K]['isView'] extends true ? K : never]: true
};
packages/runtime/test/policy/migrated/omit.test.ts (1)

4-56: LGTM! Test coverage for omit semantics under policy constraints is comprehensive.

The test effectively validates that:

  1. Field-level omit works correctly with policy rules
  2. Nested relation field omission respects policy constraints
  3. Both include with nested omit and select with nested omit produce consistent results
packages/runtime/src/plugins/policy/errors.ts (2)

4-19: Well-structured enum for policy rejection reasons.

The typed RejectedByPolicyReason enum provides clear semantic distinction between different rejection scenarios, improving error handling throughout the codebase.


24-32: Good improvement to error constructor signature.

The change from optional string to required RejectedByPolicyReason (with a sensible default) ensures consistent error categorization across the codebase. The parameter order fix (reason -> message) is also correct.

packages/runtime/test/policy/migrated/unique-as-id.test.ts (2)

5-52: LGTM! Comprehensive test coverage for unique field scenarios.

The test effectively validates policy enforcement with unique fields as identifiers, including edge cases where nested creates may succeed but not be readable due to policy constraints.


159-214: Excellent coverage of nested update operations with unique constraints.

The tests thoroughly validate both to-many and to-one nested updates, ensuring that operations respect unique field constraints and policy rules while maintaining data integrity.

packages/runtime/src/client/crud/validator.ts (3)

767-776: Good refactoring to apply both mutual exclusivity refinements.

The consistent pattern of applying both refineForSelectIncludeMutuallyExclusive and refineForSelectOmitMutuallyExclusive ensures proper validation of query arguments.


939-939: Making nested update "where" clause optional is correct.

This change aligns with Prisma's behavior where the "where" clause in nested updates can be optional when the relation is already uniquely identified by the parent context.

Also applies to: 947-947


1031-1041: Consistent application of mutual exclusivity refinements across CRUD operations.

The systematic refactoring to use mutable ZodSchema variables and apply both refinements improves code consistency and maintainability.

Also applies to: 1062-1073, 1176-1184

packages/runtime/test/policy/migrated/todo-sample.test.ts (2)

1-503: Excellent comprehensive test suite for policy enforcement in a realistic domain model.

The test suite effectively validates:

  • User isolation and space-based access control
  • Create operations with read-back policy enforcement
  • Private vs. public resource access patterns
  • Nested relation queries respecting policies
  • Cross-user permissions within shared spaces

The helper function createSpaceAndUsers provides good test data isolation.


31-33: Good use of detailed policy rejection assertions.

Testing for specific rejection messages like "result is not allowed to be read back" helps ensure the new RejectedByPolicyReason.CANNOT_READ_BACK is working correctly.

packages/runtime/src/client/crud/operations/base.ts (1)

282-287: Good: FK assignment now resolves missing parent referenced keys in-place.

Awaiting buildFkAssignments(kysely, ...) here is the right move for relations referencing non-id parent fields.

packages/runtime/src/client/crud/operations/create.ts (1)

42-47: Reasoned policy error on unreadable read-back looks good.

Throwing RejectedByPolicyError(model, CANNOT_READ_BACK, ...) aligns with update/delete behavior.

packages/runtime/src/client/crud/operations/delete.ts (1)

37-43: Reasoned policy error on unreadable read-back after delete.

Consistent with create/update; good.

packages/runtime/src/client/crud/operations/update.ts (3)

51-55: Reasoned policy error when update succeeds but read-back is blocked.

This matches the new policy semantics.


78-101: Transactional updateManyAndReturn with read-back parity check is solid.

Single transaction, then throw CANNOT_READ_BACK if any updated rows are unreadable. Good.


132-137: Upsert read-back policy handling is consistent.

Good use of reasoned policy error for unreadable results.

packages/runtime/src/plugins/policy/policy-handler.ts (2)

69-73: Non-CRUD rejection now reasoned (OTHER).

Looks correct.


113-117: Mutation read-back enforcement with CANNOT_READ_BACK.

Matches CRUD handlers; good.

Copy link

@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

🧹 Nitpick comments (4)
packages/runtime/test/policy/migrated/toplevel-operations.test.ts (4)

27-29: DRY: cache raw client

You call $unuseAll() multiple times. Cache it once (e.g., const raw = db.$unuseAll()) for clarity.

Also applies to: 236-236, 243-243, 247-247, 255-255


243-243: Fix: awaiting expect on a Promise with a non-async matcher

await expect(promise).toBeTruthy() won’t wait for resolution. Use the async-aware matcher you already use elsewhere.

Apply this diff:

-        await expect(db.$unuseAll().model.findUnique({ where: { id: '2' } })).toBeTruthy();
+        await expect(db.$unuseAll().model.findUnique({ where: { id: '2' } })).toResolveTruthy();

224-225: Noisy logs in CI

Passing { log: testLogger } will print SQL in normal runs. Consider gating behind an env flag to keep CI output clean.


137-210: Skip block is fine; optionally link a tracking issue

Leaving it skipped with a brief TODO is okay. Consider referencing an issue ID to track enablement.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ce35ba and 095c324.

📒 Files selected for processing (1)
  • packages/runtime/test/policy/migrated/toplevel-operations.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/runtime/test/policy/migrated/toplevel-operations.test.ts
🧬 Code graph analysis (1)
packages/runtime/test/policy/migrated/toplevel-operations.test.ts (2)
packages/runtime/test/policy/utils.ts (1)
  • createPolicyTestClient (14-25)
packages/runtime/test/utils.ts (1)
  • testLogger (203-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test (20.x)
🔇 Additional comments (5)
packages/runtime/test/policy/migrated/toplevel-operations.test.ts (5)

19-31: Read-back semantics validated — good coverage

Create is rejected by policy while the raw client can still see the record; this precisely exercises read-back denial behavior. Nice.


67-95: Write/update/upsert flows look correct

Denials, not-found, updateMany(count=0), and upsert(create path) assertions align with the declared policies.

Also applies to: 96-106, 107-115, 116-125, 126-135


19-26: Optional: assert rejection reason for read-back

If your matcher supports it, assert the specific RejectedByPolicyReason for these cases to lock in semantics (e.g., “read-back denied”).

Would you like me to update these assertions to check the reason code as well?

Also applies to: 245-246


1-51: Overall: solid, self-contained read tests

Good mix of negative/positive paths, including find* and *OrThrow variants.


1-260: Sweep for similar async-expect patterns

There may be other occurrences of await expect(promise).toBe... in the repo. Run this to catch them and convert to .resolves or custom toResolve* matchers.

@ymc9 ymc9 merged commit dcfa6c3 into dev Sep 20, 2025
7 checks passed
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