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_cd enabled, edit a Page, trigger an external change, and confirm the PATCH endpoint returns 409/422/404/204 as expected; confirm the GET layout endpoint switches between published and auto-save with ?autoSaved=0|1 and 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.

Edited by Feliksas Mazeikis

Merge request reports

Loading