feat(Conflict detection): #3591668 "Text-based Page entity representation via Canvas API"

TL;DR: Adds a new endpoint and related infra that serves Page entities in textual representation using YAML format, using only fields that can be stored in auto-save item.

For people with attention span or AI summary tools:

🤖 Summary

Adds a GET /canvas/api/v0/content/canvas_page/values-as-text/{canvas_page} endpoint that returns a Page entity's storable field values encoded as YAML text. The primary use case is client-side comparison of published and draft Page entities using text-based representation of values that can be stored in an auto-save item. The endpoint accepts an ?autoSaved=true|false query parameter (default true). When true, it reconstructs the entity from the active auto-save item and returns its values alongside the auto-save timestamp. When false, it returns the values of the entity as persisted in storage alongside its revision creation timestamp. This is the same approach that was introduced to layout endpoint in !1194 (merged) .

The returned values are produced by AutoSaveManager::toStorableArray() - the same normalization that is used when writing an auto-save item to storage. This way the YAML output is directly comparable to stored auto-save data, without showing values user has no way to change in Canvas UI. Fields the current user has no permissions to view are omitted from the response. Component-tree fields are placed at the end of the YAML output for better UX. AutoSaveManager::toStorableArray() is promoted from private to public to allow the controller to reuse it without duplication.

Adds new canvas_test_field_view_access helper module for testing access to individual entity type fields using view operation. Used in this MR to test that "fields the user has no permission to access are excluded from the output", but can be used for other controller routes that check individual field permissions as well.

Testing instructions

Testing requires enabling canvas_dev_cd feature flag module.

  1. Pick existing or create new Page entity and publish it, take write down its title and what its "published at" date / timestamp.
  2. Open a Canvas Page in the editor, change it's title to Updated page without publishing and:
    • confirm that draft (unpublished auto-save item) is created and is being tracked in Canvas
    • take note of timestamp (https://unix.click/ or some other method)
  3. Make GET call to /canvas/api/v0/content/canvas_page/values-as-text/{id} (default ?autoSaved=true) and confirm:
    • response contains entity property, that is a encoded YAML blob
    • entity property contains the draft title - Updated page
    • updated timestamp is close to the time that Page entity was updated in Canvas in step 2.
  4. Make GET call to /canvas/api/v0/content/canvas_page/values-as-text/{id}?autoSaved=false and confirm:
    • response contains the published title written down in step 1
    • updated timestamp matching the time Page entity was published at and taken in step 1.
  5. Make GET call to /canvas/api/v0/content/canvas_page/values-as-text/{id} with no active auto-save and ?autoSaved=true and confirm the result is identical to step 3:
    • response contains the draft title - Updated page
    • updated timestamp is close to the time that Page entity was updated in Canvas 2.
  6. Publish changes to Page entity, take note of timestamp
  7. Make GET call to /canvas/api/v0/content/canvas_page/values-as-text/{id}?autoSaved=false and confirm:
    • response contains the title set in step 2 - Updated page
    • updated timestamp is close to the timestamp taken in step 6.
  8. Test GET calls to /canvas/api/v0/content/canvas_page/values-as-text/{id} edge cases:
    • Calling endpoint using {id} of a Page that doesn't exist results in HTTP 404 with []
    • Calling endpoint using {id} of page that is published, bus has no draft / auto-save item, without autoSaved argument or autoSaved=true results in HTTP 404 with response body containing errors property with single item, which contains error message in details and auto-save item identifier
    • Calling endpoint using {id} of page that is published, bus has no draft / auto-save item, with autoSaved=false results in successful fetching of entity
    • Calling endpoint using {id} of page that is published, but contains a field that user has no permission to view, results in that field being absent from the entity property in the response
  9. Disable canvas_dev_cd feature flag module
  10. Make GET call to /canvas/api/v0/content/canvas_page/values-as-text/{id}, confirm:
  • the response is HTTP 404
  • response body contains errors with single item that contains error message "Feature flag module canvas_dev_cd is disabled." in details property and auto-save item identifier
  • the response is consistent with any permutation of autoSaved

AI use disclosure

This MR description was drafted with AI assistance (GitHub Copilot) per Drupal.org's policy on AI contributions. It was then reviewed and made worse extensively rewritten by a human anyway 🫠. Parts of code in this MR were written with AI assistance (Github Copilot with Sonnet 4.6 and Opus 4.8). AI was mostly used when writing PHPUnit tests, trying to write clear documentation, early self-reviews and during some of the debugging and exploration of the functionality.

Closes #3591668

Edited by Feliksas Mazeikis

Merge request reports

Loading