Skip to content

implement delegateMap attribute#2676

Open
wolflu05 wants to merge 1 commit into
zenstackhq:devfrom
wolflu05:delegate-map
Open

implement delegateMap attribute#2676
wolflu05 wants to merge 1 commit into
zenstackhq:devfrom
wolflu05:delegate-map

Conversation

@wolflu05
Copy link
Copy Markdown
Contributor

@wolflu05 wolflu05 commented May 20, 2026

This PR implement custom delegate mapping as per option 3 described in #2558. This is my first "larger" PR to this project, so please let me know if there is anything that could be improved.

closes #2558

Summary by CodeRabbit

Release Notes

  • New Features

    • Added @@delegateMap attribute to enable mapping delegate sub-models to specific discriminator values
    • Enhanced support for enum-based discriminator fields in polymorphic delegate models
  • Improvements

    • Strengthened validation for delegate model configurations and discriminator value uniqueness
    • Improved type safety in discriminator resolution and polymorphic query results

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

This PR implements the @@delegateMap attribute to allow custom discriminator value mapping for delegate polymorphism. The changes add language validation, schema metadata support, ORM type system updates, and comprehensive test coverage demonstrating enum-driven discriminator values replacing model-name defaults.

Changes

Delegate Map Feature

Layer / File(s) Summary
Language schema definition and validation
packages/language/res/stdlib.zmodel, packages/language/src/validators/datamodel-validator.ts, packages/language/test/delegate.test.ts
Add @@delegateMap attribute and implement validation to ensure correct usage only on models extending delegate bases, validate discriminator values against enum types, detect duplicate mappings, and provide comprehensive test coverage for valid/invalid scenarios.
Schema metadata and code generation
packages/schema/src/schema.ts, packages/sdk/src/ts-schema-generator.ts
Add optional delegateMap?: string field to ModelDef, introduce GetModelDelegateMapValue conditional type for discriminator typing, and generate delegateMap metadata in SDK-generated model objects via new getDelegateMapValue helper.
ORM type system and discriminator computation
packages/orm/src/client/crud-types.ts, packages/orm/src/client/query-utils.ts, packages/orm/src/client/crud/operations/base.ts
Update DelegateUnionResult to use GetModelDelegateMapValue for discriminator typing, export getDelegateDiscriminatorValue helper that resolves delegateMap or defaults to model name, and apply it in BaseOperationHandler for single-entity and bulk create operations.
E2E test schema and assertions
tests/e2e/orm/schemas/delegate/schema.zmodel, tests/e2e/orm/schemas/delegate/schema.ts, tests/e2e/orm/schemas/delegate/typecheck.ts, tests/e2e/orm/client-api/delegate.test.ts
Update test schema to use enum types for discriminator fields, add @@delegateMap attributes mapping to enum constants, regenerate schema metadata, and update all test assertions and type narrowing checks to expect enum-based discriminator literals instead of model names.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 A mapping so divine, where enums dance in polymorphic lines,
Custom discriminators shine, where delegates now align!
No more model names decree, just values we define with glee,
Delegate maps make it free!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'implement delegateMap attribute' clearly and specifically describes the main change in the PR, which is implementing a new delegateMap attribute for custom delegate discriminator mapping.
Linked Issues check ✅ Passed The PR fully implements issue #2558's requirements: supports custom discriminator values via @@delegateMap attribute (option 3), handles both enum and string values, provides validation for completeness and uniqueness, and maintains fallback to model name behavior.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the delegateMap feature: stdlib definition, validation logic, type system updates, ORM integration, and comprehensive test coverage. No unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit 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.

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

🤖 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/language/res/stdlib.zmodel`:
- Line 649: The @@delegateMap attribute must be marked single-use to prevent
multiple mappings being accepted but only one being resolved; update the
attribute declaration for @@delegateMap to include the single-use modifier
(e.g., add @@@once or the equivalent single-use flag) so the parser enforces
only one @@delegateMap per model and rejects additional entries, ensuring unique
mapping resolution for the model.

In `@packages/orm/src/client/query-utils.ts`:
- Around line 384-387: The fallback for the discriminator currently returns the
raw input parameter `model`, which can preserve incorrect casing; update
getDelegateDiscriminatorValue to use the canonical model name from the resolved
model definition instead of the input string when delegateMap is undefined: call
requireModel(schema, model) as already done, then return modelDef.delegateMap ??
modelDef.name (or the canonical name field on the modelDef) so the schema's
canonical casing is used; keep delegateMap precedence and only use the modelDef
canonical name as the default.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f8f43335-fb2a-4f23-876c-e4a882be0f16

📥 Commits

Reviewing files that changed from the base of the PR and between ed01275 and 7c07e2f.

📒 Files selected for processing (12)
  • packages/language/res/stdlib.zmodel
  • packages/language/src/validators/datamodel-validator.ts
  • packages/language/test/delegate.test.ts
  • packages/orm/src/client/crud-types.ts
  • packages/orm/src/client/crud/operations/base.ts
  • packages/orm/src/client/query-utils.ts
  • packages/schema/src/schema.ts
  • packages/sdk/src/ts-schema-generator.ts
  • tests/e2e/orm/client-api/delegate.test.ts
  • tests/e2e/orm/schemas/delegate/schema.ts
  • tests/e2e/orm/schemas/delegate/schema.zmodel
  • tests/e2e/orm/schemas/delegate/typecheck.ts

*
* @param value: A string literal or enum member used as the discriminator.
*/
attribute @@delegateMap(_ value: Any)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make @@delegateMap single-use to avoid ambiguous mappings.

Without @@@once, multiple @@delegateMap entries on one model are accepted, while downstream resolution reads only one and silently ignores others.

Proposed fix
-attribute @@delegateMap(_ value: Any)
+attribute @@delegateMap(_ value: Any) @@@once
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
attribute @@delegateMap(_ value: Any)
attribute @@delegateMap(_ value: Any) @@@once
🤖 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/language/res/stdlib.zmodel` at line 649, The @@delegateMap attribute
must be marked single-use to prevent multiple mappings being accepted but only
one being resolved; update the attribute declaration for @@delegateMap to
include the single-use modifier (e.g., add @@@once or the equivalent single-use
flag) so the parser enforces only one @@delegateMap per model and rejects
additional entries, ensuring unique mapping resolution for the model.

Comment on lines +384 to +387
export function getDelegateDiscriminatorValue(schema: SchemaDef, model: string) {
const modelDef = requireModel(schema, model);
return modelDef.delegateMap ?? model;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use canonical model name for default discriminator fallback.

Line 386 falls back to the input model string. Because lookup is case-insensitive, passing "video" can produce "video" instead of schema model name "Video" when delegateMap is not set.

Proposed fix
 export function getDelegateDiscriminatorValue(schema: SchemaDef, model: string) {
     const modelDef = requireModel(schema, model);
-    return modelDef.delegateMap ?? model;
+    return modelDef.delegateMap ?? modelDef.name;
 }
🤖 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/orm/src/client/query-utils.ts` around lines 384 - 387, The fallback
for the discriminator currently returns the raw input parameter `model`, which
can preserve incorrect casing; update getDelegateDiscriminatorValue to use the
canonical model name from the resolved model definition instead of the input
string when delegateMap is undefined: call requireModel(schema, model) as
already done, then return modelDef.delegateMap ?? modelDef.name (or the
canonical name field on the modelDef) so the schema's canonical casing is used;
keep delegateMap precedence and only use the modelDef canonical name as the
default.

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.

[Feature Request] Support Custom Mapping for @@delegate Type Attribute

1 participant