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 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).
  • OpenAPI Documents the new request argument, the oneOf mutual-exclusivity constraint, and the new 404/409/422 responses on the PATCH calls to the endpoint.

Selecting entity version (published vs draft) for preview

(GET '/canvas/api/v0/layout/{entityTypeId}/{entityId}' endpoint)

  • New autoSaved query argument (optional, defaults to TRUE): TRUE responds using the entity version in auto-save, FALSE using the published version.
  • New updated response property: revision-creation time for the published entity, or the auto-save entry's updated property value for the entity version in auto-save.
  • AutoSaveEntity now carries an updated timestamp;
  • ApiLayoutController::get() and ::getLabel() updated to expect the Request as parameter and fetch title based on autoSaved value in the request.
  • Documents the new 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_cd enabled, edit a Page, trigger an external change, and confirm the PATCH call to the '/canvas/api/v0/content/auto-save/canvas_page/{entityId}' endpoint returns 409/422/404/204 as expected;
  • confirm the GET calls to the '/canvas/api/v0/layout/{entityTypeId}/{entityId}' endpoint switches between published and auto-saved version with ?autoSaved=0|1 and 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.

Edited by Feliksas Mazeikis

Merge request reports

Loading