Skip to content

[API] Containers #39063

Open

Description

@FuaadZam

Functional Area: Containers

Endpoints under this functional area:

Module API Version Resource / Endpoint HTTP Method Description
Content Management v1 /contentstore/v1/container/{usage_key_string}/children GET Retrieves the children of a container/vertical block in a course. Returns a JSON object with children array (including name, block_id, block_type, user_partition_info, upstream_link, actions, validation_messages, render_error), is_published, can_paste_component, display_name, and upstream_ready_to_sync_children_info. Useful for AI or external tools analyzing course structure, component readiness, or automating content synchronization.

Activity

  1. added theissue type on Sep 2, 2026
  2. Faraz32123 commented on Oct 27, 2026

    @Faraz32123
    Contributor

    Standardization PR open: #39317

    PR: #39317 — feat: serve container children from authoring v2
    State: open

    What this does

    Standardizes the container children read from the [API] Containers tracking issue (#39063, part of the FC-0118 umbrella #38137): GET /api/contentstore/v1/container/{usage_key_string}/children is superseded by GET /api/authoring/v2/containers/{usage_key}/children/. This is the second half of the container pair: the container itself is #39274 (/api/authoring/v2/containers/{usage_key}/), and its PR body assigns this address to this PR.

    ADRs applied

    11 of 16 decisions applied on this pass; every one left open carries its reason.

    • 0025 serializers
    • 0026 permissions
    • 0027 schema / docs
    • 0028 viewsets
    • 0030 GET idempotence
    • 0032 pagination
    • 0034 authentication
    • 0036 nested JSON
    • 0037 versioning
    • 0038 URL structure
    • OEP-69 conventions
    • 0029 errors — partial: every error the view and CanViewCourse produce is the envelope; a malformed or i4x:// key fails at URL resolution and gets the service's HTML 404, and 405/406 are typed internal because edx-drf-extensions 10.9.0 omits them (both pinned by tests that flip when fixed)
    • 0031 merged endpoints — n/a: one read operation; the only sibling is the frozen 301 alias, and container_handler is a different resource
    • 0033 filtering — n/a: no filter and no sort; the order is the order the container stores; view selects a representation
    • 0035 MFE config — n/a: course content, not site or front-end configuration
    • OEP-66 queryset scoping — the mechanism is excluded: no ORM queryset to wrap; the layering is below

    The evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.

    Backward compatibility

    • Nothing changes for any v1 caller. v1/ is not edited; every v1 address, URL name, body and status stays, including the 301 alias container/vertical/{key}/children.
    • Versions gate (check_old_versions_untouched.sh --base origin/pr/39274): PASS. Two allowed edits (listed below), four added files.
    • URL gate (url_snapshot.py check): 2166 routes at head, 2165 at base, no address and no URL name lost, one new API route checked: PASS.
    • Schema diff: 0 BREAKING. Added: operation GET /api/authoring/v2/containers/{usage_key}/children/, operationId v2_containers_children_list, 8 components (CourseContainerChild, CourseContainerChildActions, CourseContainerChildFull, CourseContainerChildMessage, CourseContainerChildRead, CourseContainerChildUpstreamChild, CourseContainerChildUpstreamLink, PaginatedCourseContainerChildReadList). Zero operationIds and zero components.schemas names removed. The v1 operation is newly marked deprecated: true. The two WARN lines are drf-spectacular failing to resolve the platform's default authenticator classes, which every default-authentication view in this schema triggers.
    • Parity (ContainerChildrenParityTest, ContainerChildrenLibraryLinkedParityTest): every child of a section, subsection, unit, split test and group, with a library link to a block and to a container, a broken link, restricted groups, a validation error, tagging on and off, and a block that fails to render, is compared leaf by leaf with the v1 body. INTENTIONAL_DIFFERENCES, verbatim from the test:
      • is_published|can_paste_component|display_name|upstream_ready_to_sync_children_info(\[\d+\].*)? — v1 also describes the container itself; the page lists only its children
      • count|next|previous|num_pages|current_page|start — the page envelope
      • results\[\d+\]\.render_error — the cause of a render failure is logged and a fixed message returned

    Gates

    Gate report — Containers (#39063)

    Run 2026-10-27, base origin/pr/39274 (3291339)

    Gates run individually (not through run_gates.sh) due to worktree environment

    versions gate: check_old_versions_untouched.sh

    RESULT: PASS (2 allowed, 4 added)
    --base origin/pr/39274
    --allow cms/djangoapps/contentstore/rest_api/v2/authoring_urls.py
    --allow cms/djangoapps/contentstore/rest_api/v2/views/tests/test_containers.py
    --protect cms/djangoapps/contentstore/rest_api/urls.py
    --protect cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py
    --protect cms/djangoapps/contentstore/utils.py
    --protect cms/djangoapps/contentstore/helpers.py
    --protect cms/djangoapps/contentstore/views/preview.py
    --protect cms/lib/xblock/upstream_sync.py
    --protect openedx/core/lib/api/view_utils.py
    No v0/, v1/, v3/, v4/ file changed. No protected file changed.
    Allowed: authoring_urls.py, test_containers.py
    Added: container_children_service.py, serializers/container_children.py, views/container_children.py, views/tests/test_container_children.py

    hygiene gate: check_code_hygiene.py

    RESULT: PASS (0 FAIL, 1 WARN)
    WARN H3: CourseContainerChildPagination pagination subclass (kept: adds the seven-key schema; documented in docstring)

    schema gate: schema_diff.py compare

    RESULT: PASS (0 BREAKING, 2 WARN, 4 INFO)
    Added: 1 operation (v2_containers_children_list), 8 components
    v1 operation v1_container_children_retrieve: newly deprecated: true
    0 operationIds removed, 0 component names removed
    WARN: "could not resolve authenticator" for default JWT and session classes (every default-authentication view)
    check-present --prefix /api/authoring/v2/containers/ -> PASS

    url gate: url_snapshot.py check

    RESULT: PASS (0 FAIL, 0 WARN)
    head routes: 2166, base routes: 2165, new api routes checked: 1
    All v1 addresses still resolve. New route: /api/authoring/v2/containers/{usage_key}/children/

    old-tests gate: pytest v1/views/tests/test_vertical_block.py

    RESULT: PASS
    All classes (ContainerVerticalViewTest, ContainerHandlerViewTest, ContainerHandlerViewAuthzTest) green.
    No v1 test edited.

    tests gate: pytest v2/views/tests/test_container_children.py

    RESULT: PASS (144 cases)
    86 test methods, parametrized to 144 cases.
    Also: test_spectacular.py (25 cases) PASS.
    Full affected set: 606 passed, 3 failed (pre-existing in #39274's test_containers.py, fail identically at base).

    linting

    ruff check: All checks passed
    pylint: no findings (except pytest-plugin F6401, environment)

    NOTE: gates run against origin/pr/39274 (stack base), not origin/master

    Re-run against origin/master after rebase (plan step 7 / 12.4)

    Deprecations

    GET /api/contentstore/v1/container/{usage_key_string}/children is superseded by GET /api/authoring/v2/containers/{usage_key}/children/?view=full and marked deprecated: true in the published schema, by one exact SUPERSEDED_PATHS entry. The superseded view is unchanged: no response header, no deprecation notes in code. The 301 alias container/vertical/{usage_key_string}/children is not a DRF view, has no schema operation, and is named on the DEPR entry.

    Tracked on the FC-0118 umbrella DEPR ticket, #39128, which removes them one named release after the release this ships in. This area's entry is posted there once the PR is open.

    Gap named, per the deprecation decision for this area: four things leave the response and two of them have no v2 home yet: is_published and the container's own upstream_ready_to_sync_children_info. The Authoring MFE's compare-containers widget reads the second. #39274 is asked to carry both on the container detail before it merges; until then the window could end before one exists, and the DEPR entry says so.

    Migration notes for callers: call the v2 address with the trailing slash and ?view=full&page_size=100 for the Unit page; read results and follow next; errors use the envelope; Bearer is refused; a leaf or course-root key is 404; the caller must be able to view the course; render_error is a fixed message.

    Follow-ups

    • Carry is_published and the container's own upstream readiness (ready_to_sync_children) on the container detail (feat: serve course containers from authoring v2 #39274), so the compare-containers widget has a v2 source; recorded in the DEPR entry.
    • A catch-all under containers/ so keys that fail to parse answer with the envelope instead of the HTML 404 (both container routes together).
    • The untyped user_partition_info and user_partitions objects are candidates for a typed shape in a next version; v1's declared shapes are kept as they are.
    • The UpstreamLink fields upstream_name, downstream_key, upstream_link and is_ready_to_sync_individually stay dropped, as in v1.
    • CourseContainerChildPagination is deleted for DefaultPagination once edx-drf-extensions documents all seven keys.
    • docs/cms-openapi.yaml is regenerated by the schema workflow, not edited here; the SDK gains a method for the new operation.
    • The end-to-end suite's containerChildrenPath calls the 301 alias; it moves to the v2 address in a separate change.

    🤖 Generated with Claude Code

    Review on the PR, please — this issue stays open until it merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions