Mega-PR: combine open PRs #424-#443 for integration review - #444
Mega-PR: combine open PRs #424-#443 for integration review#444karpikpl wants to merge 32 commits into
Conversation
…top chat model (#420) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…tiles (#421) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…ity band (#422) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…penders (#419) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…ses (#416) Add bounded pagination for /api/user-metrics with a default and maximum page size of 500 rows. Historical DB reads now page distinct users before aggregating per-day records, while preserving array responses for existing callers and exposing pagination metadata headers. Validation: npm test; npm run build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…nly usage (#433) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…s mapping (#432) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…ring redaction (#435) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…-access events (#436) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bbf27d66-9aca-451b-85ee-3a32e0775d52
…dates Bumps the npm_and_yarn group with 2 updates in the / directory: [svgo](https://github.com/svg/svgo) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `svgo` from 4.0.1 to 4.0.2 - [Release notes](https://github.com/svg/svgo/releases) - [Commits](svg/svgo@v4.0.1...v4.0.2) Updates `vite` from 7.3.3 to 7.3.6 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v7.3.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.3.6/packages/vite) --- updated-dependencies: - dependency-name: svgo dependency-version: 4.0.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: vite dependency-version: 7.3.6 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com>
# Conflicts: # app/components/BillingCreditsViewer.vue
…load # Conflicts: # app/components/BillingCreditsViewer.vue
# Conflicts: # app/components/BillingCreditsViewer.vue
# Conflicts: # app/components/BillingCreditsViewer.vue # server/api/billing-credits.get.ts # server/services/billing-credit-reader.ts
# Conflicts: # server/routes/auth/auth0.get.ts # server/routes/auth/github.get.ts # server/routes/auth/google.get.ts # server/routes/auth/keycloak.get.ts # server/routes/auth/microsoft.get.ts # server/utils/proxy-agent.ts
…ctory with 2 updates
There was a problem hiding this comment.
Pull request overview
This mega-PR integrates a set of fixes and features across billing, user-metrics pagination, accessibility improvements, and new observability (structured logging + opt-in audit log) in the Nuxt 3 Copilot Metrics Viewer.
Changes:
- Adds DB-backed billing capabilities (global top spenders endpoint, per-user sorting/paging hooks, EMU billing username aliasing + unmatched username surfacing).
- Introduces structured, tagged server logging with request IDs and expands coverage with new Vitest suites.
- Improves UI accessibility (aria-labels, non-color activity cues) and refines UX copy/tooltips across multiple viewers.
Reviewed changes
Copilot reviewed 60 out of 60 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/user-metrics.spec.ts | Updates assertions and adds pagination-focused coverage for /api/user-metrics. |
| tests/top-chat-model.nuxt.spec.ts | Nuxt-environment regression tests for skipping synthetic chat model buckets in KPIs. |
| tests/server-logger.spec.ts | Unit tests for new logger behavior and request logging middleware. |
| tests/MyUsageViewer.source.spec.ts | Source-level regression for Accepted lines tooltip presence/copy. |
| tests/billing-top-spenders.spec.ts | Tests for gross fallback ranking logic in top spenders chart data builder. |
| tests/billing-per-user-search.spec.ts | Source-level tests for per-user search wiring + lazy-load behavior. |
| tests/billing-per-user-lazy-load.spec.ts | Tests for building sort-aware per-user lazy-load requests. |
| tests/billing-credits.db-first.spec.ts | Expands DB-first billing tests (by-user sorting/paging + top-users endpoint behavior). |
| tests/billing-credits-top-token-empty-state.spec.ts | Source-level regression for corrected Top token users empty state and test id. |
| tests/billing-credits-calculation.spec.ts | Tests shared credits definition between tiles and per-user rows. |
| tests/billing-credit-reader.spec.ts | Adds tests for EMU aliasing, unmatched billing usernames, paging/sorting, and top-user ranking SQL. |
| tests/audit.spec.ts | Unit tests for opt-in audit logging, field inclusion, and redaction behavior. |
| tests/audit-call-sites.spec.ts | Verifies audit events are emitted from key auth/authz/admin call sites. |
| tests/a11y-components.spec.ts | Enforces aria-labels for icon-only buttons and verifies activity-band cue presence. |
| shared/utils/billing-user-identity.ts | Adds parsing/normalization utilities for EMU billing username ↔ metrics login aliasing. |
| server/utils/usage-admin.ts | Emits audit events for usage-admin allow/deny decisions. |
| server/utils/proxy-agent.ts | Switches proxy initialization logging to structured logger and redacts proxy credentials. |
| server/utils/logger.ts | Introduces tagged logger abstraction with pretty/JSON output support. |
| server/utils/authorization.ts | Emits audit events for OAuth allowlist denials. |
| server/utils/audit.ts | Adds opt-in JSONL audit event emitter with sensitive-detail redaction. |
| server/storage/user-metrics-storage.ts | Adds paging/filter support when reading stored user metrics, including total count. |
| server/services/sync-service.ts | Migrates sync gap logging to structured logger. |
| server/services/github-saml-service.ts | Migrates SAML fetch warnings to structured logger. |
| server/services/github-copilot-usage-api.ts | Migrates error/info logging to structured logger with structured fields. |
| server/services/billing-credit-reader.ts | Adds top-users aggregate + EMU aliasing/unmatched reporting + new sort/paging hooks. |
| server/routes/auth/microsoft.get.ts | Adds structured logger + audit login success for Microsoft OAuth handler. |
| server/routes/auth/keycloak.get.ts | Adds structured logger + audit login success for Keycloak OAuth handler. |
| server/routes/auth/google.get.ts | Adds structured logger + audit login success for Google OAuth handler. |
| server/routes/auth/github.get.ts | Adds structured logger + audit login success for GitHub OAuth handler. |
| server/routes/auth/auth0.get.ts | Adds structured logger + audit login success for Auth0 OAuth handler. |
| server/plugins/db-init.ts | Migrates DB init logs to structured logger. |
| server/plugins/auth-config-check.ts | Migrates auth config warning to structured logger. |
| server/modules/github-app-auth.ts | Migrates JWT build log to structured logger. |
| server/middleware/log.ts | Adds request logging middleware with request IDs and skip/redaction behavior. |
| server/api/user-metrics.ts | Adds pagination (cap at 500) + pagination headers + self-filtering support for storage reads. |
| server/api/my-usage.get.ts | Emits audit event when an admin views another user’s usage. |
| server/api/billing-credits.get.ts | Adds audit event for billing view + extends response shape for users/unmatched usernames. |
| server/api/billing-credits-top-users.get.ts | Adds DB-only endpoint for global top billing users ranking. |
| server/api/billing-credits-by-user.get.ts | Adds audit event, EMU alias fan-out, and client sort/page support for live responses; forwards DB sort/page options. |
| server/api/ai/chat.post.ts | Migrates Models API error logging to structured logger. |
| server/api/admin/sync.post.ts | Emits audit events for admin sync/billing CSV actions; reduces repeated identifier recomputation. |
| server/api/admin/overview.get.ts | Migrates enrichment warning logging to structured logger. |
| package-lock.json | Dependency lock updates (svgo/vite bumps and root dependency spec metadata changes). |
| nuxt.config.ts | Adds runtime-config wiring for log controls and audit log enable flag. |
| app/utils/billingTopSpenders.ts | Adds chart data builder with net→gross fallback behavior. |
| app/utils/billingPerUserLazyLoad.ts | Adds helper to build sort-aware per-user billing lazy-load requests. |
| app/utils/billingCredits.ts | Centralizes credits-used calculation (net + discount). |
| app/components/UserMetricsViewer.vue | Adds aria-labels and non-color activity labels for user activity UI elements. |
| app/components/TeamsComponent.vue | Adds aria-labels for icon-only buttons and layout toggles. |
| app/components/SeatsAnalysisViewer.vue | Adds aria-labels for layout toggle buttons. |
| app/components/PullRequestViewer.vue | Adds aria-labels for layout toggle buttons. |
| app/components/MyUsageViewer.vue | Adds Accepted lines tooltip + filters synthetic chat model buckets for top model. |
| app/components/MetricsViewer.vue | Skips synthetic chat model buckets and adds aria-labels for layout toggles. |
| app/components/MainComponent.vue | Adds aria-labels for toolbar icon buttons; forces user-metrics fetch to page=1/pageSize=500. |
| app/components/CopilotChatViewer.vue | Adds aria-labels for layout toggle buttons. |
| app/components/BreakdownComponent.vue | Adds aria-labels for layout toggle buttons. |
| app/components/BillingCreditsViewer.vue | Unifies credits definition + tooltips; adds per-user search; server-side top spenders; shows unmatched billing usernames. |
| app/components/AiChatPanel.vue | Adds aria-labels for chat panel actions and send button. |
| app/components/AgentModeViewer.vue | Adds aria-labels for layout toggle buttons. |
| app/components/AgentActivityViewer.vue | Adds aria-labels for layout toggle buttons. |
| app/components/AdminPanel.vue | Adds aria-labels for icon-only admin actions. |
| export function createLogger(tag: string): TaggedLogger { | ||
| const log = (type: 'error' | 'warn' | 'info' | 'debug', message: string, ...args: unknown[]) => { | ||
| const config = getRuntimeLoggingConfig() | ||
| const level = normalizeLogLevel(config.logLevel) | ||
| if (!isLogLevelAtLeast(level, type)) return | ||
|
|
||
| const logger = createConsola({ | ||
| level: LEVELS.debug, | ||
| reporters: [createReporter(shouldUseJsonFormat(config.logFormat))], | ||
| }).withTag(tag) | ||
|
|
||
| logger[type](message, ...args) | ||
| } | ||
|
|
||
| return { | ||
| error: (message, ...args) => log('error', message, ...args), | ||
| warn: (message, ...args) => log('warn', message, ...args), | ||
| info: (message, ...args) => log('info', message, ...args), | ||
| debug: (message, ...args) => log('debug', message, ...args), | ||
| } | ||
| } |
| await emitAuditEvent('billing.per_user.viewed', { | ||
| action: 'view', | ||
| outcome: 'allow', | ||
| target: options.githubOrg || options.githubEnt || 'unknown', | ||
| detail: { | ||
| scope: options.scope, | ||
| requestedLogins, | ||
| requestedCount: requestedLogins.length, | ||
| year: query.year, | ||
| month: query.month, | ||
| day: query.day, | ||
| model: query.model, | ||
| product: query.product, | ||
| costCenterId: query.cost_center_id, | ||
| }, | ||
| }, event); |
| FROM billing_credit_usage | ||
| WHERE ${conds.join(' AND ')} | ||
| GROUP BY username, product, sku, model, unit_type | ||
| ORDER BY username, product, sku, model | ||
| ORDER BY ${order}, username, product, sku, model | ||
| ${limitClause} |
🚀 Preview Deployment ReadyPreview URL: https://cmv-app.purpletree-900a4a21.westus.azurecontainerapps.io Deployed: PR #444 | 3ed80d7 | 2026-07-29T12:43:37Z
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 60 out of 60 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
server/services/billing-credit-reader.ts:360
- LIMIT/OFFSET is applied to the per-(username, product, sku, model, unit_type) grouped rows, not to distinct users. This means a "page" can contain partial data for a user (under-counting their totals), and sort keys like
modelsare also incorrect here becauseCOUNT(DISTINCT model)will be 1 for every grouped row when grouping includesmodel. To support server-side sorted paging by user, the query needs to page a distinct user list (ordered by aggregated metric) and then fetch all grouped rows for just those users.
server/utils/logger.ts:106 createLoggercurrently constructs a new Consola instance (and reporter) on every log call. On hot paths (e.g., request middleware) this adds avoidable allocation/GC overhead. Consider caching the Consola instance per tag and only recreating it when the log format mode changes.
| const billingHeaders = new Headers({ | ||
| Authorization: `Bearer ${billingToken}`, | ||
| Accept: 'application/vnd.github+json', | ||
| 'X-GitHub-Api-Version': '2026-03-10', | ||
| }); |
|
Hi Piotr, It's awesome to see the project growing so quickly! We plan to roll out the Metrics Viewer to our developers next weeks. |
Mega-PR that combines all currently-open PRs #424 and above into one branch, resolving conflicts along the way. Verified with
npm test(827/827 passing) andnpm run build(success).Included PRs
topSpendersData.value.usersthroughbuildTopSpendersChartData()so gross-fallback ranking still runs against the full datasetonTableOptionsnow feedsfilteredPerUserRowsForLazyLoadintobuildPerUserBillingLazyLoadRequest, so search + sort + lazy-load all interact correctlyusersandunmatchedBillingUsernames; frontend accumulates bothcreateLoggeralongsideemitAuditEventimports in all five auth providers;proxy-agent.tsnow logs the redacted proxy URL vialogger.infoReview notes
perUserRows; after fix(billing): rank Top spenders server-side across all users #428 shifted top-spenders to the server, that data source is gone. Resolution feedstopSpendersData.value.users(which already includes gross/net) into Fix Top spenders for fully-discounted billing plans #429'sbuildTopSpendersChartData, preserving both "server-side over full dataset" and "fall back to gross when net is uniformly zero".BillingCreditsResponsenow exposes bothusersandunmatchedBillingUsernames; both are consumed in the by-login fetch loop.Validation
npm install✅npm test→ 827 passed, 0 failed ✅npm run build→ ✅Purpose
Intended as a single integration branch for reviewers to see the full combined behavior. Individual PRs remain the canonical review units — this is a draft and should not be merged directly.