Skip to content

Conversation

@sanny-io
Copy link
Contributor

@sanny-io sanny-io commented Jan 8, 2026

Allows the @updatedAt attribute to accept an array of field references. An update which only contains ignored fields does not update the timestamp.

Sample Usage

model Post {
    ...
    published Boolean @default(false)
    updatedAt DateTime @updatedAt(ignore: [published])
}

Summary by CodeRabbit

  • New Features

    • The @updatedat attribute now accepts an optional ignore list so specified fields do not trigger updatedAt changes; updates that modify only ignored fields leave the timestamp unchanged.
  • Tests

    • Added unit and end-to-end tests verifying ignorable-field behavior and that updatedAt only changes when non-ignored fields are modified.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

Extends the @updatedAt attribute to accept an optional ignore list; schema types, language stdlib, generator, ORM types, and update runtime now support UpdatedAtInfo with an ignore array. Tests and fixtures were added/updated to validate full-schema generation and update behavior when only ignored fields change.

Changes

Cohort / File(s) Summary
Schema types & language model
packages/schema/src/schema.ts, packages/language/res/stdlib.zmodel
Added UpdatedAtInfo type; changed FieldDef.updatedAt from boolean to `boolean
ORM type integration
packages/orm/src/client/crud-types.ts
Imported UpdatedAtInfo; adjusted optional-field inference to treat updatedAt as `(true
Runtime update logic
packages/orm/src/client/crud/operations/base.ts
Update handler now computes an ignore set from updatedAt info and only auto-updates timestamps when non-ignored scalar/foreign-key fields are present.
Schema generator
packages/sdk/src/ts-schema-generator.ts, packages/cli/test/ts-schema-gen.test.ts
Generator emits updatedAt as true or an object with ignore array depending on attribute args; test added to validate ignorable fields in full schema output.
E2E tests & fixtures
tests/e2e/orm/client-api/update.test.ts, tests/e2e/orm/schemas/basic/schema.ts, tests/e2e/orm/schemas/basic/schema.zmodel
Updated fixture schemas to use updatedAt: { ignore: [...] }; added e2e tests asserting updatedAt is unchanged when only ignored fields change and updated when non-ignored fields change.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • zenstack-v3 PR 477 — Modifies BaseOperationHandler.update logic and related ORM types, overlapping conditional updatedAt behavior.
  • zenstack-v3 PR 3 — Edits TypeScript schema generation and tests touching ts-schema-generator and ts-schema-gen.test, overlapping schema output changes.

Poem

🐇 I nibble code and hop about,
I learned which fields to leave without.
Ignore a tweak, the clock stays still,
Touch a true field — it ticks with will.
Hooray for tidy timestamp skill!

🚥 Pre-merge checks | ✅ 3
✅ 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 'feat: ignore argument for @updatedAt' directly and concisely describes the main change—adding an ignore argument to the @updatedat attribute.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


📜 Recent 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 46db3e2 and 8754a74.

📒 Files selected for processing (1)
  • packages/language/res/stdlib.zmodel
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/language/res/stdlib.zmodel
⏰ 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 (22.x, postgresql)
  • GitHub Check: build-test (22.x, sqlite)

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.

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

🤖 Fix all issues with AI agents
In @packages/sdk/src/ts-schema-generator.ts:
- Around line 566-578: The updatedAt attribute handling uses unsafe casts for
ignoreArg.value and items; add explicit type guards to validate that
ignoreArg.value is an ArrayExpr and each item is a ReferenceExpr before mapping:
check with a helper isArrayExpr(ignoreArg.value) and isReferenceExpr(item), and
if validation fails either skip the ignore mapping or throw a clear error
mentioning the field name and attribute (e.g., "invalid @updatedAt(ignore) —
expected array of references"); then only call
ts.factory.createArrayLiteralExpression and use (item as
ReferenceExpr).target.$refText after those guards. Update the block that builds
the 'updatedAt' propertyAssignment to use these guards and produce a clear
error/log instead of letting runtime type assertions throw.
📜 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 3fde86d and 1ed6565.

📒 Files selected for processing (8)
  • packages/cli/test/ts-schema-gen.test.ts
  • packages/language/res/stdlib.zmodel
  • packages/orm/src/client/crud-types.ts
  • packages/orm/src/client/crud/operations/base.ts
  • packages/schema/src/schema.ts
  • packages/sdk/src/ts-schema-generator.ts
  • tests/e2e/orm/client-api/update.test.ts
  • tests/e2e/orm/schemas/basic/schema.ts
🧰 Additional context used
📓 Path-based instructions (2)
tests/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

E2E tests should validate real-world schema compatibility with established projects

Files:

  • tests/e2e/orm/client-api/update.test.ts
  • tests/e2e/orm/schemas/basic/schema.ts
**/*.zmodel

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.zmodel: Always run zenstack generate after modifying ZModel schemas
ZModel schema files should define database structure and policies that compile to TypeScript via zenstack generate

Files:

  • packages/language/res/stdlib.zmodel
🧠 Learnings (5)
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.test.{ts,tsx} : ORM package tests should include comprehensive client API tests and policy tests

Applied to files:

  • tests/e2e/orm/client-api/update.test.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to tests/e2e/**/*.{ts,tsx} : E2E tests should validate real-world schema compatibility with established projects

Applied to files:

  • tests/e2e/orm/client-api/update.test.ts
  • packages/cli/test/ts-schema-gen.test.ts
  • tests/e2e/orm/schemas/basic/schema.ts
📚 Learning: 2025-12-30T15:07:06.254Z
Learnt from: mwillbanks
Repo: zenstackhq/zenstack-v3 PR: 550
File: packages/orm/src/client/crud/operations/base.ts:158-159
Timestamp: 2025-12-30T15:07:06.254Z
Learning: Do not use ts-expect-error in production code within the zenstackhq/zenstack-v3 repo (e.g., packages/*). Use explicit type annotations, targeted type assertions, or refactor to resolve the type error. ts-expect-error may be acceptable only in test files for stubbing or temporary silencing. Ensure production code is type-safe and maintainable.

Applied to files:

  • packages/schema/src/schema.ts
  • packages/orm/src/client/crud/operations/base.ts
  • packages/orm/src/client/crud-types.ts
  • packages/sdk/src/ts-schema-generator.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Implement plugin hooks at ORM, Kysely, and entity mutation levels for query interception and customization

Applied to files:

  • packages/orm/src/client/crud-types.ts
📚 Learning: 2025-10-21T16:09:31.218Z
Learnt from: ymc9
Repo: zenstackhq/zenstack-v3 PR: 319
File: packages/runtime/src/client/executor/zenstack-query-executor.ts:63-72
Timestamp: 2025-10-21T16:09:31.218Z
Learning: In ZenStack, TypeDefs can be inherited by models. When a TypeDef contains fields with `map` attributes, those mapped field names need to be processed by the QueryNameMapper since they become part of the inheriting model's schema. Therefore, when checking if a schema has mapped names (e.g., in `schemaHasMappedNames`), both `schema.models` and `schema.typeDefs` must be inspected for `@map` and `map` attributes.

Applied to files:

  • packages/orm/src/client/crud-types.ts
🧬 Code graph analysis (5)
tests/e2e/orm/client-api/update.test.ts (1)
tests/e2e/orm/client-api/utils.ts (1)
  • createUser (6-21)
packages/cli/test/ts-schema-gen.test.ts (4)
packages/testtools/src/schema.ts (1)
  • generateTsSchema (40-74)
tests/e2e/orm/schemas/basic/schema.ts (1)
  • schema (292-292)
packages/clients/tanstack-query/test/schemas/basic/schema-lite.ts (1)
  • schema (172-172)
samples/next.js/zenstack/schema-lite.ts (1)
  • schema (106-106)
packages/orm/src/client/crud/operations/base.ts (2)
packages/orm/src/client/query-utils.ts (1)
  • isScalarField (134-137)
packages/common-helpers/src/clone.ts (1)
  • clone (6-24)
packages/orm/src/client/crud-types.ts (1)
packages/schema/src/schema.ts (2)
  • GetModelField (156-160)
  • UpdatedAtInfo (62-64)
packages/sdk/src/ts-schema-generator.ts (3)
packages/language/src/utils.ts (1)
  • getAttribute (59-61)
packages/sdk/src/model-utils.ts (1)
  • getAttribute (66-68)
packages/language/src/generated/ast.ts (6)
  • DataFieldAttribute (348-353)
  • DataFieldAttribute (355-355)
  • ArrayExpr (185-189)
  • ArrayExpr (191-191)
  • ReferenceExpr (688-693)
  • ReferenceExpr (695-695)
⏰ 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 (22.x, postgresql)
  • GitHub Check: build-test (22.x, sqlite)
🔇 Additional comments (9)
packages/schema/src/schema.ts (1)

62-64: LGTM! Clean type definition for the ignore feature.

The new UpdatedAtInfo type and the extension of FieldDef.updatedAt to accept boolean | UpdatedAtInfo are well-designed. The use of readonly string[] ensures immutability, and the conditional type update in FieldHasDefault maintains consistency across the type system.

Also applies to: 73-73, 288-288

tests/e2e/orm/schemas/basic/schema.ts (1)

33-38: LGTM! Generated schema correctly reflects the ignore configuration.

The generated updatedAt field structure with the ignore array matches the UpdatedAtInfo type definition. The inclusion of 'id' and 'createdAt' in the ignore list provides a practical example for E2E testing.

tests/e2e/orm/client-api/update.test.ts (2)

133-167: LGTM! Comprehensive test coverage for ignored fields behavior.

This test validates the core feature: updatedAt should not change when only ignored fields (id, createdAt) are modified. The test covers multiple scenarios including updating ignored fields individually and in combination, and properly verifies the timestamp remains unchanged by fetching the record after each update.


169-183: LGTM! Validates that non-ignored fields trigger updatedAt.

This test ensures the feature doesn't break the default behavior: when any non-ignored field (like name) is updated alongside ignored fields, the updatedAt timestamp should still be updated. Good complementary coverage to the previous test.

packages/language/res/stdlib.zmodel (1)

406-406: LGTM! Backward-compatible attribute signature update.

The addition of the optional ignore: FieldReference[]? parameter to @updatedAt is well-designed:

  • Uses FieldReference[] for type-safe field references
  • Optional parameter maintains backward compatibility with existing schemas
  • Follows ZModel naming conventions with the underscore prefix
packages/cli/test/ts-schema-gen.test.ts (1)

446-726: LGTM! Comprehensive test coverage for the new ignore feature.

The test validates that the generated schema correctly represents @updatedAt(ignore: [email]) with an UpdatedAtInfo object containing the ignore array. The assertions are thorough and cover both the field structure and the attribute representation.

packages/orm/src/client/crud-types.ts (2)

33-33: LGTM! Correct import for the new type.

The UpdatedAtInfo import is necessary to support the updated type signature at line 984.


984-984: LGTM! Type widening correctly handles the new UpdatedAtInfo structure.

The change from extends true to extends (true | UpdatedAtInfo) properly accounts for the new schema where updatedAt can be either a boolean or an object with ignore configuration. This ensures that fields with @updatedAt remain optional during create operations regardless of the ignore configuration.

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

997-1005: LGTM! Correct implementation of conditional timestamp updates.

The logic correctly implements the ignore feature:

  1. Identifies which scalar fields are being updated
  2. Constructs an ignore set from the configuration
  3. Only updates the timestamp if at least one non-ignored scalar field is being updated

Edge cases are handled correctly:

  • Only ignored fields updated → timestamp NOT updated ✓
  • Only relation fields updated → timestamp NOT updated ✓
  • Empty updates → timestamp NOT updated ✓
  • Mix of ignored/non-ignored → timestamp updated ✓

@sanny-io sanny-io marked this pull request as draft January 8, 2026 03:15
@sanny-io
Copy link
Contributor Author

sanny-io commented Jan 8, 2026

@coderabbitai why is the test failing in CI when it passes locally?

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the CI status and runs for PR #572
gh pr checks 572 --repo zenstackhq/zenstack-v3

# Get the latest workflow runs for this PR
gh run list --repo zenstackhq/zenstack-v3 --branch feat/updatedat-ignore --limit 5

# Get detailed logs from the most recent failed run
gh run list --repo zenstackhq/zenstack-v3 --branch feat/updatedat-ignore --limit 1 --json databaseId,conclusion,status --jq '.[0].databaseId' | xargs -I {} gh run view {} --repo zenstackhq/zenstack-v3 --log-failed

Length of output: 1116


🏁 Script executed:

#!/bin/bash
# Try getting logs from the specific run
gh run view 20804120171 --repo zenstackhq/zenstack-v3 --log

# Also check the PR comments for any CI bot messages
gh pr view 572 --repo zenstackhq/zenstack-v3 --json comments --jq '.comments[] | select(.author.login | test("github-actions|bot")) | {author: .author.login, body: .body}'

Length of output: 285018


🏁 Script executed:

#!/bin/bash
# Try to get logs from specific failed jobs
gh run view 20804120171 --repo zenstackhq/zenstack-v3 --job 59754746730 --log 2>&1 | tail -100

echo "---SEPARATOR---"

gh run view 20804120171 --repo zenstackhq/zenstack-v3 --job 59754746726 --log 2>&1 | tail -100

Length of output: 32454


@sanny-io Thanks for the logs — the failures are coming from the CI Postgres setup, not from the updatedAt ignore logic.

What’s different in CI vs local

  • CI Postgres logs are full of: FATAL: role "root" does not exist. This repeats throughout both failing jobs (postgresql and sqlite job also spins up Postgres). That means at least part of the test suite is connecting to Postgres with user=root, but the CI container only has the default user=postgres.
  • Because connections fail (wrong role), some setup/teardown logic misfires, which then surfaces as secondary errors (database "... does not exist", schema "... does not exist", and duplicate key violations when retries hit a half‑initialized DB).

Why it passes locally

  • Your local Postgres likely has a role root (or your local connection string maps to a valid role), so the tests run cleanly.

How to fix
Pick either approach and it should stabilize CI:

Option A — Align connection strings to user=postgres

  • Ensure the test connection URL(s) use user=postgres and password=postgres for CI (and ideally for all environments unless there’s a reason to use root).
  • Concretely, set in the workflow env (or .env.test):
    • POSTGRES_USER=postgres
    • POSTGRES_PASSWORD=postgres
    • POSTGRES_DB=postgres (or a scratch DB your tests create)
    • DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres?schema=public
  • Make sure any hardcoded or fallback URLs in test helpers don’t default to root.

Option B — Create the root role in the CI Postgres container

  • If you must keep user=root in tests, run an init step before tests:
    • psql -U postgres -c "CREATE ROLE root WITH LOGIN SUPERUSER PASSWORD 'root';"
    • Optionally CREATE DATABASE root OWNER root; if your URL expects it.
  • Or mount an init SQL in /docker-entrypoint-initdb.d that creates the role before the suite runs.

Quick checks you can make

  • Search for root in connection strings or env usage in tests/helpers and the GitHub Actions workflow that sets TEST_DB_PROVIDER and DB envs.
  • Verify the DATABASE_URL used by prisma/orm in CI resolves to postgres user (not root).

If you want, I can prep a small PR to:

  • Normalize DATABASE_URL across test packages to use postgres in CI, and
  • Add a sanity check in the workflow to print the effective DATABASE_URL at test start to avoid this class of mismatch.

Let me know which option you prefer and I’ll provide the exact patch.

@sanny-io sanny-io marked this pull request as ready for review January 9, 2026 06:01
@sanny-io
Copy link
Contributor Author

sanny-io commented Jan 9, 2026

What an absolute adventure lol. This should be ready now...

@ymc9 how are the e2e test schemas supposed to be handled? Am I supposed to run the test:generate script and include all those changes in the PR too?

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.

1 participant