Skip to content

Fix inheritance of an explicit bindable:true constraint on id/version declared in an abstract @DirtyCheck base#15796

Open
jdaugherty wants to merge 2 commits into
7.0.xfrom
id-binding-isuee
Open

Fix inheritance of an explicit bindable:true constraint on id/version declared in an abstract @DirtyCheck base#15796
jdaugherty wants to merge 2 commits into
7.0.xfrom
id-binding-isuee

Conversation

@jdaugherty

Copy link
Copy Markdown
Contributor

Description

When an abstract base class — for example a @DirtyCheck class in src/main/groovy — declares an
explicit bindable: true constraint on one of the default-excluded special properties
(id, version, dateCreated, lastUpdated), a concrete domain subclass that does not redeclare
the constraint failed to inherit it. The property was silently excluded from data binding even though
the parent explicitly opted it in.

This is a regression introduced by the fix for #15681 (PR #15699).

Root cause

PR #15699 added a filter in DefaultASTDatabindingHelper.getPropertyNamesToIncludeInWhiteList that
strips the default-excluded special properties from the inherited parent whitelist for any domain
class. That correctly stops GORM-injected id/version from leaking out of a non-domain @DirtyCheck
base, but the filter was unconditional: it could not distinguish a special property that landed in the
parent whitelist by default from one the parent made bindable via an explicit bindable: true
constraint. As a result the explicit constraint was discarded.

Fix

The helper now tracks which excluded-by-default special properties were made bindable via an explicit
constraint, propagates that set down the inheritance hierarchy, and only skips an inherited special
property when it was not explicitly opted in:

Tests

  • Unit (DefaultASTDatabindingHelperDomainClassSpecialPropertiesSpec): an explicit bindable: true
    on id declared in a @DirtyCheck abstract base is inherited by the domain subclass (this case fails
    without the fix), and a subclass bindable: false override beats the inherited bindable: true.
  • Functional (grails-test-examples-gorm / DirtyCheckBindingSpec): a new @DirtyCheck base
    (AbstractBindableIdRecord) opting id into binding, an inheriting domain class (BindableIdRecord),
    a controller action, and an end-to-end HTTP assertion that the inherited id is bound while version
    remains unbound.

Fixes #15795

Contributor Checklist

Issue and Scope

  • This PR is linked to an existing issue that has been acknowledged or approved by the project team.
  • This PR addresses the complete scope of the linked issue.
  • This PR contains a single, focused change.
  • This PR targets the correct branch for the type of change:
    • Patch release branches (e.g., 7.0.x): Bug fixes only. No new features or API changes.

Code Quality

  • I have added or updated tests that cover the changes introduced in this PR.
  • I have verified that all existing tests pass by running ./gradlew build --rerun-tasks.
  • My code follows the project's code style guidelines. I have run ./gradlew codeStyle and resolved any violations.
  • This PR does not include mass reformatting, style-only changes, or large-scale refactoring.
  • If generative AI tooling was used in preparing this contribution, a quality model was used to ensure contributions are consistent with the project's quality standards.

Licensing and Attribution

  • All contributed code is provided under the Apache License 2.0, and new source files include the appropriate Apache license header.
  • I have the necessary rights to submit this contribution and confirm it is my own original work.
  • If generative AI tooling was used in preparing this contribution, I have followed the ASF's policy on generative tooling and have properly attributed its use.

Documentation

  • If this PR introduces user-facing changes, I have included or updated the relevant documentation.
  • If this PR adds a new feature, I have updated the What's New section of the Grails Guide.
  • If this PR introduces breaking changes or changes that require user action during an upgrade, I have updated the Upgrade Notes.
  • The PR description clearly explains what was changed and why.

@jamesfredley jamesfredley left a comment

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.

there's no positive multi-level case - a @DirtyCheck grandparent with id bindable:true, a plain abstract class in between, and the domain subclass binding id while still excluding version. Existing tests cover direct-inherited-true, direct-false-override, and multi-level-without-explicit, but not explicit opt-in propagating through an intermediate level. The code handles it correctly; adding the test would just lock that path. Purely optional - not a blocker for merge.

@sbglasius I think you should get final review since the original issue was one you submitted.

@jdaugherty

Copy link
Copy Markdown
Contributor Author

@jamesfredley I've added the test you suggested (and also updated the issue number in comments)

@testlens-app

testlens-app Bot commented Jun 30, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: b38bd68
▶️ Tests: 30589 executed
⚪️ Checks: 33/33 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants