Skip to content

feat(session): move the SPA session poll off the legacy CFM - #306

Open
rlorenzo wants to merge 1 commit into
feature/session-timeout-endpointfrom
feature/session-timeout-spa
Open

feat(session): move the SPA session poll off the legacy CFM#306
rlorenzo wants to merge 1 commit into
feature/session-timeout-endpointfrom
feature/session-timeout-spa

Conversation

@rlorenzo

@rlorenzo rlorenzo commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Stacked on #304, which is stacked on #302. Review this one third.

Moves SessionTimeout.vue onto /api/sessionTimeout and applies the same fixes as #302 (non-OK rejection, error banner, poll reschedule, stale-warning clear). After this, nothing in web/ or VueApp/ references the CFM, so seconds_until_timeout_v2.cfm can be retired in the legacy repo (seconds_until_timeout.cfm stays; VIPER 1 uses it).

Uses plain fetch rather than useFetch on purpose: useFetch pushes every failure to the global error store, which would raise a banner on each blip of a background poll and fire the auth handler while our own dialog offers Log in.

No component test; the component is timer-driven and the useful assertions need the real endpoint. End-to-end under the /2 PathBase needs a TEST deploy.

@rlorenzo

rlorenzo commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: c8296b0f-53a4-44a8-a543-aed971873586

📥 Commits

Reviewing files that changed from the base of the PR and between 551ed0c and 253a224.

📒 Files selected for processing (1)
  • VueApp/src/components/SessionTimeout.vue

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Session timeout polling now uses the API session-timeout endpoint. The component retries failed checks, handles session extensions, formats expiration times correctly, and displays extension failures without closing the dialog.

Changes

Session timeout flow

Layer / File(s) Summary
Polling and timeout state
VueApp/src/components/SessionTimeout.vue
Polling uses direct fetch requests with HTTP error handling. Failed checks reschedule silently. The component hides stale warnings and formats expiration times with 12-hour handling.
Session extension and feedback
VueApp/src/components/SessionTimeout.vue
Successful extensions reset polling and warning state. Failed extensions keep the dialog open and show an error banner. Reset logic clears the failure state.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 253a2

Session timeout polling now uses the API endpoint and provides retry and failure feedback without leaving stale warnings. The supplied change context identifies no remaining merge-blocking risk.

Sequence Diagram(s)

sequenceDiagram
  participant SessionTimeoutVue
  participant PollingTimer
  participant SessionTimeoutAPI
  PollingTimer->>SessionTimeoutVue: trigger session check
  SessionTimeoutVue->>SessionTimeoutAPI: fetch session-timeout status
  SessionTimeoutAPI-->>SessionTimeoutVue: return status or HTTP error
  SessionTimeoutVue->>PollingTimer: schedule next check
  SessionTimeoutVue->>SessionTimeoutAPI: request session extension
  SessionTimeoutAPI-->>SessionTimeoutVue: return extension result
  SessionTimeoutVue->>PollingTimer: reset polling after success
  SessionTimeoutVue-->>SessionTimeoutVue: show error banner after failure
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: moving the SPA session poll from the legacy CFM endpoint.
Description check ✅ Passed The description accurately explains the endpoint migration, related fixes, testing limits, and legacy CFM retirement plan.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/session-timeout-spa

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@VueApp/src/components/SessionTimeout.vue`:
- Around line 32-33: The session lifecycle requests in
VueApp/src/components/SessionTimeout.vue at lines 32-33 and 58-59 both require
changes: replace the raw fetch calls in the polling and session-extension flows
with a shared silent useFetch or service-layer method for sessionTimeout and
RefreshSession. Ensure this path suppresses global error and authentication
handling while preserving the existing API response contract.
- Around line 68-70: Update the failed-session-extension state in SessionTimeout
so the open dialog includes an accessible close q-btn wired to
hideSessionTimeoutWarning, while preserving the StatusBanner text. Ensure the
Log in action is visible alongside the existing Retry/Refresh action when
sessionExtendFailed is true, and add Playwright coverage for keyboard navigation
plus both actions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 41e27369-f9d1-41fd-ad3b-45d95b33e500

📥 Commits

Reviewing files that changed from the base of the PR and between 6e97020 and 7c677c8.

📒 Files selected for processing (1)
  • VueApp/src/components/SessionTimeout.vue

Comment thread VueApp/src/components/SessionTimeout.vue Outdated
Comment thread VueApp/src/components/SessionTimeout.vue

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Vue SPA’s session-timeout polling to use the VIPER 2 /api/sessionTimeout endpoint (instead of the legacy ColdFusion seconds_until_timeout_v2.cfm), aligning the SPA with the already-migrated Razor implementation and removing the legacy dependency/security hole.

Changes:

  • Replaced the legacy CFM poll URL (with loginId in the query string) with a VIPER 2 API poll based on VITE_API_URL.
  • Improved polling resiliency by rejecting non-OK responses and ensuring failures still reschedule future checks.
  • Added in-dialog error UI for failed session extension attempts via StatusBanner, and centralized the expiry time formatter.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@rlorenzo
rlorenzo force-pushed the feature/session-timeout-spa branch from 7c677c8 to e956bcb Compare August 8, 2026 02:34
@codecov-commenter

codecov-commenter commented Aug 8, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 1.57kB (0.07%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
viper-frontend-esm 2.18MB 1.57kB (0.07%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: viper-frontend-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/GenericError-*.css 818 bytes 210.88kB 0.39%
assets/GenericError-*.js 1.07kB 145.91kB 0.74%
assets/schedule-*.js 13 bytes 55.28kB 0.02%
assets/TermManagement-*.js 9 bytes 52.89kB 0.02%
assets/SortableList-*.js 48 bytes 45.56kB 0.11%
assets/CourseDetail-*.js 7 bytes 39.81kB 0.02%
assets/PhotoGallery-*.js 19 bytes 36.55kB 0.05%
assets/InstructorEdit-*.js 15 bytes 28.95kB 0.05%
assets/Files-*.js -1 bytes 27.03kB -0.0%
assets/InstructorList-*.js 9 bytes 26.19kB 0.03%
assets/StaffDashboard-*.js 57 bytes 25.81kB 0.22%
assets/ManageCompetencies-*.js -1 bytes 23.96kB -0.0%
assets/effort-*.js 58 bytes 23.75kB 0.24%
assets/CrossListedCoursesSection-*.js 14 bytes 22.49kB 0.06%
assets/RichTextEditor-*.js 53 bytes 21.38kB 0.25%
assets/ContentBlockEdit-*.js 7 bytes 20.78kB 0.03%
assets/MultiYearReport-*.js 14 bytes 18.91kB 0.07%
assets/ClinicianScheduleView-*.js 13 bytes 18.73kB 0.07%
assets/AuditList-*.js -1 bytes 5.32kB -0.02%
assets/AuditList-*.js 47 bytes 17.38kB 0.27%
assets/EmergencyContactForm-*.js 12 bytes 17.31kB 0.07%
assets/RotationScheduleView-*.js 13 bytes 17.31kB 0.08%
assets/use-*.js 13 bytes 6.28kB 0.21%
assets/MyAssessments-*.js 53 bytes 16.77kB 0.32%
assets/CourseList-*.js 11 bytes 16.21kB 0.07%
assets/EffortTypeList-*.js 47 bytes 15.59kB 0.3%
assets/ManageLinkCollections-*.js 14 bytes 15.42kB 0.09%
assets/EmergencyContactList-*.js 13 bytes 12.09kB 0.11%
assets/AuditLogPage-*.js -41 bytes 11.55kB -0.35%
assets/CmsHome-*.js 13 bytes 11.49kB 0.11%
assets/CourseImportDialog-*.js 12 bytes 11.0kB 0.11%
assets/SVMPhonesMaintain-*.js -41 bytes 10.57kB -0.39%
assets/permission-*.js 53 bytes 9.82kB 0.54%
assets/CourseLinkDialog-*.js -36 bytes 9.54kB -0.38%
assets/ManageBundleCompetencies-*.js 1 bytes 9.18kB 0.01%
assets/StudentClassYear-*.js -3 bytes 9.09kB -0.03%
assets/LeftNavEdit-*.js 13 bytes 8.82kB 0.15%
assets/MyEffort-*.js 13 bytes 8.36kB 0.16%
assets/LeftNavMenus-*.js -51 bytes 8.22kB -0.62%
assets/ImportFiles-*.js 12 bytes 7.84kB 0.15%
assets/ContentBlocks-*.js -1 bytes 7.8kB -0.01%
assets/EmergencyContactView-*.js 54 bytes 7.64kB 0.71%
assets/AssessmentList-*.js -1 bytes 7.05kB -0.01%
assets/ContentBlockHistory-*.js 52 bytes 6.79kB 0.77%
assets/CompetenciesBundleReport-*.js 58 bytes 6.27kB 0.93%
assets/WebReports-*.js 12 bytes 6.26kB 0.19%
assets/EffortRecordEditDialog-*.js -41 bytes 6.14kB -0.66%
assets/RecordActionCell-*.js 52 bytes 6.04kB 0.87%
assets/AssessmentEpa-*.js 12 bytes 5.78kB 0.21%
assets/BulkEncrypt-*.js -1 bytes 5.65kB -0.02%
assets/PhoneListMaintain-*.js -41 bytes 5.6kB -0.73%
assets/ClinicalEffort-*.js 53 bytes 5.45kB 0.98%
assets/ManageEpas-*.js -1 bytes 5.45kB -0.02%
assets/ManageBundles-*.js -1 bytes 5.24kB -0.02%
assets/ManageLevels-*.js -1 bytes 4.69kB -0.02%
assets/FileAuditLog-*.js 52 bytes 4.47kB 1.18%
assets/BiorenderStudents-*.js -1 bytes 4.37kB -0.02%
assets/SchoolSummary-*.js 13 bytes 3.97kB 0.33%
assets/AssessmentEpaEdit-*.js 12 bytes 3.93kB 0.31%
assets/RecordFormDialog-*.js -45 bytes 3.77kB -1.18%
assets/TeachingActivityGrouped-*.js 13 bytes 3.67kB 0.35%
assets/ClinicalSchedulerHome-*.js 88 bytes 3.63kB 2.48%
assets/MeritAverage-*.js 13 bytes 3.61kB 0.36%
assets/ContentDiffDialog-*.js 48 bytes 3.49kB 1.39%
assets/TeachingActivityIndividual-*.js 48 bytes 3.4kB 1.43%
assets/StatusIcon-*.js 58 bytes 3.38kB 1.74%
assets/AssessmentCompetency-*.js -41 bytes 3.01kB -1.34%
assets/ExportToolbar-*.js 52 bytes 2.97kB 1.78%
assets/EvalDetail-*.js 13 bytes 2.94kB 0.44%
assets/ManageDomains-*.js -1 bytes 2.89kB -0.03%
assets/SVMPhones-*.js 13 bytes 2.72kB 0.48%
assets/LevelSelect-*.js 53 bytes 2.68kB 2.02%
assets/DateRangeFilter-*.js -1 bytes 2.09kB -0.05%
assets/EvalSummary-*.js 13 bytes 2.07kB 0.63%
assets/ManageRoles-*.js -1 bytes 2.05kB -0.05%
assets/LeftNavMenuSettingsFields-*.js 52 bytes 1.65kB 3.25%
assets/ModifiedStamp-*.js 13 bytes 1.27kB 1.03%
assets/ListCardField-*.js 58 bytes 927 bytes 6.67% ⚠️
assets/QBanner-*.js (New) 906 bytes 906 bytes 100.0% 🚀
assets/report-*.js 13 bytes 664 bytes 2.0%
assets/StudentsHome-*.js 13 bytes 227 bytes 6.07% ⚠️
assets/ManageLegacyCompetencyMapping-*.js 13 bytes 207 bytes 6.7% ⚠️
assets/_plugin-*.js (New) 84 bytes 84 bytes 100.0% 🚀
assets/StatusBanner-*.js (Deleted) -1.74kB 0 bytes -100.0% 🗑️
assets/StatusBanner-*.css (Deleted) -831 bytes 0 bytes -100.0% 🗑️

Files in assets/GenericError-*.js:

  • ./src/components/SessionTimeout.vue → Total Size: 146 bytes

@codecov-commenter

codecov-commenter commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.06%. Comparing base (81c4a61) to head (572fe7f).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
VueApp/src/components/SessionTimeout.vue 0.00% 33 Missing ⚠️
Additional details and impacted files
@@                         Coverage Diff                          @@
##           feature/session-timeout-endpoint     #306      +/-   ##
====================================================================
- Coverage                             45.07%   45.06%   -0.01%     
====================================================================
  Files                                   942      942              
  Lines                                 49202    49210       +8     
  Branches                               6595     6598       +3     
====================================================================
  Hits                                  22177    22177              
- Misses                                26079    26087       +8     
  Partials                                946      946              
Flag Coverage Δ
backend 42.35% <ø> (ø)
frontend 63.27% <0.00%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
VueApp/src/components/SessionTimeout.vue 0.00% <0.00%> (ø)

@rlorenzo
rlorenzo force-pushed the feature/session-timeout-spa branch from e956bcb to d5226aa Compare August 8, 2026 06:10
@rlorenzo
rlorenzo force-pushed the feature/session-timeout-spa branch 2 times, most recently from 6081219 to 281d071 Compare August 8, 2026 18:08
@rlorenzo
rlorenzo force-pushed the feature/session-timeout-spa branch from 281d071 to 2f32839 Compare August 10, 2026 03:27
@rlorenzo
rlorenzo requested a lite review from Copilot August 10, 2026 05:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (3)

VueApp/src/components/SessionTimeout.vue:60

  • fetch(viperHome + "RefreshSession") assumes VITE_VIPER_HOME always ends with a trailing slash. If it is configured as "/2" (no trailing slash), this becomes "/2RefreshSession" and the refresh button will never work on that environment.
    fetch(viperHome + "RefreshSession")
        .then((r) => (r.ok ? r.json() : Promise.reject(new Error("RefreshSession returned " + r.status))))

VueApp/src/components/SessionTimeout.vue:9

  • sessionTimeoutUrl is built by direct string concatenation and relies on VITE_API_URL always ending with a trailing slash. If someone configures it as "/api" (no trailing slash), the fetch will go to "/apisessionTimeout" and silently fail/reschedule forever.

This issue also appears on line 59 of the same file.

const sessionTimeoutUrl = `${import.meta.env.VITE_API_URL}sessionTimeout`

VueApp/src/components/SessionTimeout.vue:53

  • The reschedule delay uses the magic number 300000 (ms). Using a seconds/minutes expression makes this easier to audit and prevents unit mixups during future edits.
            sessionTimeoutCheckEventId = window.setTimeout(checkSessionTimeout, 300000)

@rlorenzo

rlorenzo commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

It introduces browser-compatibility risk via AbortSignal.timeout() without a fallback and adds more direct fetch usage that diverges from the app’s useFetch conventions without a wrapper-level “silent poll” option.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

VueApp/src/components/SessionTimeout.vue:57

  • Same compatibility concern here: AbortSignal.timeout() may be missing in some browsers, which would throw before the request starts. Guarding the timeout creation (or using AbortController) avoids breaking the Refresh Session flow on unsupported clients.
    fetch(viperHome + "RefreshSession", { signal: AbortSignal.timeout(10000) })
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread VueApp/src/components/SessionTimeout.vue
Comment thread VueApp/src/components/SessionTimeout.vue

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The timer/poll scheduling can accumulate multiple concurrent timeouts when a refresh overlaps an in-flight poll, which can cause unnecessary extra polling over time.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread VueApp/src/components/SessionTimeout.vue
- Point the poll at the VIPER 2 endpoint via VITE_API_URL, dropping the
  CFM URL that passed the login id as an unauthenticated query parameter
- Keep plain fetch rather than useFetch, which reports every failure
  through the global error store: that would banner a silent five minute
  poll and fire the auth handler while our dialog offers a log in
- Carry over the Razor fixes, since this file had the same defects:
  reject non-OK responses, report a failed extend in a StatusBanner
  while offering both Refresh Session and Log in,
  reschedule after a failed poll rather than stopping for the life of
  the page, stand the warning down when the session is extended
  elsewhere, and render midnight as 12 AM rather than 0 AM

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is localized to the SPA session timeout component and the updated polling/URL construction matches the repo’s VITE_API_URL and VITE_VIPER_HOME conventions without introducing verified defects.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants