Self-heal stored default-relative-url $id on read, and clean it up on update
Closes #3591904
What this MR does
Data written before Canvas 1.5.1 stored a default-relative-url prop source's resolved JSON Schema including the $id keys the schema resolver emits. Since 1.5.1 (#3591629 (closed)) PropShape::normalize() strips id/$id, so DefaultRelativeUrlPropSource::parse() re-normalizes the stored schema, finds it differs, and throws — a 500 from the layout endpoint, and a fatal in any update or health path that reads it. There was no upgrade path to migrate the stored data.
Three commits, one concern each:
- Fix the read.
parse()now strips the resolver-injected$idbefore its strict comparison, so old data loads again. It still rejects genuinely extraneous schema keys. - Clean the stored data.
0027post-update hooks re-normalize content component trees (every revision and translation), the four config types that hold a component tree (Pattern, Page Region, Content Template, field defaults), and auto-save drafts. Config cleanup also rides onpreSave, so any later save fixes it too, and the read-onlyneedsDefaultRelativeUrlSchemaNormalization()predicate is picked up automatically by the site health report. - Test. An update-path test over all of the above.
Testing steps
-
ddev xb-phpunit tests/src/Functional/Update/DefaultRelativeUrlSchemaIdsUpdateTest.php— passes (page, all four config types, and both auto-save shapes go in with a stale$idand come out clean). -
ddev xb-phpunit tests/src/Kernel/PropSource/DefaultRelativeUrlPropSourceTest.php— passes (parse()accepts old$iddata, still rejects real extras). -
composer run lint— clean (phpcs + phpstan).
Note on the test fixture dump
drupal-11.3.10-with-canvas-1.5.0.bare.php.gz is a bare Canvas 1.5.0 install (updates done through 0019). That baseline matters: 0004/0016/0017 have already run, so no earlier update re-saves the seeded data and cleans it up as a side effect — which lets the 0027 updates be the only thing that fixes each case (the config path can't be isolated on the older 1.0.0-alpha1/1.2.0 dumps).
It was generated the standard base + test_site way, with one caveat: test_site's demo content references a block component (system_menu_block.admin) that Canvas 1.5.0 fails to generate on core 11.3 — its BlockComponentDiscovery::checkRequirements() passes a boolean label_display, which is invalid under core 11.3's string-enum schema. That is already fixed in HEAD ('0'), so HEAD's one-line fix was applied to the 1.5.0 checkout for generation only.
AI usage
Developed with AI assistance (Claude), verified against the installed codebase and the test suite, per Drupal.org's policy on the use of AI when contributing.