feat: add REST API convention building blocks for Open edX endpoints - #573
Open
Abdul-Muqadim-Arbisoft wants to merge 7 commits into
Open
feat: add REST API convention building blocks for Open edX endpoints#573Abdul-Muqadim-Arbisoft wants to merge 7 commits into
Abdul-Muqadim-Arbisoft wants to merge 7 commits into
Conversation
Move the reusable core of edx-platform's standardized error envelope (openedx/core/lib/api/exceptions.py + mixins.py) into the library, per edx-platform ADR 0039 (extract the REST API reference implementation): * errors.py — standardized_error_exception_handler, the Conflict (409) exception, the envelope formatters (build_error_envelope, flatten_detail, normalize_validation_errors), the error-type URI catalog (ERROR_TYPE_BASE_URI, error_type_uri, classify_error) and its register_error_type extension helper, and ErrorResponseSerializer for OpenAPI response declarations. * mixins.py — StandardizedErrorMixin, the per-view opt-in for the handler. The handler's platform coupling is inverted: instead of importing ignored_error_exception_handler from openedx.core.lib.request_utils, it delegates to a base handler resolved from the new EDX_DRF_EXTENSIONS['STANDARDIZED_ERROR_BASE_HANDLER'] setting (a dotted path or callable, defaulting to DRF's exception_handler), so edx-platform keeps its error-monitoring behavior via configuration. A guard test enforces that nothing in this package imports from edx-platform (ADR 0039 rule 9).
Generalize the response-shaping patterns the FC-0118 pilot APIs each hand-wrote (apply_field_selection in contentstore v3, the ?view=minimal helpers in enrollments v2) into a shared shaping module: * project(data, fields) — keep only the named top-level keys of a serialized object; accepts an iterable or the raw ?fields= CSV value. * MinimalViewMixin — the ?view=minimal response preset. Views set minimal_fields for a plain projection or override to_minimal_representation for shapes a projection cannot express (such as collapsing an embedded sub-object to its id), and apply it with shape_minimal() just before building the response. An unconfigured view raises ImproperlyConfigured rather than silently returning the full payload.
ADR 0032 requires the standard pagination envelope even on endpoints whose data does not flow through DRF's generic list machinery — plain ViewSet actions and views that assemble their items by hand (the enrollments v2 list action being the reference case in edx-platform). * paginate_manually(request, items, serialize, ...) — the shared spelling of the paginate-serialize-respond sequence those endpoints hand-write. * IterablePaginationMixin — view-level wrapper; paginate_iterable() defaults to the view's get_serializer and DefaultPagination (the ADR 0032 seven-field envelope) via pagination_class.
DRF routers default lookup_value_regex to [^/.]+, which cannot match Open edX opaque keys, so every router-registered ViewSet keyed by a course or usage key hand-writes its own pattern (contentstore v3 course-details and grading, the v1 xblock view, cms course_runs). Provide the shared spellings — COURSE_KEY_LOOKUP_REGEX and USAGE_KEY_LOOKUP_REGEX — matching the patterns openedx/core/constants.py has used in URL routes, with capturing groups made non-capturing since router regexes are embedded inside the named lookup group. The patterns delimit the key within the URL path; real validation stays with opaque_keys in the view.
Give consumer test suites a single assertion that a response carries a well-formed ADR 0029 error envelope, replacing the per-field loops the FC-0118 pilot tests repeat (e.g. enrollments v2 test_envelope.py): required fields present, body status matching the response status, the type URI belonging to the https://docs.openedx.org/errors/ catalog, instance matching the request path when available, and no legacy DeveloperErrorViewMixin fields leaking through.
Abdul-Muqadim-Arbisoft
requested review from
Faraz32123,
bradenmacdonald,
feanil and
taimoor-ahmed-1
August 28, 2026 10:43
Add url_converters.py — CourseKeyConverter and UsageKeyConverter, the shared Django path converters edx-platform ADR 0038 (rule 9) requires for conforming API routes, plus register_url_converters() to register them once per service as <course_key:...> / <usage_key:...>. Views receive parsed keys; malformed keys and the deprecated Org/Course/Run and i4x:// forms become routing-level 404s. Endpoints that must keep serving Old Mongo content keep the permissive routers.py lookup regexes instead. Extracted here per the ADR's 'Code examples' section (the converters depend only on edx-opaque-keys, already a base dependency, so the 10.8.0 'no new dependencies' claim holds) and the ADR review request (openedx-platform#39003, r3833688640). Behaviour verified identical to the edx-platform copy it replaces, on Django 4.2 and 5.2. Tests pin what the ADR records as verified: resolve()/reverse() round-trips (parsed and string forms), 404 for malformed and deprecated keys, NoReverseMatch for a deprecated key, and two converters in one route. The CCX case skips where the optional ccx-keys package is absent, since the converter accepts whichever key types the host installs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the reusable core of the FC-0118 REST API conventions work to this library, as discussed in the OEP-69 review (openedx/openedx-proposals#805 (comment)). Until now the reference implementation was split: pagination and JWT auth live here, but the error envelope, response shaping, and test helpers live inside edx-platform where plugins and other IDAs can't reach them. This PR moves the pieces that were agreed as feasible to extract without dragging platform coupling along:
errors.py: the ADR 0029 standardized error envelope: standardized_error_exception_handler, the Conflict (409) exception, the envelope formatters, the error-type URI catalog with a register_error_type extension helper, and a plain-DRF ErrorResponseSerializer for documenting error responses in OpenAPI schemas.
mixins.py: StandardizedErrorMixin, the per-view opt-in for the handler.
shaping.py: ADR 0036 response shaping: project() for ?fields= top-level field selection and MinimalViewMixin for the ?view=minimal preset.
paginators.py (existing): paginate_manually() and IterablePaginationMixin, so endpoints outside DRF's generic list machinery can return the ADR 0032 envelope without hand-writing the paginate/serialize/respond sequence.
routers.py: opaque-key lookup_value_regex constants (COURSE_KEY_LOOKUP_REGEX, USAGE_KEY_LOOKUP_REGEX), which every router-registered ViewSet keyed by an opaque key currently hand-writes.
url_converters.py: the ADR 0038 (URL structure standardization) shared Django path converters: CourseKeyConverter and UsageKeyConverter, registered once per service via register_url_converters() as <course_key:...> / <usage_key:...>. Conforming routes resolve opaque keys in the URLconf, views receive parsed keys, and malformed or deprecated (Org/Course/Run, i4x://) keys become routing-level 404s. Extracted here per the ADR's "Code examples" section and the ADR review request (openedx/openedx-platform#39003 (comment) — openedx/openedx-platform#39003 (comment)); non-deprecated-key APIs use the converters, while endpoints that must keep serving Old Mongo content keep the permissive routers.py lookup regexes. Behaviour verified identical to the edx-platform copy it replaces, on Django 4.2 and 5.2.
testing.py: assert_error_envelope(), one assertion for consumer test suites instead of the per-field loops the pilot tests repeat.
The one real obstacle was the handler's import of ignored_error_exception_handler from platform internals. That coupling is now inverted: the handler delegates to a base handler resolved from a new EDX_DRF_EXTENSIONS['STANDARDIZED_ERROR_BASE_HANDLER'] setting (dotted path or callable, defaulting to DRF's own exception_handler), so edx-platform keeps its error monitoring purely through configuration. A guard test enforces that nothing in this package imports from edx-platform.
No new dependencies (url_converters depends only on edx-opaque-keys, already in base.in). Bumps version to 10.8.0.