feat(Conflict detection): #3591601 "Conflict resolution via API call and layout endpoint support for published entities"
What this branch does
TL;DR: Modifies the ApiContentAutoSaveControllers::patch() to allow updating auto-save item conflict as resolved; Adds optional argument to the ApiLayoutController::get() that allows fetching layout of a published entity; all new functionality is hidden behind canvas_dev_cd
Long version:
This MR adds "Ability to resolve conflict by keeping changes in the auto-save" and an "ability to load the visual preview of both auto-save and published version of the entity with conflict" capability described in the %Review of changes and Conflict resolution milestone.
It is BE implementation of #3591601 (closed) and BE dependency of !1183 (merged).
To keep the scope manageable, changes are limited to Page (canvas_page) entities. Effort is made to leave path open for other entity types.
All new behaviour is gated behind the canvas_dev_cd feature-flag module, so pre-existing functionality is unaffected.
Conflict resolution (PATCH auto-save endpoint)
- New enum ConflictResolutionOutcome (Resolved, NoAutoSaveItem, NoActiveConflict, ConflictMismatch).
- AutoSaveManager::resolveConflict() reads the auto-save entry, compares the requested conflict id against the active one, and persists the resolution in a single pass, returning the typed outcome.
- AutoSaveManager::getUnresolvedConflictForEntity() returns the active unresolved conflict id for a Page, or NULL.
- Terminology split: conflict_id now denotes the active/unresolved conflict; the new resolved_conflict_id key (constant AUTO_SAVE_RESOLVED_CONFLICT_KEY) persists a resolved one. ApiContentAutoSaveControllers::patch() accepts a resolved_conflict_id field (mutually exclusive with status) and maps each outcome to a distinct status: 204, 404, 409, 422.
- Two new ErrorCodesEnum cases: NoActiveConflictMatchingConflictId (5) and AutoSaveItemNotFound (6).
Published vs. auto-save preview (GET layout endpoint)
- New autoSaved query argument (optional, default TRUE): TRUE serves the auto-save version, FALSE the published version.
- New updated response property: revision-creation time for the published view, or the auto-save entry's updated for the draft view.
- AutoSaveEntity now carries an updated timestamp; ApiLayoutController::get() and ::getLabel() accept the Request and honor autoSaved.
- OpenAPI Documents the new request field, the oneOf mutual-exclusivity constraint, and the 404/409/422 responses on the PATCH endpoint.
- Documents the autoSaved query argument and the updated property on the GET layout endpoint.
Testing instructions
- composer run phpunit -- tests/src/Kernel/AutoSaveManagerTest.php --filter testConflictResolutionMethods
- composer run phpunit -- tests/src/Kernel/ApiLayoutControllerGetTest.php --filter testGetWithAutoSavedQueryArgument composer run phpunit -- tests/src/Kernel/ApiAutoSaveControllerTest.php
- composer run phpunit -- tests/src/Functional/CanvasContentEntityHttpApiTest.php --filter testPatchConflictResolution
- composer run lint and npm run lint pass.
- Manual: with
canvas_dev_cdenabled, edit a Page, trigger an external change, and confirm the PATCH endpoint returns 409/422/404/204 as expected; confirm theGETlayout endpoint switches between published and auto-save with?autoSaved=0|1and reports updated.
Suggested reviewers Based on recent By: lines on the modified files: wim leers, penyaskito, tedbow (consistent contributors to AutoSaveManager and the auto-save controllers).
AI usage disclosure Per Drupal.org's policy on AI: AI assistance was used for refactoring, code documentation, and drafting this summary. A human contributor reviewed and validated the changes.