feat: Audit instrumentation for Browse & Request approval flows - #271
feat: Audit instrumentation for Browse & Request approval flows#271shravani-sonata-gottapu wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #271 +/- ##
==========================================
+ Coverage 87.64% 87.66% +0.02%
==========================================
Files 157 157
Lines 13450 13477 +27
Branches 1303 1308 +5
==========================================
+ Hits 11788 11815 +27
Misses 1359 1359
Partials 303 303 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds audit instrumentation to the Browse & Request (B&R) learner credit approval flows so that admin approvals and the resulting allocation/reallocation write LearnerContentAssignmentAction rows with consistent attribution, source, request UUID metadata, and per-call correlation IDs for traceability across batched approvals.
Changes:
- Threaded
actor_lms_user_id,source, andcorrelation_idthroughsubsidy_request.api → subsidy_access_policy.api/models → content_assignments.api. - Added creation of
APPROVEDassignment action rows during approval, andALLOCATED/REALLOCATEDassignment action rows during allocation. - Added/updated tests to validate correlation ID reuse across approve-all batches and correct action attribution/metadata.
Critical issues (must address):
create_assignment_action()currently writes action rows withcompleted_atunset andactor_typehard-coded toADMIN, which can misattribute system-triggered actions and produces inconsistent audit data.- Current call flow writes allocation action rows before approved action rows, which contradicts the PR description’s “APPROVED followed by ALLOCATED/REALLOCATED” ordering.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| enterprise_access/apps/subsidy_request/tests/test_api.py | Extends approval tests to assert APPROVED action rows and correlation-id sharing for bulk approvals. |
| enterprise_access/apps/subsidy_request/api.py | Threads source/correlation_id through approval flow and writes APPROVED assignment action rows. |
| enterprise_access/apps/subsidy_access_policy/tests/test_models.py | Updates expectations for new passthrough args to allocate_assignment_for_requests. |
| enterprise_access/apps/subsidy_access_policy/models.py | Adds passthrough parameters to SubsidyAccessPolicy.approve() and forwards them to allocation. |
| enterprise_access/apps/subsidy_access_policy/api.py | Adds passthrough parameters to validate_and_allocate() and forwards them to policy approval. |
| enterprise_access/apps/content_assignments/tests/test_api.py | Adds tests asserting ALLOCATED/REALLOCATED action rows and correlation-id behavior. |
| enterprise_access/apps/content_assignments/api.py | Adds create_assignment_action() helper and writes allocation action rows during allocate_assignment_for_requests(). |
| enterprise_access/apps/api/v1/views/browse_and_request.py | Generates a fresh correlation ID per approve / approve-all request and passes appropriate AssignmentSources. |
| enterprise_access/apps/api/v1/tests/test_browse_and_request_views.py | Adds view-level assertions for source propagation and correlation-id generation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
enterprise_access/apps/content_assignments/api.py:692
- Mapping assignments back to requests keys by (lms_user_id, course_id). If multiple requests are processed for users without an lms_user_id (supported elsewhere in this module), the key becomes (None, course_id) for all of them and the dict will collide, potentially associating the wrong assignment (and wrong audit action metadata) to a request. Consider falling back to learner_email/user.email when lms_user_id is None to keep keys unique.
assignments_by_learner_and_course = {
(asg.lms_user_id, asg.content_key): asg for asg in all_affected_assignments
}
| def build_assignment_action( | ||
| assignment, | ||
| action_type, | ||
| actor_lms_user_id, | ||
| source, | ||
| correlation_id=None, | ||
| extra_metadata=None, | ||
| ): | ||
| """ | ||
| Returns an unsaved ``LearnerContentAssignmentAction`` for the given assignment. | ||
|
|
||
| Callers are responsible for persisting the returned instance, typically via | ||
| ``LearnerContentAssignmentAction.objects.bulk_create()`` alongside other action | ||
| rows so that call order determines write order. | ||
| """ |
JIRA
ENT-12028 : https://2u-internal.atlassian.net/browse/ENT-12028
Description:
Instruments the Browse & Request (B&R) approval flows in enterprise-access to write LearnerContentAssignmentAction audit rows for admin approvals and their resulting allocation/reallocation. Every approval call generates its own correlation ID and reuses it across the row(s) it produces: for a single approval this links the APPROVED row to its ALLOCATED/REALLOCATED row; for approve-all it also ties together every approved request in that batch. Each row captures the reviewing admin as actor_lms_user_id with actor_type=admin, and records the originating request UUID in metadata.
Changes:
Learner credit request approval flow :
Updated the single approval endpoint (POST /api/v1/learner-credit-requests/approve/) to write an APPROVED action row followed by an ALLOCATED or REALLOCATED action row for the approved request. Both rows capture the reviewer as the acting admin, actor_type=admin, and the originating request UUID in metadata, sharing one correlation ID.
Bulk approval flow :
Updated the approve-all endpoint (POST /api/v1/learner-credit-requests/approve-all/) to write the same APPROVED + ALLOCATED/REALLOCATED row pair for each approved request. A single correlation ID is generated per approve-all call and shared across every row written for that batch, for traceability.
Audit source tracking:
Added and threaded new AssignmentSources values through the approval call chain (subsidy_request.api → subsidy_access_policy.api/models → content_assignments.api):
browse_request_approve for individual approvals
browse_request_approve_all for bulk approvals
Audit row helper
Added create_assignment_action in content_assignments/api.py as a shared helper for persisting a single audit action row (actor, actor_type, source, metadata) so both the approval path and the allocation path write consistent rows.
Tests :
Added and updated coverage for:
Single approvals producing APPROVED plus ALLOCATED/REALLOCATED action rows, with reviewer attribution (actor_lms_user_id), actor_type=admin, and request UUID present in metadata
Bulk approvals writing one action-row pair per approved request, sharing one correlation ID across the batch
A fresh correlation ID being generated per approval call (distinct across separate calls)
Correct source values recorded for single vs. bulk approval flows
SubsidyAccessPolicy.approve / validate_and_allocate / allocate_assignment_for_requests passing actor_lms_user_id, source, and correlation_id through correctly