Fix: Refactored backend to fetch course data with schedule#1421
Fix: Refactored backend to fetch course data with schedule#1421
Conversation
|
Sorry for ghosting this. I'm looking through codebase and stuff to compose my review ✍️ |
alexespejo
left a comment
There was a problem hiding this comment.
Sorry for the very delayed review. it took me a while to figure out how stuff works :p
There was a problem hiding this comment.
8 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/antalmanac/src/backend/lib/websoc-service.ts">
<violation number="1" location="apps/antalmanac/src/backend/lib/websoc-service.ts:69">
P2: Debug `console.log` statements left in production code. These log the full request URL and entire API response body, which will produce excessive output in production. Remove or replace with a proper logger at an appropriate log level.</violation>
<violation number="2" location="apps/antalmanac/src/backend/lib/websoc-service.ts:76">
P1: Missing error handling: `queryWebSoc` doesn't check the HTTP response status or validate `data.data` before passing it to `sortWebsocResponse`. If the API returns an error, this will produce a confusing runtime exception. The sibling `queryWebSocDepartments` correctly validates the response — apply similar validation here.</violation>
<violation number="3" location="apps/antalmanac/src/backend/lib/websoc-service.ts:112">
P1: Bug: `Set` uses reference equality for objects, so this deduplication logic is ineffective. Two course objects from different API responses representing the same course will always be treated as distinct. Consider deduplicating by a unique key (e.g., `courseNumber`) using a `Map` instead.</violation>
</file>
<file name="apps/antalmanac/src/backend/routers/userData.ts">
<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:113">
P2: Courses that fail to hydrate are silently dropped from the schedule. If WebSOC is temporarily unavailable or a course is no longer listed, users will see schedules with missing courses and no indication of what was lost. Consider logging which courses couldn't be hydrated, or returning a partial/fallback representation (e.g., keep the `ShortCourse` data with a flag indicating it's unhydrated) so the frontend can inform the user.</violation>
<violation number="2" location="apps/antalmanac/src/backend/routers/userData.ts:178">
P2: The hydration wrapping logic (null-check → hydrate → reconstruct response) is duplicated identically across `getUserDataHydrated` and `getUserDataWithSession`. Extract a shared helper (e.g., `hydrateUserData(userData)`) to reduce duplication and make future changes less error-prone.</violation>
</file>
<file name="apps/antalmanac/src/stores/AppStore.ts">
<violation number="1" location="apps/antalmanac/src/stores/AppStore.ts:421">
P1: Bug: `JSON.stringify` comparison will always fail due to mismatched property order between `getScheduleAsSaveState()` and `toShortSaveState()`.
`getScheduleAsSaveState()` returns `{ schedules, scheduleIndex }` with courses as `{ color, term, sectionCode }`, but `toShortSaveState()` produces `{ scheduleIndex, schedules }` with courses as `{ sectionCode, term, color }`. Since `JSON.stringify` preserves insertion order, these will never be equal even for identical data. This means `hasDataChanged` is always `false`, so `deleteTempSaveData()` is never reached and `loadTempSaveData()` always runs.
Either align the property order in `toShortSaveState` with `getScheduleAsSaveState`, or use a deep-equality comparison that is order-insensitive.</violation>
</file>
<file name="apps/antalmanac/src/backend/lib/rds.ts">
<violation number="1" location="apps/antalmanac/src/backend/lib/rds.ts:291">
P2: Misleading JSDoc: `schedulesToShort` is a pure data transformation, but the comment says it "Deletes and recreates all of the user's schedules and contents" — this was copy-pasted from the `upsertSchedulesAndContents` method below.</violation>
</file>
<file name="apps/antalmanac/src/actions/AppStoreActions.ts">
<violation number="1" location="apps/antalmanac/src/actions/AppStoreActions.ts:375">
P2: If the `?? userDataResponse` fallback ever activates (i.e., `userData` is nullish), `scheduleSaveState.schedules` will be `undefined`, causing `isEmptySchedule` to throw at runtime. Use optional chaining like `handleScheduleImport` does (`scheduleSaveState?.schedules`), or guard against this case explicitly.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
2 issues found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/antalmanac/src/backend/routers/userData.ts">
<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:82">
P1: All-or-nothing hydration: a single term's WebSOC failure now causes the entire hydration to abort, returning all schedules unhydrated. Previously, only the failed term's courses would be dropped while other terms' courses were still hydrated. Consider restoring per-term graceful degradation (e.g., setting an empty dict for the failed term) so that a partial WebSOC outage doesn't penalize courses from other terms.</violation>
<violation number="2" location="apps/antalmanac/src/backend/routers/userData.ts:178">
P3: The try/catch + hydration + fallback pattern is duplicated between `getUserDataHydrated` and `getUserDataWithSession`. Consider extracting a helper like `tryHydrateUserData(userData)` to reduce duplication and ensure consistent error handling if this pattern evolves.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/antalmanac/src/backend/routers/userData.ts">
<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:82">
P1: Silent data loss: When WebSOC is unavailable for a term, all courses from that term are silently dropped from the hydrated schedule. Previously, the thrown error caused the caller to fall back to returning the original unhydrated data (which the frontend can handle). Now courses just disappear.
Consider either: (a) preserving the original short course entries when hydration fails for a term so the frontend can handle them, or (b) restoring the throw so the caller's fallback logic can return unhydrated data.</violation>
<violation number="2" location="apps/antalmanac/src/backend/routers/userData.ts:178">
P1: Resilience regression: Removing the try-catch around `hydrateScheduleCourses` means any unexpected error during hydration (e.g., malformed data, runtime errors in the mapping logic) will now crash the endpoint and return an error to the client. Previously, the catch block gracefully fell back to returning unhydrated schedule data, ensuring users always got their data. Consider keeping the try-catch fallback for robustness.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/antalmanac/src/backend/routers/userData.ts">
<violation number="1" location="apps/antalmanac/src/backend/routers/userData.ts:181">
P2: The hydration try-catch block is duplicated verbatim between `getUserDataHydrated` and `getUserDataWithSession`. Extract a shared helper (e.g., `hydrateUserData(userData)`) that wraps the try-catch, result construction, and fallback logic to keep it DRY and reduce maintenance burden.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
alexespejo
left a comment
There was a problem hiding this comment.
I want to use the dev feature in #1565 to test the changes on staging. Maybe we can also benchmark the performance improvements
| }; | ||
|
|
||
| export function isEmptySchedule(schedules: ShortCourseSchedule[]) { | ||
| export function isEmptySchedule(schedules: ShortCourseSchedule[] | ScheduleSaveState['schedules']) { |
There was a problem hiding this comment.
question: ShortCourseSchedule[] and ScheduleSaveState['schedules'] seem to have the same shape?
There was a problem hiding this comment.
ScheduleSaveState['schedules'] has ScheduleCourse which has the course data
| export type ScheduleSaveState = { | ||
| schedules: Array<{ | ||
| scheduleName: string; | ||
| courses: (ShortCourse | ScheduleCourse)[]; |
There was a problem hiding this comment.
I'm still weary about mixing the types just afraid it will create unforeseen complications in prod
There was a problem hiding this comment.
It's more of a fallback in case websoc resp fails and also allows for the same type to be used to save into db. Like I could technically get rid of it and seperate the two types, bc idt aa will load without websoc. I'm more weary having just a state that must relies on websoc resp but if you think its better, than I can just remove it the mixed type, seperate the two. There's just no way for me to not keep the legacy type bc of our db, skeleton logic, etc.
| export type ScheduleSaveState = { | ||
| schedules: Array<{ | ||
| scheduleName: string; | ||
| courses: (ShortCourse | ScheduleCourse)[]; |
There was a problem hiding this comment.
I'm still weary of mixing the types in our code base. I'm afraid of unforseen complications in prod that can come up
Summary
Refactored the backend to return a hydrated schedule (schedule + course data in one response) instead of requiring the frontend to make n separate websoc requests per course.
Note: The legacy ScheduleSaveState type is kept for backward compatibility (fallback scenarios, skeleton loading, imports, db saving)
Test Plan
Issues
Closes #1059