Service status page - #403
Conversation
📝 WalkthroughWalkthroughThis PR adds service-health history contracts, uptime range utilities, a heatmap-based uptime page, toolbar controls, service cards, drilldown dialogs, navigation, and tests. It also updates typing, datastore handling, container builds, dependencies, and CI workflows. ChangesUptime monitoring
Repository maintenance
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant UptimeToolbar
participant UptimePage
participant useAPIFetch
participant HealthHistoryAPI
participant ServiceUptimeCard
participant UptimeTrack
participant BucketDrilldownDialog
User->>UptimeToolbar: select range or refresh
UptimeToolbar->>UptimePage: emit rangeChange or refresh
UptimePage->>useAPIFetch: request service-health history
useAPIFetch->>HealthHistoryAPI: GET /health/services/history
HealthHistoryAPI-->>useAPIFetch: return ServiceHealthHistory
useAPIFetch-->>UptimePage: provide history data
UptimePage->>ServiceUptimeCard: pass service summary and slots
ServiceUptimeCard->>UptimeTrack: render aligned buckets
User->>UptimeTrack: click uptime cell
UptimeTrack->>BucketDrilldownDialog: emit selected slot and bucket
BucketDrilldownDialog->>HealthHistoryAPI: fetch raw checks for selected slice
HealthHistoryAPI-->>BucketDrilldownDialog: return probe checks
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
# Conflicts: # test/components/data-stores/create/DataStoreProjectInitializer.spec.ts # test/utils/data-store-name.test.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
app/composables/useAPIFetch.ts (1)
56-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueType the
optsparameter.
opts?is still inferred asany. Type it with the option type thatuseAPIFetchaccepts so the wrapper is self-documenting and future refactorings are safer.🛠️ Proposed typing
export function getServiceHealthHistory( query: { start_date?: string; end_date?: string; service?: string[]; include_checks?: boolean; limit?: number; resolution?: number; } = {}, - opts?, + opts?: UseFetchOptions<ServiceHealthHistory>, ) {🤖 Prompt for 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. In `@app/composables/useAPIFetch.ts` around lines 56 - 73, Type the opts parameter in getServiceHealthHistory using the options type accepted by useAPIFetch, preserving its optional nature and ensuring the wrapper’s spread options remain type-safe.app/components/uptime/ServiceUptimeCard.vue (1)
71-71: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider dropping
servicefrom the card payload.The card sets
servicetoname, which is the display title (mapServiceName(name)fromapp/pages/uptime.vueline 130). The page then overwrites it with the raw service key atapp/pages/uptime.vueline 133. The value the card emits is therefore never used, and any other consumer would receive a display title where a key is expected. Either emit the track payload unchanged, or pass the service key to the card as a separate prop.♻️ Proposed change
- `@cell-click`="emit('cellClick', { ...$event, service: name })" + `@cell-click`="emit('cellClick', $event)"Then declare the emit payload without
serviceand keep the page's mapping as the single source:-const emit = defineEmits<{ - cellClick: [ - payload: { service: string; slot: UptimeSlot; bucket: UptimeBucket | null }, - ]; -}>(); +const emit = defineEmits<{ + cellClick: [payload: { slot: UptimeSlot; bucket: UptimeBucket | null }]; +}>();🤖 Prompt for 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. In `@app/components/uptime/ServiceUptimeCard.vue` at line 71, Update the cell-click handler in ServiceUptimeCard so it emits the track payload unchanged, removing the locally added service field. Keep the page-level mapping in uptime.vue as the single source for the raw service key, and adjust the event payload declaration if needed to no longer require service.test/components/uptime/UptimeTrack.spec.ts (1)
147-180: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReset the theme in a hook instead of at the end of each test.
setDark(false)runs as the last statement of the dark-mode tests. If an earlier assertion in the same test fails, the call never runs and the sharedtheme.isDarkref staystruefor the following tests. AbeforeEachreset makes isolation independent of assertion order.♻️ Proposed change
describe("UptimeTrack.vue", () => { + beforeEach(() => { + theme.isDark.value = false; + }); + it("renders one data point per slot", () => {Then remove the trailing
await setDark(false);calls, and addbeforeEachto thevitestimport.🤖 Prompt for 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. In `@test/components/uptime/UptimeTrack.spec.ts` around lines 147 - 180, Update the UptimeTrack test setup to import beforeEach from vitest and add a beforeEach hook that resets the shared theme to light with setDark(false). Remove the trailing await setDark(false) calls from the dark-mode tests, including “swaps to the dark palette when the theme changes” and “separates the cells with the surface colour of the current theme,” so cleanup runs even when assertions fail.test/pages/uptime.spec.ts (2)
361-372: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAdd coverage for overlapping range requests.
No test holds two page requests open at the same time. The page assigns
historyfrom whichever response settles last, so an out-of-order response is currently undetectable by this suite. See the related comment onapp/pages/uptime.vuelines 67-84.🤖 Prompt for 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. In `@test/pages/uptime.spec.ts` around lines 361 - 372, Add a test in the uptime page suite that starts two overlapping fetches, resolves them out of order, and verifies the page retains the latest requested range rather than replacing history with the stale response. Reuse the existing mockFetch, mountPage, and page state assertions, and cover the request-ordering behavior described in the uptime page implementation.
80-82: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAlign the comment with the assertion.
The comment describes a comparison of the floored start, but this test asserts only
end_date. Line 126 covers the flooring. Either assert the flooredstart_datehere or trim the comment to theend_dateclaim.🤖 Prompt for 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. In `@test/pages/uptime.spec.ts` around lines 80 - 82, Update the comment immediately above the assertion in the uptime test so it describes only the untouched end-date comparison, since the floored start-date behavior is already asserted elsewhere; keep the existing end_date assertion unchanged.
🤖 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 `@app/components/uptime/UptimeTrack.vue`:
- Around line 146-164: Add a keyboard-accessible control path in UptimeTrack’s
template by rendering a visually hidden, focusable button for each slot, with
keyboard activation emitting cellClick using that slot and its corresponding
bucketAt index. Keep the existing VChart pointer handler and role="img" behavior
unchanged, and ensure each button has an accessible label identifying its slot.
In `@app/pages/uptime.vue`:
- Around line 67-84: The uptime page’s load function applies stale results from
overlapping requests. In app/pages/uptime.vue lines 67-84, add a latestRequest
sequence guard, apply slots and history only for the newest request, and add
catch handling that surfaces failures; in test/pages/uptime.spec.ts lines
361-372, add coverage that keeps two requests pending, resolves them out of
order, and verifies the newest range remains displayed.
---
Nitpick comments:
In `@app/components/uptime/ServiceUptimeCard.vue`:
- Line 71: Update the cell-click handler in ServiceUptimeCard so it emits the
track payload unchanged, removing the locally added service field. Keep the
page-level mapping in uptime.vue as the single source for the raw service key,
and adjust the event payload declaration if needed to no longer require service.
In `@app/composables/useAPIFetch.ts`:
- Around line 56-73: Type the opts parameter in getServiceHealthHistory using
the options type accepted by useAPIFetch, preserving its optional nature and
ensuring the wrapper’s spread options remain type-safe.
In `@test/components/uptime/UptimeTrack.spec.ts`:
- Around line 147-180: Update the UptimeTrack test setup to import beforeEach
from vitest and add a beforeEach hook that resets the shared theme to light with
setDark(false). Remove the trailing await setDark(false) calls from the
dark-mode tests, including “swaps to the dark palette when the theme changes”
and “separates the cells with the surface colour of the current theme,” so
cleanup runs even when assertions fail.
In `@test/pages/uptime.spec.ts`:
- Around line 361-372: Add a test in the uptime page suite that starts two
overlapping fetches, resolves them out of order, and verifies the page retains
the latest requested range rather than replacing history with the stale
response. Reuse the existing mockFetch, mountPage, and page state assertions,
and cover the request-ordering behavior described in the uptime page
implementation.
- Around line 80-82: Update the comment immediately above the assertion in the
uptime test so it describes only the untouched end-date comparison, since the
floored start-date behavior is already asserted elsewhere; keep the existing
end_date assertion unchanged.
🪄 Autofix (Beta)
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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9e3ab9f0-1eb3-43f5-81d4-76f9a9c0667d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (34)
Dockerfileapp/components/analysis/AnalysesTable.vueapp/components/header/MenuHeader.vueapp/components/table/SearchBar.vueapp/components/uptime/BucketDrilldownDialog.vueapp/components/uptime/ServiceUptimeCard.vueapp/components/uptime/UptimeToolbar.vueapp/components/uptime/UptimeTrack.vueapp/composables/useAPIFetch.tsapp/composables/useServiceHealth.tsapp/pages/uptime.vueapp/plugins/api.tsapp/services/Api.tsapp/services/hub_adapter_swagger.jsonapp/utils/prettify-key.tsapp/utils/uptime-state.tsnuxt.config.tspackage.jsonpnpm-workspace.yamlprod.Dockerfileserver/routes/flame/api/auth/[...].tstest/components/data-stores/create/DataStoreProjectInitializer.spec.tstest/components/header/MenuHeader.spec.tstest/components/uptime/BucketDrilldownDialog.spec.tstest/components/uptime/ServiceUptimeCard.spec.tstest/components/uptime/UptimeToolbar.spec.tstest/components/uptime/UptimeTrack.spec.tstest/components/uptime/constants.tstest/composables/useServiceHealth.test.tstest/mockapi/handlers.tstest/pages/uptime.spec.tstest/utils/data-store-name.test.tstest/utils/uptime-state.test.tstsconfig.json
| function onChartClick(params: unknown) { | ||
| const index = slotIndexOf(params); | ||
| const slot = index === null ? undefined : props.slots[index]; | ||
| if (index === null || !slot) return; | ||
|
|
||
| emit("cellClick", { slot, bucket: bucketAt(index) }); | ||
| } | ||
| </script> | ||
|
|
||
| <template> | ||
| <VChart | ||
| class="uptime-track" | ||
| role="img" | ||
| :aria-label="ariaLabel" | ||
| :option="option" | ||
| autoresize | ||
| @click="onChartClick" | ||
| /> | ||
| </template> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
The bucket drilldown is reachable only with a pointer.
The track emits cellClick from the chart @click handler only. The root element uses role="img", which hides the chart contents from assistive technology, and nothing in the component is focusable or handles keyboard events. A keyboard user can read the ariaLabel summary, but cannot open the drilldown dialog for any slice.
Add a keyboard path to the same emit. One option: keep the visual chart as role="img", and render a visually hidden but focusable list of buttons, one per slot, that emit cellClick with the same payload.
🤖 Prompt for 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.
In `@app/components/uptime/UptimeTrack.vue` around lines 146 - 164, Add a
keyboard-accessible control path in UptimeTrack’s template by rendering a
visually hidden, focusable button for each slot, with keyboard activation
emitting cellClick using that slot and its corresponding bucketAt index. Keep
the existing VChart pointer handler and role="img" behavior unchanged, and
ensure each button has an accessible label identifying its slot.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/components/uptime/BucketDrilldownDialog.vue (1)
37-39: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winInvalidate pending requests before clearing the dialog state.
When
visiblebecomes false during a pending request, this branch clearschecksbut does not changelatestRequest. The pending request can then pass the check on Line 53 and restore old checks on Line 55. Increment the request generation and clearloadingbefore returning.Proposed fix
if (!props.visible || !service || !range) { + latestRequest += 1; checks.value = []; + loading.value = false; return; }🤖 Prompt for 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. In `@app/components/uptime/BucketDrilldownDialog.vue` around lines 37 - 39, Update the early-return guard in the dialog’s request/state reset logic to increment the request generation represented by latestRequest and set loading to false before clearing checks and returning. Preserve the existing reset behavior while ensuring pending requests cannot pass the response-generation check and restore stale checks.
🤖 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.
Outside diff comments:
In `@app/components/uptime/BucketDrilldownDialog.vue`:
- Around line 37-39: Update the early-return guard in the dialog’s request/state
reset logic to increment the request generation represented by latestRequest and
set loading to false before clearing checks and returning. Preserve the existing
reset behavior while ensuring pending requests cannot pass the
response-generation check and restore stale checks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: c2d070d6-fb68-4889-9fa7-d590ef360f73
📒 Files selected for processing (23)
.github/actions/build-docker-image/action.yaml.github/actions/setup-and-cache/action.yaml.github/workflows/ci.yaml.github/workflows/docker-preview.yaml.github/workflows/release.yamlapp/components/TableRowMetadata.vueapp/components/analysis/ObjectDownloadButtons.vueapp/components/data-stores/DataStoreList.vueapp/components/data-stores/DetailedDataStoreTable.vueapp/components/table/ExpandRowButtons.vueapp/components/uptime/BucketDrilldownDialog.vueapp/composables/useAPIFetch.tsapp/composables/useDataStoreList.tsapp/pages/uptime.vueapp/utils/prettify-key.tsnuxt.config.tstest/components/data-stores/DataStoreList.spec.tstest/components/data-stores/DetailedDataStoreTable.spec.tstest/components/uptime/BucketDrilldownDialog.spec.tstest/composables/useAPIFetch.test.tstest/composables/useDataStoreList.test.tstest/pages/uptime.spec.tstest/utils/prettify-key.test.ts
💤 Files with no reviewable changes (6)
- .github/workflows/docker-preview.yaml
- test/utils/prettify-key.test.ts
- app/utils/prettify-key.ts
- app/components/TableRowMetadata.vue
- app/components/table/ExpandRowButtons.vue
- app/components/analysis/ObjectDownloadButtons.vue
🚧 Files skipped from review as they are similar to previous changes (3)
- nuxt.config.ts
- test/components/uptime/BucketDrilldownDialog.spec.ts
- app/pages/uptime.vue
This PR was also used to clean up the repo including removing old/dead code and fetch methods. This includes refactoring some of the kept fetch calls to no longer be composables when called outside of the setup.
Summary by CodeRabbit
New Features
Bug Fixes
Tests