Migrate the facility plugin to the new resource methods - #15184
Conversation
Build Artifacts
Smoke test screenshot |
39f2b74 to
7f68609
Compare
7f68609 to
e08ef88
Compare
AlexVelezLl
left a comment
There was a problem hiding this comment.
Good job overall, @rtibblesbot! I think the major issue right now is the entire facility object being sent even if we just update a single setting. Beyond that, please add a "Manual QA" section on the PR description following these instructions (and that PR description example), there are several risky areas to be tested here, like the users filters handling, so please carefully describe them.
| id: facilityDatasetId.value, | ||
| data, | ||
| }); | ||
| await FacilityDatasetResource.update(facilityDatasetId.value, data); |
There was a problem hiding this comment.
This is no longer doing any diff checking; this is a regression from the old implementation. Please refactor this so that we can send a baseline to the update method.
There was a problem hiding this comment.
Fixed — saveFacilityConfig now passes settingsCopy as the update baseline, so only edited settings go on the wire (and an unchanged config sends nothing). settingsCopy is the snapshot last synced with the server: copySettings() runs on mount and after every save. Spec asserts the baseline.
| id, | ||
| data: updateData, | ||
| }).then( | ||
| return ClassroomResource.update(id, updateData).then( |
There was a problem hiding this comment.
This is only being used to edit the class name, right? Could you rather have updateData specify which specific fields it allows? So that we can better justify why we don't do any diff checking here.
There was a problem hiding this comment.
Right, rename is the only use. The action now takes { id, name } and sends { name } — the one field it can change, so the payload is the diff by construction. ClassRenameModal updated to match.
9e061cb to
7ee2391
Compare
The Model/Collection cache goes with them, so every read that did not pass `force: true` now hits the network. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The resource is imported only by facility, so the recipe's in-place rewrite applies rather than a `_v2` sibling. It now resolves with the payload instead of the axios response, and failures are logged by `request`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four behaviour changes come with useFetch: `dataLoading` clears on failure rather than spinning forever, `usersCount`/`totalPages` normalise to 0 instead of undefined, a superseded fetch's response is discarded, and failure handling runs from a watcher a tick later instead of a catch. `fetchClasses` stays a plain `list()` — `useUsersFilters` dereferences the `classes` ref before any fetch runs, and `useFetch` initialises `data` to null. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It duplicates packages/kolibri-common/apiResources/PortalResource.js and has no importers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
member_of, related_to__in and user_type joined memberships and roles, returning a user once per matching row - a learner in two classes appeared twice. The legacy Collection cache deduplicated by id client-side, hiding it until the resource migration. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Legacy saveModel dirty-checked and skipped the request when the payload was empty; update() always PATCHes. A user-type change with no edited details sent an empty PATCH purely to obtain roles for updateFacilityLevelRoles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`saveConfig` saves login settings and then the rest of the config. `saveFacilityLoginSettings` re-snapshotted `settingsCopy` as it finished, so the config diff that follows saw no changes and `update` sent no request — every edited setting was silently dropped behind a "saved" snackbar. `saveFacilityConfig` already takes the snapshot once both halves are saved. Dropping the early one also fixes the failure path, where `undoSettingsChange` restored the edited state rather than the original. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fec356f to
ae36409
Compare
AlexVelezLl
left a comment
There was a problem hiding this comment.
Code changes look good, ready for QA!
|
Holding off on changes while this PR is in Change the board status when QA is done and I'll pick up any outstanding review feedback, CI failures and conflicts on the next cycle. @rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly How was this generated?
|
|
All the facility features listed in the QA guidance perform as expected: editing user details, deleting accounts, changing facility settings & PIN - everything persists across reloads, and no regressions or console errors are observed. |
|
Holding off on changes while this PR is in Change the board status when QA is done and I'll pick up any outstanding review feedback, CI failures and conflicts on the next cycle. @rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly How was this generated?
|
|
Thanks @radinamatic. The branch is still Nothing outstanding on my side: all of @AlexVelezLl's review comments have replies and were confirmed. CI is green (the one pending check is the hourly contributor-check workflow, unrelated). Merge is blocked only by the standing Move this off |
radinamatic
left a comment
There was a problem hiding this comment.
Delay in approval due to GH glitches...
LGTM! 💯
🚀
Summary
facilitymoves to the newResourcemethods;DeletedFacilityUserResource.restoreCollectionis rewritten ontorequest.useList. Deletes the dead plugin-localPortalResource.member_of,related_to__inanduser_typeOR-ed a multi-valued relation, returning a user once per membership or role. Now a correlatedExists()subquery.updateClasstakes anamerather than an arbitraryupdateData, so its payload is already the diff with no cache to compute one.References
Fixes #15062. Recipe: #15057.
Reviewer guidance
Code review
useFacilityEditor.js:220—saveFacilityConfigpassessettingsCopyas theupdatebaseline, replacing the Model cache's diff. Check no path mutatessettingswithout a matchingcopySettings().userManagement/actions.js:62— a role-only change nowretrieves the user; check nothing downstream needed the PATCH response.useUserManagement.js:73— check the stale-page 404 redirect cannot loop, now error handling runs in awatch.facility_user.py:71— check no caller relied on a row per membership or role.MoveToTrashModal.vue:90,AssignCoachesSidePanel.vue:178,EnrollLearnersSidePanel.vue:176,ClassCopyModal.vue:178andRemoveFromClassSidePanel.vue:212keeplist(): their loading ref is also set by a write path, whichuseListcannot do.force: truenow hit the network every time.Manual QA
No UI changes; the
facilityplugin should keep behaving the same. Both flows need a facility with a user other than yourself: on a fresh install finish the setup wizard, sign in as the super admin, then Facility > Users > New user to create a learner.AI usage
Used Claude Code to apply the recipe call site by call site and diagnose the duplicate row. Verified with the facility and kolibri-common Jest suites, the auth Python suite, prek, and live QA.
Duplicate row, before and after:
Pages exercised live:
@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
Committed separately: no commit on this branch introduced the lines these changes touch, so they are a new commit rather than folded into the work they amend:
kolibri/plugins/facility/frontend/modules/userManagement/__tests__/actions.spec.js🟡 Waiting for feedback
Last updated: 2026-08-17 16:33 UTC