Resolve "Create Route for Delete Translation"

This currently covers only the translation deletion route for the canvas_page entity.

  • Add DELETE /canvas/api/v0/content/canvas_page/{id}/translations/{language} REST endpoint gated behind canvas_dev_translation module.
  • Add ApiContentTranslationControllers::delete() in the canvas module with guards: 400 for default language, 404 for non-existent translation, 403 via entity access check for missing permissions.
  • Create canvas_dev_translation.routing.yml to gate the route behind the feature-flag module. Add content_translation + language as dependencies.
  • Add testDeleteTranslation() functional test covering 204/400/404 cases.

Testing instructions


Prerequisites

  1. A running DDEV site:

    ddev start
    ddev site-install
  2. Add French as a content language:

    • Go to Admin → Configuration → Regional and language → Languages (/admin/config/regional/language)
    • Click Add language, choose French, and save.
  3. Enable translation for the Page content type:

    • Go to Admin → Configuration → Regional and language → Content language and translation (/admin/config/regional/content-language)
    • Expand Page, tick Enable translation, and save.
  4. Create a Canvas page and add a French translation:

    ddev drush php:eval "
      \$page = \Drupal\canvas\Entity\Page::load(1);
      \$fr = \$page->addTranslation('fr');
      \$fr->set('title', 'Page de test');
      \$fr->save();
      echo 'French translation created for page ' . \$page->id() . PHP_EOL;
    "
  5. Note the page ID (used as {id} below).


ddev drush uli
# Open that URL in a browser, then copy the SSESS... cookie from DevTools
COOKIE="SSESS...=..."
PAGE_ID=1

Scenario 1 — Delete an existing non-default translation → 204

The language is specified via the URL prefix (/fr/):

curl -X DELETE \
  "https://canvas-dev.ddev.site/fr/canvas/api/v0/content/canvas_page/${PAGE_ID}/translations" \
  -H "Cookie: ${COOKIE}" \
  -v 2>&1 | grep "< HTTP"

Expected: < HTTP/2 204 with an empty body.

Verify via Drush:

ddev drush php:eval "
  \$p = \Drupal\canvas\Entity\Page::load(${PAGE_ID});
  var_dump(\$p->hasTranslation('fr'), \$p->hasTranslation('en'));
"
## Expected: bool(false) bool(true)

Scenario 2 — Attempt to delete the default language → 400

No language prefix means the request resolves to the default (English) translation:

curl -X DELETE \
  "https://canvas-dev.ddev.site/canvas/api/v0/content/canvas_page/${PAGE_ID}/translations" \
  -H "Cookie: ${COOKIE}" \
  -v 2>&1 | grep "< HTTP"

Expected: < HTTP/2 400 with a JSON body:

{
  "message": "Cannot delete the default translation for canvas_page 1. Use the entity delete endpoint to remove the whole entity."
}

Scenario 3 — Language prefix for a translation that no longer exists → 400

(Run after Scenario 1 so the French translation is gone.)

Drupal's language negotiation falls back to the default translation when the requested language has no translation, so the endpoint returns 400 (default translation guard):

curl -X DELETE \
  "https://canvas-dev.ddev.site/fr/canvas/api/v0/content/canvas_page/${PAGE_ID}/translations" \
  -H "Cookie: ${COOKIE}" \
  -v 2>&1 | grep "< HTTP"

Expected: < HTTP/2 400


Scenario 4 — Unauthenticated request → 401

curl -X DELETE \
  "https://canvas-dev.ddev.site/fr/canvas/api/v0/content/canvas_page/${PAGE_ID}/translations" \
  -v 2>&1 | grep "< HTTP"

Expected: < HTTP/2 401


Scenario 5 — User without delete permission → 403

  1. Create a user without the Administer Canvas pages permission and log in.
  2. Copy that session cookie, then:
curl -X DELETE \
  "https://canvas-dev.ddev.site/fr/canvas/api/v0/content/canvas_page/${PAGE_ID}/translations" \
  -H "Cookie: ${LIMITED_COOKIE}" \
  -v 2>&1 | grep "< HTTP"

Expected: < HTTP/2 403


Closes #3591588

Edited by Chandan Singh

Merge request reports

Loading