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 '/canvas/api/v0/content/auto-save/canvas_page/{entityId}' 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 aPage, orNULL.- Terminology split:
conflict_idnow denotes the active/unresolved conflict; the newresolved_conflict_idkey (constantAUTO_SAVE_RESOLVED_CONFLICT_KEY) persists a resolved one.ApiContentAutoSaveControllers::patch()accepts aresolved_conflict_idfield (mutually exclusive with status) and maps each outcome to a distinct status:204,404,409,422. - Two new
ErrorCodesEnumcases:NoActiveConflictMatchingConflictId(5) andAutoSaveItemNotFound(6). - OpenAPI Documents the new request argument, the
oneOfmutual-exclusivity constraint, and the new404/409/422responses on the PATCH calls to the endpoint.
Selecting entity version (published vs draft) for preview
(GET '/canvas/api/v0/layout/{entityTypeId}/{entityId}' endpoint)
- New
autoSavedquery argument (optional, defaults toTRUE):TRUEresponds using the entity version in auto-save,FALSEusing the published version. - New
updatedresponse property: revision-creation time for the published entity, or the auto-save entry'supdatedproperty value for the entity version in auto-save. AutoSaveEntitynow carries anupdatedtimestamp;ApiLayoutController::get()and::getLabel()updated to expect theRequestas parameter and fetch title based onautoSavedvalue in the request.- Documents the new
autoSavedquery 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 lintandnpm run lint pass. - Manual: with
canvas_dev_cdenabled, edit a Page, trigger an external change, and confirm the PATCH call to the'/canvas/api/v0/content/auto-save/canvas_page/{entityId}'endpoint returns409/422/404/204as expected; - confirm the
GETcalls to the'/canvas/api/v0/layout/{entityTypeId}/{entityId}'endpoint switches between published and auto-saved version with?autoSaved=0|1and reports updated.
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.