refactor: migrate Xblock callers to standardized v1 REST endpoint (FC-0118)#3143
refactor: migrate Xblock callers to standardized v1 REST endpoint (FC-0118)#3143taimoor-ahmed-1 wants to merge 5 commits into
Conversation
…-0118) FC-0118 (ADR 0028/0037): point Studio's xblock CRUD callers at the standardized `/api/contentstore/v1/xblock/` REST endpoint instead of the legacy `/xblock/` Studio route. The v1 XblockViewSet uses strict REST routing, so: - create/duplicate/paste stay POST on the collection URL; - retrieve stays GET, delete stays DELETE (now on the detail URL, trailing slash); - legacy POST-as-update becomes PATCH (partial_update) on the detail URL; - the "move" op (previously PATCH on the collection) becomes PATCH on the source block's detail URL — `update_xblock_response` still routes by `move_source_locator`; - custom-pages "add page" (previously PUT on the collection) becomes POST (create). Trailing slashes are required by the DRF DefaultRouter that serves the v1 viewset. Fields the v1 strict serializer does not accept and the backend does not read are dropped from request bodies: `type` (create; redundant with `category`) and `courseKey` (editor save). `library_content_key` is kept (library import) and is accepted by the v1 serializer via openedx/openedx-platform#38908. The `/xblock/outline/...`, `/xblock/{id}/studio_view`, and `/xblock/{id}/handler/...` sub-routes are NOT part of the v1 REST viewset and remain on the legacy route (editors `block()` helper kept; new `blockV1()` helper added for CRUD). Source-only commit; test mocks updated in follow-up commits. Refs openedx#3127 Depends-on openedx/openedx-platform#38908 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…trailing slash) Refs openedx#3127 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detail-update mocks POST->PATCH; history assertions for updates read history.patch; create expected bodies drop the dropped `type` key. Refs openedx#3127 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detail-update mocks POST->PATCH; move mocks PATCH the source detail URL; create body-matchers/expected bodies drop the dropped `type` key; update history assertions read history.patch. Refs openedx#3127 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fetchBlockById/saveBlock use blockV1; saveBlock asserts PATCH; normalizeContent tests drop learningContextId/courseKey; add blockV1 url test; PdfEditor save mock targets the v1 detail PATCH URL. Refs openedx#3127 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the pull request, @taimoor-ahmed-1! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. 🔘 Update the status of your PRYour PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate. Where can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
⛔ Draft — depends on a backend PR (do not merge yet)
Item #1 (Xblock) of the FC-0118 Studio-API caller migration tracked in
#3127.
Blocked by / merge after: openedx/openedx-platform#38908 — the v1
XblockViewSetvalidates writes with a strict serializer that 400s on unlisted fields, and the
real Studio create payload sends
library_content_key(v2-library import). Thatbackend PR declares the field on the serializer. This PR must not merge until
#38908 is merged and deployed, or block creation/import will 400.
Independent of the Course Home (#3140) / Home Courses (#3139) drafts.
Description
Migrates Studio's xblock CRUD callers from the legacy
/xblock/Studio route tothe standardized
/api/contentstore/v1/xblock/REST endpoint (FC-0118,ADR 0028/0037). The v1
XblockViewSetuses strict REST routing, so the verbs andURLs change per operation:
POST /xblock/POST /api/contentstore/v1/xblock/(unchanged verb)GET /xblock/{id}GET …/xblock/{id}/POST/xblock/{id}PATCH…/xblock/{id}/PATCH /xblock/(collection)PATCH …/xblock/{sourceId}/(detail)DELETE /xblock/{id}DELETE …/xblock/{id}/PUT /xblock/POST …/xblock/(create)Trailing slashes are required by the DRF
DefaultRouter. For "move",update_xblock_responsestill routes offmove_source_locatorin the body — theURL key only scopes the permission check, so we PATCH the source block's detail URL.
Request-body changes (strict serializer)
The v1 serializer rejects unlisted top-level fields. Two fields the legacy route
ignored are dropped from request bodies:
typeon create — redundant withcategory(category: category || type), whichthe backend actually reads.
courseKeyon the editor save — not read by the update handler.library_content_key(functional, library import) is kept and accepted by thev1 serializer via #38908.
Left on the legacy route (NOT part of the v1 REST viewset)
These are handler/view sub-routes the v1 viewset does not serve, so they stay on
/xblock/…:/xblock/outline/{id}(course-outlinegetXBlockApiUrl, content-tags-drawer)/xblock/{id}/studio_view(editorsblockStudioView)/xblock/{id}/handler/…(editors transcripts, PdfEditor handler)The editors
block()URL helper is kept for those sub-routes; a newblockV1()helper serves the CRUD callers (
fetchBlockById,saveBlock,blockAncestor).Files
Source
src/course-unit/data/api.ts— helpers → v1;editUnitDisplayNamePOST→PATCH;patchUnitItem(move) → PATCH source detail URL.src/course-outline/data/api.ts— helpers → v1 (split out the legacy/xblock/outline/builder); update calls POST→PATCH; droptypefrom create.src/custom-pages/data/api.ts— delete/update → v1 detail;addCustomPagePUT→POST.src/course-updates/data/api.js— handouts helper → v1 detail (GET + PUT).src/editors/data/services/cms/{urls,api,utils}.ts— addblockV1()+patch()helper;fetchBlockById/saveBlock→blockV1;saveBlockPOST→PATCH; dropcourseKeyfromnormalizeContent.Tests — mocks/verbs/assertions updated to match (course-unit, course-outline, custom-pages, editors).
Testing
npx jest src/course-unit→ 210 passnpx jest src/course-outline→ 524 pass (1 pre-existingPageAlertsflake, unrelated: deprecatedtoBeCalledmatcher, fails onmastertoo)npx jest src/custom-pages→ pass (1 pre-existing date flake inUpdateForm, fails onmaster)npx jest src/editors/data/services/cms→ 75 pass;PdfEditor→ 4 passdprint check+oxlint --deny-warningson changed files → cleanRemaining failures in a full
src/editorsrun are pre-existing (identical set onmaster:GalleryCarddate,ImageUploadModal,SocialShareWidget,VideoEditorHandout) plus known parallel-run flakes.